UNPKG

@ai-sdk/mcp

Version:

The **Model Context Protocol (MCP) client** for the [AI SDK](https://ai-sdk.dev/docs) lets you connect to MCP servers and use their tools with AI SDK functions like `generateText` and `streamText`.

451 lines (444 loc) 12.7 kB
// src/tool/json-rpc-message.ts import { parseJSON } from "@ai-sdk/provider-utils"; import { z as z2 } from "zod/v4"; // src/tool/types.ts import { z } from "zod/v4"; var ToolMetaSchema = z.optional(z.record(z.string(), z.unknown())); var ClientOrServerImplementationSchema = z.looseObject({ name: z.string(), version: z.string(), title: z.optional(z.string()) }); var BaseParamsSchema = z.looseObject({ _meta: z.optional(z.object({}).loose()) }); var ResultSchema = BaseParamsSchema; var RequestSchema = z.object({ method: z.string(), params: z.optional(BaseParamsSchema) }); var ElicitationCapabilitySchema = z.object({ applyDefaults: z.optional(z.boolean()) }).loose(); var ServerCapabilitiesSchema = z.looseObject({ experimental: z.optional(z.object({}).loose()), logging: z.optional(z.object({}).loose()), prompts: z.optional( z.looseObject({ listChanged: z.optional(z.boolean()) }) ), resources: z.optional( z.looseObject({ subscribe: z.optional(z.boolean()), listChanged: z.optional(z.boolean()) }) ), tools: z.optional( z.looseObject({ listChanged: z.optional(z.boolean()) }) ), elicitation: z.optional(ElicitationCapabilitySchema) }); var ClientCapabilitiesSchema = z.object({ elicitation: z.optional(ElicitationCapabilitySchema) }).loose(); var InitializeResultSchema = ResultSchema.extend({ protocolVersion: z.string(), capabilities: ServerCapabilitiesSchema, serverInfo: ClientOrServerImplementationSchema, instructions: z.optional(z.string()) }); var PaginatedResultSchema = ResultSchema.extend({ nextCursor: z.optional(z.string()) }); var ToolSchema = z.object({ name: z.string(), /** * @see https://modelcontextprotocol.io/specification/2025-11-25/server/tools#tool */ title: z.optional(z.string()), description: z.optional(z.string()), inputSchema: z.object({ type: z.literal("object"), properties: z.optional(z.object({}).loose()) }).loose(), /** * @see https://modelcontextprotocol.io/specification/2025-06-18/server/tools#output-schema */ outputSchema: z.optional(z.object({}).loose()), annotations: z.optional( z.object({ title: z.optional(z.string()) }).loose() ), _meta: ToolMetaSchema }).loose(); var ListToolsResultSchema = PaginatedResultSchema.extend({ tools: z.array(ToolSchema) }); var TextContentSchema = z.object({ type: z.literal("text"), text: z.string() }).loose(); var ImageContentSchema = z.object({ type: z.literal("image"), data: z.base64(), mimeType: z.string() }).loose(); var ResourceSchema = z.object({ uri: z.string(), name: z.string(), title: z.optional(z.string()), description: z.optional(z.string()), mimeType: z.optional(z.string()), size: z.optional(z.number()) }).loose(); var ListResourcesResultSchema = PaginatedResultSchema.extend({ resources: z.array(ResourceSchema) }); var ResourceContentsSchema = z.object({ /** * The URI of this resource. */ uri: z.string(), /** * Optional display name of the resource content. */ name: z.optional(z.string()), /** * Optional human readable title. */ title: z.optional(z.string()), /** * The MIME type of this resource, if known. */ mimeType: z.optional(z.string()) }).loose(); var TextResourceContentsSchema = ResourceContentsSchema.extend({ text: z.string() }); var BlobResourceContentsSchema = ResourceContentsSchema.extend({ blob: z.base64() }); var EmbeddedResourceSchema = z.object({ type: z.literal("resource"), resource: z.union([TextResourceContentsSchema, BlobResourceContentsSchema]) }).loose(); var ResourceLinkContentSchema = z.object({ type: z.literal("resource_link"), uri: z.string(), name: z.string(), description: z.optional(z.string()), mimeType: z.optional(z.string()) }).loose(); var CallToolResultSchema = ResultSchema.extend({ content: z.array( z.union([ TextContentSchema, ImageContentSchema, EmbeddedResourceSchema, ResourceLinkContentSchema ]) ), /** * @see https://modelcontextprotocol.io/specification/2025-06-18/server/tools#structured-content */ structuredContent: z.optional(z.unknown()), isError: z.boolean().default(false).optional() }).or( ResultSchema.extend({ toolResult: z.unknown() }) ); var ResourceTemplateSchema = z.object({ uriTemplate: z.string(), name: z.string(), title: z.optional(z.string()), description: z.optional(z.string()), mimeType: z.optional(z.string()) }).loose(); var ListResourceTemplatesResultSchema = ResultSchema.extend({ resourceTemplates: z.array(ResourceTemplateSchema) }); var ReadResourceResultSchema = ResultSchema.extend({ contents: z.array( z.union([TextResourceContentsSchema, BlobResourceContentsSchema]) ) }); var PromptArgumentSchema = z.object({ name: z.string(), description: z.optional(z.string()), required: z.optional(z.boolean()) }).loose(); var PromptSchema = z.object({ name: z.string(), title: z.optional(z.string()), description: z.optional(z.string()), arguments: z.optional(z.array(PromptArgumentSchema)) }).loose(); var ListPromptsResultSchema = PaginatedResultSchema.extend({ prompts: z.array(PromptSchema) }); var PromptMessageSchema = z.object({ role: z.union([z.literal("user"), z.literal("assistant")]), content: z.union([ TextContentSchema, ImageContentSchema, EmbeddedResourceSchema, ResourceLinkContentSchema ]) }).loose(); var GetPromptResultSchema = ResultSchema.extend({ description: z.optional(z.string()), messages: z.array(PromptMessageSchema) }); var ElicitationRequestParamsSchema = BaseParamsSchema.extend({ message: z.string(), requestedSchema: z.unknown() }); var ElicitationRequestSchema = RequestSchema.extend({ method: z.literal("elicitation/create"), params: ElicitationRequestParamsSchema }); var ElicitResultSchema = ResultSchema.extend({ action: z.union([ z.literal("accept"), z.literal("decline"), z.literal("cancel") ]), content: z.optional(z.record(z.string(), z.unknown())) }); // src/tool/json-rpc-message.ts var JSONRPC_VERSION = "2.0"; var JSONRPCRequestSchema = z2.object({ jsonrpc: z2.literal(JSONRPC_VERSION), id: z2.union([z2.string(), z2.number().int()]) }).merge(RequestSchema).strict(); var JSONRPCResponseSchema = z2.object({ jsonrpc: z2.literal(JSONRPC_VERSION), id: z2.union([z2.string(), z2.number().int()]), result: ResultSchema }).strict(); var JSONRPCErrorSchema = z2.object({ jsonrpc: z2.literal(JSONRPC_VERSION), id: z2.union([z2.string(), z2.number().int()]), error: z2.object({ code: z2.number().int(), message: z2.string(), data: z2.optional(z2.unknown()) }) }).strict(); var JSONRPCNotificationSchema = z2.object({ jsonrpc: z2.literal(JSONRPC_VERSION) }).merge( z2.object({ method: z2.string(), params: z2.optional(BaseParamsSchema) }) ).strict(); var JSONRPCMessageSchema = z2.union([ JSONRPCRequestSchema, JSONRPCNotificationSchema, JSONRPCResponseSchema, JSONRPCErrorSchema ]); async function parseJSONRPCMessage(text) { return JSONRPCMessageSchema.parse(await parseJSON({ text })); } // src/error/mcp-client-error.ts import { AISDKError } from "@ai-sdk/provider"; var name = "AI_MCPClientError"; var marker = `vercel.ai.error.${name}`; var symbol = Symbol.for(marker); var _a, _b; var MCPClientError = class extends (_b = AISDKError, _a = symbol, _b) { constructor({ name: name2 = "MCPClientError", message, cause, data, code, statusCode, url, responseBody }) { super({ name: name2, message, cause }); this[_a] = true; this.data = data; this.code = code; this.statusCode = statusCode; this.url = url; this.responseBody = responseBody; } static isInstance(error) { return AISDKError.hasMarker(error, marker); } }; // src/tool/mcp-stdio/create-child-process.ts import { spawn } from "child_process"; // src/tool/mcp-stdio/get-environment.ts function getEnvironment(customEnv) { const DEFAULT_INHERITED_ENV_VARS = globalThis.process.platform === "win32" ? [ "APPDATA", "HOMEDRIVE", "HOMEPATH", "LOCALAPPDATA", "PATH", "PROCESSOR_ARCHITECTURE", "SYSTEMDRIVE", "SYSTEMROOT", "TEMP", "USERNAME", "USERPROFILE" ] : ["HOME", "LOGNAME", "PATH", "SHELL", "TERM", "USER"]; const env = customEnv ? { ...customEnv } : {}; for (const key of DEFAULT_INHERITED_ENV_VARS) { const value = globalThis.process.env[key]; if (value === void 0) { continue; } if (value.startsWith("()")) { continue; } env[key] = value; } return env; } // src/tool/mcp-stdio/create-child-process.ts function createChildProcess(config, signal) { var _a2, _b2; return spawn(config.command, (_a2 = config.args) != null ? _a2 : [], { env: getEnvironment(config.env), stdio: ["pipe", "pipe", (_b2 = config.stderr) != null ? _b2 : "inherit"], shell: false, signal, windowsHide: globalThis.process.platform === "win32" && isElectron(), cwd: config.cwd }); } function isElectron() { return "type" in globalThis.process; } // src/tool/mcp-stdio/mcp-stdio-transport.ts var StdioMCPTransport = class { constructor(server) { this.abortController = new AbortController(); this.readBuffer = new ReadBuffer(); this.serverParams = server; } async start() { if (this.process) { throw new MCPClientError({ message: "StdioMCPTransport already started." }); } return new Promise((resolve, reject) => { var _a2, _b2, _c, _d; try { const process = createChildProcess( this.serverParams, this.abortController.signal ); this.process = process; this.process.on("error", (error) => { var _a3, _b3; if (error.name === "AbortError") { (_a3 = this.onclose) == null ? void 0 : _a3.call(this); return; } reject(error); (_b3 = this.onerror) == null ? void 0 : _b3.call(this, error); }); this.process.on("spawn", () => { resolve(); }); this.process.on("close", (_code) => { var _a3; this.process = void 0; (_a3 = this.onclose) == null ? void 0 : _a3.call(this); }); (_a2 = this.process.stdin) == null ? void 0 : _a2.on("error", (error) => { var _a3; (_a3 = this.onerror) == null ? void 0 : _a3.call(this, error); }); (_b2 = this.process.stdout) == null ? void 0 : _b2.on("data", (chunk) => { this.readBuffer.append(chunk); void this.processReadBuffer(); }); (_c = this.process.stdout) == null ? void 0 : _c.on("error", (error) => { var _a3; (_a3 = this.onerror) == null ? void 0 : _a3.call(this, error); }); } catch (error) { reject(error); (_d = this.onerror) == null ? void 0 : _d.call(this, error); } }); } async processReadBuffer() { var _a2, _b2; while (true) { const line = this.readBuffer.readLine(); if (line === null) { break; } try { const message = await deserializeMessage(line); (_a2 = this.onmessage) == null ? void 0 : _a2.call(this, message); } catch (error) { (_b2 = this.onerror) == null ? void 0 : _b2.call(this, error); } } } async close() { this.abortController.abort(); this.process = void 0; this.readBuffer.clear(); } send(message) { return new Promise((resolve) => { var _a2; if (!((_a2 = this.process) == null ? void 0 : _a2.stdin)) { throw new MCPClientError({ message: "StdioClientTransport not connected" }); } const json = serializeMessage(message); if (this.process.stdin.write(json)) { resolve(); } else { this.process.stdin.once("drain", resolve); } }); } }; var ReadBuffer = class { append(chunk) { this.buffer = this.buffer ? Buffer.concat([this.buffer, chunk]) : chunk; } readLine() { if (!this.buffer) return null; const index = this.buffer.indexOf("\n"); if (index === -1) { return null; } const line = this.buffer.toString("utf8", 0, index); this.buffer = this.buffer.subarray(index + 1); return line; } clear() { this.buffer = void 0; } }; function serializeMessage(message) { return JSON.stringify(message) + "\n"; } async function deserializeMessage(line) { return parseJSONRPCMessage(line); } export { StdioMCPTransport as Experimental_StdioMCPTransport }; //# sourceMappingURL=index.mjs.map