LangChain Integration
Agent Tool Protocol provides first-class integration with LangChain and LangGraph, making it easy to build powerful AI agents with access to tools and APIs.
Installation
npm install @mondaydotcomorg/atp-langchain @mondaydotcomorg/atp-client langchain @langchain/core
LangChain Tools
Convert ATP APIs into LangChain tools automatically.
Basic Usage
import { createServer } from '@mondaydotcomorg/atp-server';
import { AgentToolProtocolClient } from '@mondaydotcomorg/atp-client';
import { ATPLangChainTools } from '@mondaydotcomorg/atp-langchain';
import { ChatAnthropic } from '@langchain/anthropic';
import { AgentExecutor, createToolCallingAgent } from 'langchain/agents';
import { ChatPromptTemplate } from '@langchain/core/prompts';
// 1. Create and configure ATP server
const server = createServer();
server.registerAPI('calculator', {
add: {
description: 'Add two numbers',
inputSchema: {
type: 'object',
properties: {
a: { type: 'number' },
b: { type: 'number' },
},
required: ['a', 'b'],
},
handler: async ({ a, b }) => a + b,
},
multiply: {
description: 'Multiply two numbers',
inputSchema: {
type: 'object',
properties: {
a: { type: 'number' },
b: { type: 'number' },
},
required: ['a', 'b'],
},
handler: async ({ a, b }) => a * b,
},
});
await server.listen(3333);
// 2. Create ATP client
const client = new AgentToolProtocolClient({
baseUrl: 'http://localhost:3333',
});
await client.init();
await client.connect();
// 3. Convert ATP APIs to LangChain tools
const tools = await ATPLangChainTools.fromClient(client);
// 4. Create LangChain agent
const llm = new ChatAnthropic({
modelName: 'claude-3-5-sonnet-20241022',
apiKey: process.env.ANTHROPIC_API_KEY,
});
const prompt = ChatPromptTemplate.fromMessages([
['system', 'You are a helpful assistant with access to calculator functions.'],
['human', '{input}'],
['placeholder', '{agent_scratchpad}'],
]);
const agent = createToolCallingAgent({
llm,
tools,
prompt,
});
const agentExecutor = new AgentExecutor({
agent,
tools,
});
// 5. Run the agent
const result = await agentExecutor.invoke({
input: 'What is (15 + 7) * 3?',
});
console.log(result.output);
// "The result is 66"
Tool Configuration
Filtering Tools
Only include specific APIs:
const tools = await ATPLangChainTools.fromClient(client, {
includeAPIs: ['calculator', 'users'], // Only these APIs
excludeAPIs: ['admin'], // Exclude these
});
Custom Tool Names
Customize how tools are named:
const tools = await ATPLangChainTools.fromClient(client, {
toolNameFormat: (apiGroup, functionName) => {
return `${apiGroup}_${functionName}`.toUpperCase();
},
});
// Tools will be named: CALCULATOR_ADD, CALCULATOR_MULTIPLY, etc.
Tool Metadata
Add metadata to tools:
const tools = await ATPLangChainTools.fromClient(client, {
toolMetadata: {
'calculator.add': {
tags: ['math', 'arithmetic'],
category: 'calculator',
},
},
});
LangGraph Integration
Build sophisticated multi-step agents with LangGraph.
Basic LangGraph Agent
import { StateGraph, END } from '@langchain/langgraph';
import { ATPLangChainTools } from '@mondaydotcomorg/atp-langchain';
// Define agent state
interface AgentState {
input: string;
chatHistory: Array<any>;
agentOutcome?: any;
intermediateSteps: Array<any>;
}
// Create tools
const tools = await ATPLangChainTools.fromClient(client);
// Define agent nodes
const callModel = async (state: AgentState) => {
const response = await llm.invoke(state.input);
return { agentOutcome: response };
};
const executeTools = async (state: AgentState) => {
const { agentOutcome } = state;
// Execute tool calls
const toolCalls = agentOutcome.tool_calls || [];
const toolOutputs = await Promise.all(
toolCalls.map(async (call: any) => {
const tool = tools.find(t => t.name === call.name);
if (!tool) throw new Error(`Tool ${call.name} not found`);
const output = await tool.invoke(call.args);
return { tool: call.name, output };
})
);
return {
intermediateSteps: [...state.intermediateSteps, ...toolOutputs],
};
};
// Define routing
const shouldContinue = (state: AgentState) => {
if (state.agentOutcome?.tool_calls?.length > 0) {
return 'tools';
}
return END;
};
// Build graph
const workflow = new StateGraph<AgentState>({
channels: {
input: null,
chatHistory: null,
agentOutcome: null,
intermediateSteps: null,
},
});
workflow.addNode('agent', callModel);
workflow.addNode('tools', executeTools);
workflow.setEntryPoint('agent');
workflow.addConditionalEdges('agent', shouldContinue, {
tools: 'tools',
[END]: END,
});
workflow.addEdge('tools', 'agent');
const app = workflow.compile();
// Run the agent
const result = await app.invoke({
input: 'Calculate (10 + 5) * 3 and explain your work',
chatHistory: [],
intermediateSteps: [],
});
Multi-Agent System
import { StateGraph } from '@langchain/langgraph';
// Create specialized agents for different tasks
const mathAgent = createToolCallingAgent({
llm,
tools: await ATPLangChainTools.fromClient(client, {
includeAPIs: ['calculator'],
}),
prompt: mathPrompt,
});
const dataAgent = createToolCallingAgent({
llm,
tools: await ATPLangChainTools.fromClient(client, {
includeAPIs: ['users', 'products', 'orders'],
}),
prompt: dataPrompt,
});
// Build multi-agent graph
interface MultiAgentState {
input: string;
currentAgent: 'router' | 'math' | 'data';
result?: string;
}
const routerNode = async (state: MultiAgentState) => {
// Determine which agent to use
const classification = await llm.invoke(
`Classify this request as 'math' or 'data': ${state.input}`
);
return {
currentAgent: classification.includes('math') ? 'math' : 'data',
};
};
const mathNode = async (state: MultiAgentState) => {
const result = await mathAgent.invoke({ input: state.input });
return { result: result.output };
};
const dataNode = async (state: MultiAgentState) => {
const result = await dataAgent.invoke({ input: state.input });
return { result: result.output };
};
const workflow = new StateGraph<MultiAgentState>({
channels: {
input: null,
currentAgent: null,
result: null,
},
});
workflow.addNode('router', routerNode);
workflow.addNode('math', mathNode);
workflow.addNode('data', dataNode);
workflow.setEntryPoint('router');
workflow.addConditionalEdges('router', (state) => state.currentAgent);
workflow.addEdge('math', END);
workflow.addEdge('data', END);
const multiAgentApp = workflow.compile();
// Use the multi-agent system
const result = await multiAgentApp.invoke({
input: 'What is 15 * 3?',
currentAgent: 'router',
});
LangChain Node (Server-Side Tools)
Execute LangChain agents on the server for better performance.
Server-Side Agent
import { LangChainNode } from '@mondaydotcomorg/atp-langchain';
import { createServer } from '@mondaydotcomorg/atp-server';
// Create server with LangChain node
const server = createServer();
// Register LangChain agent as an API
server.registerAPI('agent', {
analyze: {
description: 'Analyze data using AI agent',
inputSchema: {
type: 'object',
properties: {
query: { type: 'string' },
data: { type: 'object' },
},
required: ['query'],
},
handler: async ({ query, data }) => {
// Create LangChain agent
const agent = createToolCallingAgent({
llm,
tools: dataAnalysisTools,
prompt,
});
// Execute agent
const executor = new AgentExecutor({ agent, tools: dataAnalysisTools });
const result = await executor.invoke({
input: query,
data,
});
return result.output;
},
},
});
server.listen(3333);
Client Usage
const client = new AgentToolProtocolClient({
baseUrl: 'http://localhost:3333',
});
await client.init();
await client.connect();
// Call the server-side agent
const result = await client.execute(`
const analysis = await agent.analyze({
query: "What are the top 3 products by revenue?",
data: salesData,
});
return analysis;
`);
Advanced Patterns
Agent with Memory
import { BufferMemory } from 'langchain/memory';
import { ConversationChain } from 'langchain/chains';
const memory = new BufferMemory();
const tools = await ATPLangChainTools.fromClient(client);
const agent = createToolCallingAgent({
llm,
tools,
prompt,
});
const agentExecutor = new AgentExecutor({
agent,
tools,
memory,
});
// Multi-turn conversation
await agentExecutor.invoke({ input: 'Calculate 10 + 5' });
await agentExecutor.invoke({ input: 'Multiply that by 3' });
await agentExecutor.invoke({ input: 'What was the first calculation?' });
Streaming Responses
import { AgentExecutor } from 'langchain/agents';
const agentExecutor = new AgentExecutor({
agent,
tools,
});
// Stream agent responses
const stream = await agentExecutor.stream({
input: 'Calculate a complex equation step by step',
});
for await (const chunk of stream) {
console.log(chunk);
}
Error Handling and Retry
import { RetryAgent } from '@mondaydotcomorg/atp-langchain';
const tools = await ATPLangChainTools.fromClient(client);
const agent = new RetryAgent({
llm,
tools,
maxRetries: 3,
retryDelay: 1000,
onRetry: (attempt, error) => {
console.log(`Retry attempt ${attempt}: ${error.message}`);
},
});
const result = await agent.invoke({
input: 'Perform a complex calculation',
});
Tool Selection
Control which tools the agent can use:
import { ToolSelector } from '@mondaydotcomorg/atp-langchain';
const tools = await ATPLangChainTools.fromClient(client);
// Create tool selector
const selector = new ToolSelector({
tools,
strategy: 'similarity', // Use semantic similarity
maxTools: 5, // Max tools per invocation
});
// Agent automatically selects relevant tools
const agent = createToolCallingAgent({
llm,
tools: [], // Empty initially
prompt,
});
// Wrap with tool selection
const agentWithSelector = async (input: string) => {
// Select relevant tools based on input
const selectedTools = await selector.select(input);
// Create agent with selected tools
const contextAgent = createToolCallingAgent({
llm,
tools: selectedTools,
prompt,
});
return await new AgentExecutor({
agent: contextAgent,
tools: selectedTools,
}).invoke({ input });
};
Client Tools in LangChain
Use ATP client tools with LangChain:
import { AgentToolProtocolClient } from '@mondaydotcomorg/atp-client';
import { ATPLangChainTools } from '@mondaydotcomorg/atp-langchain';
const client = new AgentToolProtocolClient({
baseUrl: 'http://localhost:3333',
});
// Provide client tools
client.provideTools([
{
name: 'readFile',
namespace: 'filesystem',
description: 'Read a file from local filesystem',
inputSchema: {
type: 'object',
properties: {
path: { type: 'string' },
},
required: ['path'],
},
handler: async ({ path }) => {
const fs = await import('fs/promises');
return await fs.readFile(path, 'utf-8');
},
},
]);
await client.init();
await client.connect();
// Convert all tools (server + client) to LangChain
const tools = await ATPLangChainTools.fromClient(client);
// Agent can now use both server and client tools!
const agent = createToolCallingAgent({ llm, tools, prompt });
Integration with RAG
Combine ATP with Retrieval Augmented Generation:
import { VectorStoreRetriever } from '@langchain/core/vectorstores';
import { OpenAIEmbeddings } from '@langchain/openai';
import { MemoryVectorStore } from 'langchain/vectorstores/memory';
// Create vector store
const vectorStore = new MemoryVectorStore(new OpenAIEmbeddings());
// Add documents
await vectorStore.addDocuments([
{ pageContent: 'Document 1 content', metadata: {} },
{ pageContent: 'Document 2 content', metadata: {} },
]);
// Create retriever tool
const retrieverTool = {
name: 'search_documents',
description: 'Search relevant documents',
func: async (query: string) => {
const docs = await vectorStore.similaritySearch(query, 3);
return docs.map(d => d.pageContent).join('\n\n');
},
};
// Get ATP tools
const atpTools = await ATPLangChainTools.fromClient(client);
// Combine tools
const allTools = [...atpTools, retrieverTool];
// Create RAG agent
const agent = createToolCallingAgent({
llm,
tools: allTools,
prompt: ChatPromptTemplate.fromMessages([
['system', 'You have access to documents and APIs. Search documents first when needed.'],
['human', '{input}'],
['placeholder', '{agent_scratchpad}'],
]),
});
Best Practices
1. Tool Organization
// ✅ Good: Organized by domain
const userTools = await ATPLangChainTools.fromClient(client, {
includeAPIs: ['users'],
});
const orderTools = await ATPLangChainTools.fromClient(client, {
includeAPIs: ['orders'],
});
// Create specialized agents
const userAgent = createToolCallingAgent({ llm, tools: userTools, prompt: userPrompt });
const orderAgent = createToolCallingAgent({ llm, tools: orderTools, prompt: orderPrompt });
2. Error Handling
// ✅ Good: Comprehensive error handling
try {
const tools = await ATPLangChainTools.fromClient(client);
const agent = createToolCallingAgent({ llm, tools, prompt });
const executor = new AgentExecutor({ agent, tools });
const result = await executor.invoke({ input: query });
return result.output;
} catch (error) {
if (error.message.includes('timeout')) {
// Handle timeout
} else if (error.message.includes('rate limit')) {
// Handle rate limit
} else {
// General error handling
}
}
3. Performance Optimization
// ✅ Good: Cache tools
let cachedTools: any[] | null = null;
async function getTools() {
if (!cachedTools) {
cachedTools = await ATPLangChainTools.fromClient(client);
}
return cachedTools;
}
// Reuse tools across invocations
const tools = await getTools();
4. Prompt Engineering
// ✅ Good: Clear instructions
const prompt = ChatPromptTemplate.fromMessages([
[
'system',
`You are a helpful assistant with access to the following tools:
- calculator: For mathematical operations
- users: For user management
- orders: For order processing
Always:
1. Use the most specific tool for the task
2. Verify results before responding
3. Ask for clarification if needed`,
],
['human', '{input}'],
['placeholder', '{agent_scratchpad}'],
]);
Next Steps
- LangGraph Advanced: Build complex agent workflows
- Client Tools: Create client-side tools
- Pause/Resume: Handle async operations
- Examples: Full example projects
Common Issues
Tools Not Found
Problem: LangChain agent can't find tools
Solution: Ensure client is connected before creating tools:
await client.init();
await client.connect(); // Important!
const tools = await ATPLangChainTools.fromClient(client);
Type Errors
Problem: TypeScript type errors with tools
Solution: Use proper types:
import { StructuredTool } from '@langchain/core/tools';
const tools: StructuredTool[] = await ATPLangChainTools.fromClient(client);
Performance Issues
Problem: Slow agent execution
Solution:
- Cache tools
- Use batch callbacks
- Limit tool selection
const tools = await ATPLangChainTools.fromClient(client, {
maxTools: 10, // Limit number of tools
});