import type { Stream } from 'node:stream'; import { MastraBase } from '@mastra/core/base'; import type { MCPServerBase } from '@mastra/core/mcp'; import type { Tool } from '@mastra/core/tools'; import type { ElicitRequest, ElicitResult, ProgressNotification, Prompt, Resource, ResourceTemplate } from '@modelcontextprotocol/sdk/types.js'; import type { MastraMCPServerDefinition } from './client.js'; /** * Configuration options for creating an MCPClient instance. */ export interface MCPClientOptions { /** Optional unique identifier to prevent memory leaks when creating multiple instances with identical configurations */ id?: string; /** Map of server names to their connection configurations (stdio or HTTP-based) */ servers: Record; /** Optional global timeout in milliseconds for all servers (default: 60000ms) */ timeout?: number; } /** * MCPClient manages multiple MCP server connections and their tools in a Mastra application. * * This class handles connection lifecycle, tool namespacing, and provides access to tools, * resources, prompts, and elicitation across all configured servers. * * @example * ```typescript * import { MCPClient } from '@mastra/mcp'; * import { Agent } from '@mastra/core/agent'; * * const mcp = new MCPClient({ * servers: { * weather: { * url: new URL('http://localhost:8080/sse'), * }, * stockPrice: { * command: 'npx', * args: ['tsx', 'stock-price.ts'], * env: { API_KEY: 'your-api-key' }, * }, * }, * timeout: 30000, * }); * * const agent = new Agent({ * id: 'multi-tool-agent', * name: 'Multi-tool Agent', * instructions: 'You have access to multiple tools.', * model: 'openai/gpt-4o', * tools: await mcp.listTools(), * }); * ``` */ export declare class MCPClient extends MastraBase { private serverConfigs; private id; private defaultTimeout; private mcpClientsById; private disconnectPromise; /** * Creates a new MCPClient instance for managing MCP server connections. * * The client automatically manages connection lifecycle and prevents memory leaks by * caching instances with identical configurations. * * @param args - Configuration options * @param args.id - Optional unique identifier to allow multiple instances with same config * @param args.servers - Map of server names to server configurations * @param args.timeout - Optional global timeout in milliseconds (default: 60000) * * @throws {Error} If multiple instances with identical config are created without an ID * * @example * ```typescript * const mcp = new MCPClient({ * servers: { * weatherServer: { * url: new URL('http://localhost:8080/sse'), * requestInit: { * headers: { Authorization: 'Bearer token' } * } * } * }, * timeout: 30000 * }); * ``` */ constructor(args: MCPClientOptions); /** * Provides access to progress-related operations for tracking long-running operations. * * Progress tracking allows MCP servers to send updates about the status of ongoing operations, * providing real-time feedback to users about task completion and current state. * * @example * ```typescript * // Set up handler for progress updates from a server * await mcp.progress.onUpdate('serverName', (params) => { * console.log(`Progress: ${params.progress}%`); * console.log(`Status: ${params.message}`); * * if (params.total) { * console.log(`Completed ${params.progress} of ${params.total} items`); * } * }); * ``` */ get progress(): { onUpdate: (serverName: string, handler: (params: ProgressNotification["params"]) => void) => Promise; }; /** * Provides access to elicitation-related operations for interactive user input collection. * * Elicitation allows MCP servers to request structured information from users during tool execution. * * @example * ```typescript * // Set up handler for elicitation requests from a server * await mcp.elicitation.onRequest('serverName', async (request) => { * console.log(`Server requests: ${request.message}`); * console.log('Schema:', request.requestedSchema); * * // Collect user input and return response * return { * action: 'accept', * content: { name: 'John Doe', email: 'john@example.com' } * }; * }); * ``` */ get elicitation(): { /** * Sets up a handler function for elicitation requests from a specific server. * * The handler receives requests for user input and must return a response with * action ('accept', 'decline', or 'cancel') and optional content. * * @param serverName - Name of the server to handle elicitation requests for * @param handler - Function to handle elicitation requests * @throws {MastraError} If setting up the handler fails * * @example * ```typescript * await mcp.elicitation.onRequest('weatherServer', async (request) => { * // Prompt user for input * const userInput = await promptUser(request.requestedSchema); * return { action: 'accept', content: userInput }; * }); * ``` */ onRequest: (serverName: string, handler: (request: ElicitRequest["params"]) => Promise) => Promise; }; /** * Provides access to resource-related operations across all configured servers. * * Resources represent data exposed by MCP servers (files, database records, API responses, etc.). * * @example * ```typescript * // List all resources from all servers * const allResources = await mcp.resources.list(); * Object.entries(allResources).forEach(([serverName, resources]) => { * console.log(`${serverName}: ${resources.length} resources`); * }); * * // Read a specific resource * const content = await mcp.resources.read('weatherServer', 'file://data.json'); * * // Subscribe to resource updates * await mcp.resources.subscribe('weatherServer', 'file://data.json'); * await mcp.resources.onUpdated('weatherServer', async (params) => { * console.log(`Resource updated: ${params.uri}`); * }); * ``` */ get resources(): { /** * Lists all available resources from all configured servers. * * Returns a map of server names to their resource arrays. Errors for individual * servers are logged but don't throw - failed servers return empty arrays. * * @returns Promise resolving to object mapping server names to resource arrays * * @example * ```typescript * const resources = await mcp.resources.list(); * console.log(resources.weatherServer); // Array of resources * ``` */ list: () => Promise>; /** * Lists all available resource templates from all configured servers. * * Resource templates are URI templates (RFC 6570) describing dynamic resources. * Errors for individual servers are logged but don't throw. * * @returns Promise resolving to object mapping server names to template arrays * * @example * ```typescript * const templates = await mcp.resources.templates(); * console.log(templates.weatherServer); // Array of resource templates * ``` */ templates: () => Promise>; /** * Reads the content of a specific resource from a server. * * @param serverName - Name of the server to read from * @param uri - URI of the resource to read * @returns Promise resolving to the resource content * @throws {MastraError} If reading the resource fails * * @example * ```typescript * const content = await mcp.resources.read('weatherServer', 'file://config.json'); * console.log(content.contents[0].text); * ``` */ read: (serverName: string, uri: string) => Promise<{ [x: string]: unknown; contents: ({ uri: string; text: string; mimeType?: string | undefined; _meta?: { [x: string]: unknown; } | undefined; } | { uri: string; blob: string; mimeType?: string | undefined; _meta?: { [x: string]: unknown; } | undefined; })[]; _meta?: { [x: string]: unknown; progressToken?: string | number | undefined; "io.modelcontextprotocol/related-task"?: { taskId: string; } | undefined; } | undefined; }>; /** * Subscribes to updates for a specific resource on a server. * * @param serverName - Name of the server * @param uri - URI of the resource to subscribe to * @returns Promise resolving when subscription is established * @throws {MastraError} If subscription fails * * @example * ```typescript * await mcp.resources.subscribe('weatherServer', 'file://config.json'); * ``` */ subscribe: (serverName: string, uri: string) => Promise<{ _meta?: { [x: string]: unknown; progressToken?: string | number | undefined; "io.modelcontextprotocol/related-task"?: { taskId: string; } | undefined; } | undefined; }>; /** * Unsubscribes from updates for a specific resource on a server. * * @param serverName - Name of the server * @param uri - URI of the resource to unsubscribe from * @returns Promise resolving when unsubscription is complete * @throws {MastraError} If unsubscription fails * * @example * ```typescript * await mcp.resources.unsubscribe('weatherServer', 'file://config.json'); * ``` */ unsubscribe: (serverName: string, uri: string) => Promise<{ _meta?: { [x: string]: unknown; progressToken?: string | number | undefined; "io.modelcontextprotocol/related-task"?: { taskId: string; } | undefined; } | undefined; }>; /** * Sets a notification handler for when subscribed resources are updated on a server. * * @param serverName - Name of the server to monitor * @param handler - Callback function receiving the updated resource URI * @returns Promise resolving when handler is registered * @throws {MastraError} If setting up the handler fails * * @example * ```typescript * await mcp.resources.onUpdated('weatherServer', async (params) => { * console.log(`Resource updated: ${params.uri}`); * const content = await mcp.resources.read('weatherServer', params.uri); * }); * ``` */ onUpdated: (serverName: string, handler: (params: { uri: string; }) => void) => Promise; /** * Sets a notification handler for when the resource list changes on a server. * * @param serverName - Name of the server to monitor * @param handler - Callback function invoked when resources are added/removed * @returns Promise resolving when handler is registered * @throws {MastraError} If setting up the handler fails * * @example * ```typescript * await mcp.resources.onListChanged('weatherServer', async () => { * console.log('Resource list changed, re-fetching...'); * const resources = await mcp.resources.list(); * }); * ``` */ onListChanged: (serverName: string, handler: () => void) => Promise; }; /** * Provides access to prompt-related operations across all configured servers. * * Prompts are reusable message templates exposed by MCP servers that can be parameterized * and used for AI interactions. * * @example * ```typescript * // List all prompts from all servers * const allPrompts = await mcp.prompts.list(); * Object.entries(allPrompts).forEach(([serverName, prompts]) => { * console.log(`${serverName}: ${prompts.map(p => p.name).join(', ')}`); * }); * * // Get a specific prompt with arguments * const prompt = await mcp.prompts.get({ * serverName: 'weatherServer', * name: 'forecast-template', * args: { city: 'London', days: 7 } * }); * ``` */ get prompts(): { /** * Lists all available prompts from all configured servers. * * Returns a map of server names to their prompt arrays. Errors for individual * servers are logged but don't throw - failed servers return empty arrays. * * @returns Promise resolving to object mapping server names to prompt arrays * * @example * ```typescript * const prompts = await mcp.prompts.list(); * console.log(prompts.weatherServer); // Array of prompts * ``` */ list: () => Promise>; /** * Retrieves a specific prompt with its messages from a server. * * @param params - Parameters for the prompt request * @param params.serverName - Name of the server to retrieve from * @param params.name - Name of the prompt to retrieve * @param params.args - Optional arguments to populate the prompt template * @returns Promise resolving to the prompt result with messages * @throws {MastraError} If fetching the prompt fails * * @example * ```typescript * const prompt = await mcp.prompts.get({ * serverName: 'weatherServer', * name: 'forecast', * args: { city: 'London' }, * }); * console.log(prompt.messages); * ``` */ get: ({ serverName, name, args }: { serverName: string; name: string; args?: Record; }) => Promise<{ [x: string]: unknown; messages: { role: "user" | "assistant"; content: { type: "text"; text: string; annotations?: { audience?: ("user" | "assistant")[] | undefined; priority?: number | undefined; lastModified?: string | undefined; } | undefined; _meta?: { [x: string]: unknown; } | undefined; } | { type: "image"; data: string; mimeType: string; annotations?: { audience?: ("user" | "assistant")[] | undefined; priority?: number | undefined; lastModified?: string | undefined; } | undefined; _meta?: { [x: string]: unknown; } | undefined; } | { type: "audio"; data: string; mimeType: string; annotations?: { audience?: ("user" | "assistant")[] | undefined; priority?: number | undefined; lastModified?: string | undefined; } | undefined; _meta?: { [x: string]: unknown; } | undefined; } | { uri: string; name: string; type: "resource_link"; description?: string | undefined; mimeType?: string | undefined; size?: number | undefined; annotations?: { audience?: ("user" | "assistant")[] | undefined; priority?: number | undefined; lastModified?: string | undefined; } | undefined; _meta?: { [x: string]: unknown; } | undefined; icons?: { src: string; mimeType?: string | undefined; sizes?: string[] | undefined; theme?: "light" | "dark" | undefined; }[] | undefined; title?: string | undefined; } | { type: "resource"; resource: { uri: string; text: string; mimeType?: string | undefined; _meta?: { [x: string]: unknown; } | undefined; } | { uri: string; blob: string; mimeType?: string | undefined; _meta?: { [x: string]: unknown; } | undefined; }; annotations?: { audience?: ("user" | "assistant")[] | undefined; priority?: number | undefined; lastModified?: string | undefined; } | undefined; _meta?: { [x: string]: unknown; } | undefined; }; }[]; _meta?: { [x: string]: unknown; progressToken?: string | number | undefined; "io.modelcontextprotocol/related-task"?: { taskId: string; } | undefined; } | undefined; description?: string | undefined; }>; /** * Sets a notification handler for when the prompt list changes on a server. * * @param serverName - Name of the server to monitor * @param handler - Callback function invoked when prompts are added/removed/modified * @returns Promise resolving when handler is registered * @throws {MastraError} If setting up the handler fails * * @example * ```typescript * await mcp.prompts.onListChanged('weatherServer', async () => { * console.log('Prompt list changed, re-fetching...'); * const prompts = await mcp.prompts.list(); * }); * ``` */ onListChanged: (serverName: string, handler: () => void) => Promise; }; private addToInstanceCache; private makeId; /** * Disconnects from all MCP servers and cleans up resources. * * This method gracefully closes all server connections and clears internal caches. * Safe to call multiple times - subsequent calls will wait for the first disconnect to complete. * * @example * ```typescript * // Cleanup on application shutdown * process.on('SIGTERM', async () => { * await mcp.disconnect(); * process.exit(0); * }); * ``` */ disconnect(): Promise; /** * Reconnects a single MCP server by name. * * If the server is already connected, it will be forcefully disconnected and reconnected. * If the server has never been connected, a new connection will be established. * * @param serverName - The name of the server to reconnect (must match a key in `servers`) * @throws {Error} If the server name is not found in the configuration * * @example * ```typescript * // Reconnect a specific server after it fails * await mcp.reconnectServer('weatherServer'); * ``` */ reconnectServer(serverName: string): Promise; /** * Returns instructions advertised by connected MCP servers during initialize. * * Servers that have not connected yet, or did not advertise instructions, * return `undefined`. */ getServerInstructions(): Record; /** * Retrieves all tools from all configured servers with namespaced names. * * Tool names are namespaced as `serverName_toolName` to prevent conflicts between servers. * This method is intended to be passed directly to an Agent definition. * * @returns Object mapping namespaced tool names to tool implementations. * Errors for individual servers are logged but don't throw - failed servers are skipped. * Transient connection failures are retried once after reconnecting the affected server. * * @example * ```typescript * const agent = new Agent({ * id: 'multi-tool-agent', * name: 'Multi-tool Agent', * instructions: 'You have access to weather and stock tools.', * model: 'openai/gpt-4', * tools: await mcp.listTools(), // weather_getWeather, stockPrice_getPrice * }); * ``` */ listTools(): Promise>>; /** * Retrieves all tools from all configured servers with namespaced names, * along with any per-server errors. * * Like listTools(), but also returns errors for servers that failed to connect * or list tools. This allows callers to report specific failure reasons per server. * * @returns Object with `tools` (successful tools) and `errors` (failed servers with error messages). * Transient connection failures are retried once after reconnecting the affected server. * * @example * ```typescript * const { tools, errors } = await mcp.listToolsWithErrors(); * for (const [name, err] of Object.entries(errors)) { * console.error(`Server ${name} failed: ${err}`); * } * ``` */ listToolsWithErrors(): Promise<{ tools: Record>; errors: Record; }>; /** * Returns toolsets organized by server name for dynamic tool injection. * * Unlike listTools(), this returns tools grouped by server without namespacing. * This is intended to be passed dynamically to the generate() or stream() method. * * @returns Object mapping server names to their tool collections. * Errors for individual servers are logged but don't throw - failed servers are skipped. * * @example * ```typescript * const agent = new Agent({ * id: 'dynamic-agent', * name: 'Dynamic Agent', * instructions: 'You can use tools dynamically.', * model: 'openai/gpt-4', * }); * * const response = await agent.stream(prompt, { * toolsets: await mcp.listToolsets(), // { weather: {...}, stockPrice: {...} } * }); * ``` */ listToolsets(): Promise>>>; /** * Returns toolsets organized by server name, along with any per-server errors. * * Like listToolsets(), but also returns errors for servers that failed to connect * or list tools. This allows callers to report specific failure reasons per server. * * @returns Object with `toolsets` (successful servers) and `errors` (failed servers with error messages). * Transient connection failures are retried once after reconnecting the affected server. * * @example * ```typescript * const { toolsets, errors } = await mcp.listToolsetsWithErrors(); * for (const [name, err] of Object.entries(errors)) { * console.error(`Server ${name} failed: ${err}`); * } * ``` */ listToolsetsWithErrors(): Promise<{ toolsets: Record>>; errors: Record; }>; /** * Creates MCPServerBase-compatible proxy objects for each server connection * in this MCPClient. The returned record can be spread directly into * Mastra's `mcpServers` config so that external (non-Mastra) servers * appear in Studio alongside native MCPServer instances. * * @returns Record mapping server names to MCPServerBase proxy instances * * @example * ```typescript * const mcp = new MCPClient({ * servers: { * trailhead: { command: 'npx', args: ['trailhead-server'] }, * }, * }); * * const mastra = new Mastra({ * mcpServers: { * ...mcp.toMCPServerProxies(), * }, * }); * ``` */ toMCPServerProxies(): Record; /** * Gets current session IDs for all connected MCP clients using Streamable HTTP transport. * * Returns an object mapping server names to their session IDs. Only includes servers * that are currently connected via Streamable HTTP transport. * * @returns Object mapping server names to session IDs * * @example * ```typescript * const sessions = mcp.sessionIds; * console.log(sessions); * // { weatherServer: 'abc-123', stockServer: 'def-456' } * ``` */ get sessionIds(): Record; /** * Gets the stderr stream of a connected stdio server. * * Only available for servers using stdio transport with `stderr: 'pipe'`. * Returns null if the server is not connected, not using stdio, or stderr is not piped. * * @param serverName - The name of the server * @returns The stderr stream, or null */ getServerStderr(serverName: string): Stream | null; private getConnectedClient; private getConnectedClientForServer; private getToolsForServer; } //# sourceMappingURL=configuration.d.ts.map