# Directory Structure
```
├── .gitignore
├── jest.config.js
├── package.json
├── README.md
├── src
│ ├── __tests__
│ │ └── index.test.ts
│ ├── index.ts
│ └── types.ts
├── tsconfig.json
└── vitest.config.ts
```
# Files
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
node_modules/
dist/
coverage/
*.log
.DS_Store
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
# mcp-langchain-ts-client
A LangChain.js client for Model Context Protocol.
This is a port of [rectalogic/langchain-mcp](https://github.com/rectalogic/langchain-mcp) to the JS/TS LangChain and MCP APIs.
## Installation
```bash
npm install mcp-langchain-ts-client
```
## Usage
```typescript
const serverParams = {
command: "npx",
args: [
"-y",
"@modelcontextprotocol/server-everything"
]
}
// Initialize the toolkit
const toolkit = new MCPToolkit(serverParams);
await toolkit.initialize();
// Extract LangChain.js compatible tools
const tools = toolkit.tools;
// Use the tools
import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { ChatAnthropic } from "@langchain/anthropic";
const llm = new ChatAnthropic({ model: 'claude-3-5-sonnet-20241022' });
const agent = createReactAgent({ llm, tools });
```
```
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
```typescript
```
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
```javascript
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/__tests__/**/*.test.ts'],
};
```
--------------------------------------------------------------------------------
/vitest.config.ts:
--------------------------------------------------------------------------------
```typescript
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
include: ['src/**/*.test.ts'],
coverage: {
reporter: ['text', 'json', 'html'],
},
},
})
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"outDir": "./dist",
"declaration": true,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"rootDir": "./src"
},
"include": ["src"],
"exclude": ["node_modules", "**/__tests__/*"]
}
```
--------------------------------------------------------------------------------
/src/__tests__/index.test.ts:
--------------------------------------------------------------------------------
```typescript
import { describe, it, expect } from 'vitest';
import { MCPToolkit } from '../index';
const serverParams = {
command: "npx",
args: [
"-y",
"@modelcontextprotocol/server-everything"
]
}
const toolkit = new MCPToolkit(serverParams);
await toolkit.initialize();
const toolsByName = toolkit.tools.reduce((acc, tool) => {
acc[tool.name] = tool;
return acc;
});
describe('MCPToolkit', () => {
describe('initialize', () => {
it('should initialize the toolkit', () => {
expect(toolkit.tools.length).toBeGreaterThan(0);
});
});
describe('tools', () => {
it('should echo'), async () => {
const tool = toolsByName['echo'];
const result = await tool.invoke({ message: 'hello' });
expect(result).toBe('hello');
}
});
});
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
{
"name": "mcp-langchain-ts-client",
"version": "0.0.2.1",
"description": "TypeScript client for MCP Langchain",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"repository": {
"type": "git",
"url": "git+https://github.com/isaacwasserman/mcp-langchain-ts-client.git"
},
"bugs": {
"url": "https://github.com/isaacwasserman/mcp-langchain-ts-client/issues"
},
"homepage": "https://github.com/isaacwasserman/mcp-langchain-ts-client#readme",
"files": [
"dist"
],
"scripts": {
"build": "tsc",
"test": "vitest run",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage",
"prepare": "npm run build"
},
"keywords": [
"typescript",
"langchain",
"mcp"
],
"author": "Isaac Wasserman",
"license": "MIT",
"devDependencies": {
"@vitest/coverage-v8": "^1.2.0",
"typescript": "^5.0.0",
"vite": "^5.0.0",
"vitest": "^1.2.0"
},
"peerDependencies": {
"typescript": ">=4.0.0"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.3"
}
}
```
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
```typescript
import { CallToolRequest, CallToolResultSchema, ListToolsResult, ListToolsResultSchema } from "@modelcontextprotocol/sdk/types.js";
import { StdioServerParameters } from "@modelcontextprotocol/sdk/client/stdio.js";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import { BaseToolkit, StructuredToolInterface, StructuredToolParams, tool, Tool } from "@langchain/core/tools";
import { z } from "zod";
export { StdioServerParameters } from "@modelcontextprotocol/sdk/client/stdio.js";
export class MCPToolkit extends BaseToolkit {
tools: Tool[] = [];
_tools: ListToolsResult | null = null;
model_config: any;
transport: StdioClientTransport | null = null;
client: Client | null = null;
constructor(serverParams: StdioServerParameters) {
super();
this.transport = new StdioClientTransport(serverParams);
}
async initialize() {
if (this._tools === null) {
this.client = new Client({
name: "langchain-js-client",
version: "1.0.0",
}, {
capabilities: {}
});
if (this.transport === null) {
throw new Error("Transport is not initialized");
}
await this.client.connect(this.transport);
this._tools = await this.client.request(
{ method: "tools/list" },
ListToolsResultSchema
);
this.tools = await this.get_tools();
}
}
async get_tools(): Promise<Tool[]> {
if (this._tools === null || this.client === null) {
throw new Error("Must initialize the toolkit first");
}
const toolsPromises = this._tools.tools.map(async (tool: any) => {
if (this.client === null) {
throw new Error("Client is not initialized");
}
return await MCPTool({
client: this.client,
name: tool.name,
description: tool.description || "",
argsSchema: createSchemaModel(tool.inputSchema)
});
});
return Promise.all(toolsPromises);
}
}
export async function MCPTool({ client, name, description, argsSchema }: { client: Client, name: string, description: string, argsSchema: any }): Promise<Tool> {
return tool(
async (input): Promise<string> => {
const req: CallToolRequest = { method: "tools/call", params: { name: name, arguments: input } };
const res = await client.request(req, CallToolResultSchema);
const content = res.content;
const contentString = JSON.stringify(content);
return contentString;
},
{
name: name,
description: description,
schema: argsSchema,
}
);
}
function createSchemaModel(inputSchema: { type: "object"; properties?: import("zod").objectOutputType<{}, import("zod").ZodTypeAny, "passthrough"> | undefined; } & { [k: string]: unknown; }): any {
if (inputSchema.type !== "object" || !inputSchema.properties) {
throw new Error("Invalid schema type or missing properties");
}
const schemaProperties = Object.entries(inputSchema.properties).reduce((acc, [key, value]) => {
acc[key] = z.any(); // You can customize this to map specific types
return acc;
}, {} as Record<string, import("zod").ZodTypeAny>);
return z.object(schemaProperties);
}
```