mcp-use
Version:
A utility library for integrating Model Context Protocol (MCP) with LangChain, Zod, and related tools. Provides helpers for schema conversion, event streaming, and SDK usage.
91 lines (90 loc) • 3.32 kB
JavaScript
import { JSONSchemaToZod } from '@dmitryrechkin/json-schema-to-zod';
import { DynamicStructuredTool } from '@langchain/core/tools';
import { z } from 'zod';
import { logger } from '../logging.js';
import { BaseAdapter } from './base.js';
function schemaToZod(schema) {
try {
return JSONSchemaToZod.convert(schema);
}
catch (err) {
logger.warn(`Failed to convert JSON schema to Zod: ${err}`);
return z.any();
}
}
function parseMcpToolResult(toolResult) {
if (toolResult.isError) {
throw new Error(`Tool execution failed: ${toolResult.content}`);
}
if (!toolResult.content || toolResult.content.length === 0) {
throw new Error('Tool execution returned no content');
}
let decoded = '';
for (const item of toolResult.content) {
switch (item.type) {
case 'text': {
decoded += item.text;
break;
}
case 'image': {
decoded += item.data;
break;
}
case 'resource': {
const res = item.resource;
if (res?.text !== undefined) {
decoded += res.text;
}
else if (res?.blob !== undefined) {
// eslint-disable-next-line node/prefer-global/buffer
decoded += res.blob instanceof Uint8Array || res.blob instanceof Buffer
// eslint-disable-next-line node/prefer-global/buffer
? Buffer.from(res.blob).toString('base64')
: String(res.blob);
}
else {
throw new Error(`Unexpected resource type: ${res?.type}`);
}
break;
}
default:
throw new Error(`Unexpected content type: ${item.type}`);
}
}
return decoded;
}
export class LangChainAdapter extends BaseAdapter {
constructor(disallowedTools = []) {
super(disallowedTools);
}
/**
* Convert a single MCP tool specification into a LangChainJS structured tool.
*/
convertTool(mcpTool, connector) {
// Filter out disallowed tools early.
if (this.disallowedTools.includes(mcpTool.name)) {
return null;
}
// Derive a strict Zod schema for the tool's arguments.
const argsSchema = mcpTool.inputSchema
? schemaToZod(mcpTool.inputSchema)
: z.object({}).optional();
const tool = new DynamicStructuredTool({
name: mcpTool.name ?? 'NO NAME',
description: mcpTool.description ?? '', // Blank is acceptable but discouraged.
schema: argsSchema,
func: async (input) => {
logger.debug(`MCP tool \"${mcpTool.name}\" received input: ${JSON.stringify(input)}`);
try {
const result = await connector.callTool(mcpTool.name, input);
return parseMcpToolResult(result);
}
catch (err) {
logger.error(`Error executing MCP tool: ${err}`);
return `Error executing MCP tool: ${String(err)}`;
}
},
});
return tool;
}
}