LangChain integration for Agent Tool Protocol
npm install @mondaydotcomorg/atp-langchainLangChain and LangGraph integration for Agent Tool Protocol with production-ready human-in-the-loop support.
This package integrates ATP with LangChain and LangGraph, enabling agents to generate and execute ATP code as tools. Includes production-ready async approval workflows using LangGraph interrupts and checkpoints.
``bash`
npm install @mondaydotcomorg/atp-langchain @langchain/core @langchain/langgraph
`mermaid
graph TB
LangChain[LangChain Agent] --> Tools[ATP Tools]
Tools --> Client[LangGraphATPClient]
Client --> Server[ATP Server]
Server --> Runtime[Runtime APIs]
Runtime --> LLM[atp.llm.*]
Runtime --> Embed[atp.embedding.*]
Runtime --> Approval[atp.approval.*]
LLM --> Callback[Client LLM Callback]
Embed --> EmbedCallback[Client Embedding Callback]
Approval --> Interrupt[LangGraph Interrupt]
Interrupt --> Checkpoint[Save State]
Checkpoint --> Resume[Resume Later]
`
- 🤖 LangChain Tools - Use ATP as tools in any LangChain agent
- 🔄 LangGraph Interrupts - Production-ready async approvals
- 🧠 LLM Sampling - atp.llm.call() routes to your LangChain LLMatp.embedding.*
- 🔍 Embedding Support - routes to your embeddings model
- ✅ Approval Workflows - Human-in-the-loop via LangGraph checkpoints
- 💾 State Persistence - PostgreSQL/Redis checkpointing
- 📝 TypeScript - Full type safety
`typescript
import { ChatOpenAI } from '@langchain/openai';
import { createReactAgent } from '@langchain/langgraph/prebuilt';
import { createATPTools } from '@mondaydotcomorg/atp-langchain';
const llm = new ChatOpenAI({ modelName: 'gpt-4' });
// Create ATP tools
const { tools } = await createATPTools('http://localhost:3333', 'your-api-key', { llm });
// Create agent
const agent = createReactAgent({ llm, tools });
// Run agent
const result = await agent.invoke({
messages: [{ role: 'user', content: 'Execute ATP code to get a joke from the LLM' }],
});
`
`typescript
import { MemorySaver } from '@langchain/langgraph';
import { createATPTools, ApprovalRequiredException } from '@mondaydotcomorg/atp-langchain';
const llm = new ChatOpenAI({ modelName: 'gpt-4' });
// Create tools with interrupt-based approvals
const { client, tools, isApprovalRequired, resumeWithApproval } =
await createATPTools('http://localhost:3333', 'api-key', { llm });
// Create agent with checkpointer
const checkpointer = new MemorySaver();
const agent = createReactAgent({ llm, tools, checkpointSaver: checkpointer });
try {
await agent.invoke({ messages: [...] }, { configurable: { thread_id: 'thread-1' } });
} catch (error) {
if (isApprovalRequired(error)) {
const { executionId, message, context } = error.approvalRequest;
// Save to DB, notify user via Slack/email
console.log(Approval needed: ${message});
await notifyUser(message);
// Wait for approval (async - can take hours/days)
const approved = await waitForUserApproval(executionId);
// Resume execution
const result = await resumeWithApproval(executionId, approved);
}
}
`
`typescript
import { ChatOpenAI, OpenAIEmbeddings } from '@langchain/openai';
import { createATPTools } from '@mondaydotcomorg/atp-langchain';
const llm = new ChatOpenAI({ modelName: 'gpt-4' });
const embeddings = new OpenAIEmbeddings({ model: 'text-embedding-3-small' });
// Create tools with LLM and embedding support
const { tools } = await createATPTools('http://localhost:3333', 'api-key', {
llm,
embeddings, // Enable atp.embedding.*
});
const agent = createReactAgent({ llm, tools });
// Agent can now generate code that uses embeddings:
// const id = await atp.embedding.embed("Store this text");
// const results = await atp.embedding.search("Find similar", { topK: 5 });
`
When agents use ATP tools, they can generate TypeScript code using ATP's runtime APIs:
`typescript
// Agent generates this code:
const idea = await atp.llm.call({
prompt: 'Generate a product idea',
});
// Store embedding for semantic search
const embeddingId = await atp.embedding.embed(idea);
// Request approval
const approval = await atp.approval.request(Launch product: ${idea}?, { idea });
if (approval.approved) {
return await atp.llm.call({
prompt: Create marketing copy for: ${idea},`
});
}
atp.llm.call() routes to your LangChain LLM:
- Uses the same LLM as your agent
- Fresh context for sub-reasoning
- Supports call(), extract(), classify()
`typescript`
// In ATP code:
const analysis = await atp.llm.call({
prompt: 'Analyze this data: ' + JSON.stringify(data),
temperature: 0.7,
model: 'gpt-4',
});
When ATP code calls atp.approval.request(), LangGraph interrupts:
1. Pause - Execution pauses, state saved to checkpoint
2. Notify - Your code sends approval request (Slack, email, UI)
3. Wait - User reviews asynchronously (hours/days OK)
4. Resume - Call resumeWithApproval() with decision
5. Continue - Execution resumes from checkpoint
`mermaid
sequenceDiagram
participant Agent
participant ATP
participant Checkpoint
participant User
Agent->>ATP: Execute code
ATP->>ATP: atp.approval.request()
ATP->>Checkpoint: Save state
ATP-->>Agent: Throw ApprovalRequiredException
Agent->>User: Notify (Slack/Email)
Note over User: User reviews
(async, can take hours)
User->>Agent: Approve/Deny
Agent->>ATP: resumeWithApproval(executionId, approved)
ATP->>Checkpoint: Restore state
ATP->>ATP: Continue execution
ATP-->>Agent: Final result
`
`typescript`
async function createATPTools(
serverUrl: string,
apiKey: string,
options: CreateATPToolsOptions
): Promise
Options:
`typescript`
interface CreateATPToolsOptions {
llm: BaseChatModel; // Required: LangChain LLM
embeddings?: Embeddings; // Optional: Embeddings model
useLangGraphInterrupts?: boolean; // Default: true
approvalHandler?: ApprovalHandler; // If interrupts disabled
defaultExecutionConfig?: ExecutionConfig;
}
Returns:
`typescript`
interface ATPToolsResult {
client: LangGraphATPClient;
tools: Tool[]; // LangChain tools
isApprovalRequired: (error: any) => boolean;
resumeWithApproval: (executionId: string, approved: boolean, reason?: string) => Promise
}
`typescript`
class LangGraphATPClient extends AgentToolProtocolClient {
execute(code: string, config?: ExecutionConfig): Promise
resumeWithApproval(executionId: string, approved: boolean, reason?: string): Promise
getPendingApproval(executionId: string): Promise
}
`typescript`
class ApprovalRequiredException extends Error {
approvalRequest: {
executionId: string;
message: string;
context?: any;
timestamp: number;
};
}
`typescript
import { PostgresSaver } from '@langchain/langgraph-checkpoint-postgres';
const checkpointer = new PostgresSaver({
connectionString: process.env.DATABASE_URL,
});
const agent = createReactAgent({
llm,
tools,
checkpointSaver: checkpointer,
});
// State persists across restarts
`
`typescript
if (isApprovalRequired(error)) {
const { executionId, message, context } = error.approvalRequest;
// Save to database
await db.approvals.create({
id: executionId,
message,
context,
status: 'pending',
});
// Send Slack notification
await slack.chat.postMessage({
channel: '#approvals',
text: message,
blocks: [
{
type: 'actions',
elements: [
{ type: 'button', text: 'Approve', action_id: 'approve' },
{ type: 'button', text: 'Deny', action_id: 'deny' },
],
},
],
});
}
// In Slack webhook handler:
app.post('/slack/actions', async (req, res) => {
const { action_id } = req.body;
const executionId = req.body.state.executionId;
const approved = action_id === 'approve';
const result = await resumeWithApproval(executionId, approved);
await db.approvals.update(executionId, { status: approved ? 'approved' : 'denied' });
res.json({ ok: true });
});
`
`typescript
// Agent generates ATP code with multiple approvals:
const step1 = await atp.approval.request('Approve step 1?');
if (!step1.approved) return { cancelled: true };
const step2 = await atp.approval.request('Approve step 2?');
if (!step2.approved) return { cancelled: true };
return { success: true };
`
Each atp.approval.request() triggers a new interrupt → checkpoint → resume cycle.
`typescript`
const { tools } = await createATPTools(url, key, {
llm,
useLangGraphInterrupts: false,
approvalHandler: async (message) => {
return await promptUser(message); // Blocks
},
});
Pros: Simpler setup, good for CLI tools
Cons: Blocks execution, no persistence, not production-ready
`typescript
const { tools, isApprovalRequired, resumeWithApproval } = await createATPTools(url, key, { llm });
try {
await agent.invoke({ messages });
} catch (error) {
if (isApprovalRequired(error)) {
await handleApprovalAsync(error.approvalRequest);
}
}
`
Pros: Non-blocking, state persists, production-ready, multi-user support
Cons: Slightly more complex setup
See examples/langchain-react-agent/:
- simple-test.ts - Integration test with all 3 ATP tools
- simple-agent.ts - Basic React agent without approvals
- agent.ts - Production agent with interrupts and checkpoints
`typescript
import { ChatOpenAI, OpenAIEmbeddings } from '@langchain/openai';
import { createATPTools } from '@mondaydotcomorg/atp-langchain';
const llm = new ChatOpenAI({ modelName: 'gpt-4' });
const embeddings = new OpenAIEmbeddings();
const { tools } = await createATPTools(url, key, { llm, embeddings });
const agent = createReactAgent({ llm, tools });
// Agent can do RAG:
const result = await agent.invoke({
messages: [
{
role: 'user',
content: Use ATP to:
1. Embed these documents: ["AI is...", "ML is...", "DL is..."]
2. Search for content similar to "neural networks"
3. Use atp.llm.call() to answer based on results,`
},
],
});
`typescript`
import type {
LangGraphATPClient,
LangGraphATPClientOptions,
ApprovalRequest,
ApprovalResponse,
CreateATPToolsOptions,
ATPToolsResult,
} from '@mondaydotcomorg/atp-langchain';
- Node.js 18+
- TypeScript 5.0+
- @langchain/core ^0.3.0@langchain/langgraph` ^0.2.0
-
MIT
- ATP Documentation
- LangGraph Human-in-the-Loop
- LangChain Tools
- Examples