#
tokens: 1997/50000 9/9 files
lines: off (toggle) GitHub
raw markdown copy
# 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);
}
```