Skip to Content

MCPClient

The MCPClient class provides a way to manage multiple MCP server connections and their tools in a Mastra application. It handles connection lifecycle, tool namespacing, and provides access to tools across all configured servers.

This class replaces the deprecated MastraMCPClient.

Constructor

Creates a new instance of the MCPClient class.

constructor({ id?: string; servers: Record<string, MastraMCPServerDefinition>; timeout?: number; }: MCPClientOptions)

MCPClientOptions


id?:

string
Optional unique identifier for the configuration instance. Use this to prevent memory leaks when creating multiple instances with identical configurations.

servers:

Record<string, MastraMCPServerDefinition>
A map of server configurations, where each key is a unique server identifier and the value is the server configuration.

timeout?:

number
= 60000
Global timeout value in milliseconds for all servers unless overridden in individual server configs.

MastraMCPServerDefinition

Each server in the servers map is configured using the MastraMCPServerDefinition type. The transport type is detected based on the provided parameters:

  • If command is provided, it uses the Stdio transport.
  • If url is provided, it first attempts to use the Streamable HTTP transport and falls back to the legacy SSE transport if the initial connection fails.

command?:

string
For Stdio servers: The command to execute.

args?:

string[]
For Stdio servers: Arguments to pass to the command.

env?:

Record<string, string>
For Stdio servers: Environment variables to set for the command.

url?:

URL
For HTTP servers (Streamable HTTP or SSE): The URL of the server.

requestInit?:

RequestInit
For HTTP servers: Request configuration for the fetch API.

eventSourceInit?:

EventSourceInit
For SSE fallback: Custom fetch configuration for SSE connections. Required when using custom headers with SSE.

logger?:

LogHandler
Optional additional handler for logging.

timeout?:

number
Server-specific timeout in milliseconds.

capabilities?:

ClientCapabilities
Server-specific capabilities configuration.

enableServerLogs?:

boolean
= true
Whether to enable logging for this server.

Methods

getTools()

Retrieves all tools from all configured servers, with tool names namespaced by their server name (in the format serverName_toolName) to prevent conflicts. Intended to be passed onto an Agent definition.

new Agent({ tools: await mcp.getTools() });

getToolsets()

Returns an object mapping namespaced tool names (in the format serverName.toolName) to their tool implementations. Intended to be passed dynamically into the generate or stream method.

const res = await agent.stream(prompt, { toolsets: await mcp.getToolsets(), });

disconnect()

Disconnects from all MCP servers and cleans up resources.

async disconnect(): Promise<void>

resources Property

The MCPClient instance has a resources property that provides access to resource-related operations.

const mcpClient = new MCPClient({ /* ...servers configuration... */ }); // Access resource methods via mcpClient.resources const allResourcesByServer = await mcpClient.resources.list(); const templatesByServer = await mcpClient.resources.templates(); // ... and so on for other resource methods.

resources.list()

Retrieves all available resources from all connected MCP servers, grouped by server name.

async list(): Promise<Record<string, Resource[]>>

Example:

const resourcesByServer = await mcpClient.resources.list(); for (const serverName in resourcesByServer) { console.log(`Resources from ${serverName}:`, resourcesByServer[serverName]); }

resources.templates()

Retrieves all available resource templates from all connected MCP servers, grouped by server name.

async templates(): Promise<Record<string, ResourceTemplate[]>>

Example:

const templatesByServer = await mcpClient.resources.templates(); for (const serverName in templatesByServer) { console.log(`Templates from ${serverName}:`, templatesByServer[serverName]); }

resources.read(serverName: string, uri: string)

Reads the content of a specific resource from a named server.

async read(serverName: string, uri: string): Promise<ReadResourceResult>
  • serverName: The identifier of the server (key used in the servers constructor option).
  • uri: The URI of the resource to read.

Example:

const content = await mcpClient.resources.read( "myWeatherServer", "weather://current", ); console.log("Current weather:", content.contents[0].text);

resources.subscribe(serverName: string, uri: string)

Subscribes to updates for a specific resource on a named server.

async subscribe(serverName: string, uri: string): Promise<object>

Example:

await mcpClient.resources.subscribe("myWeatherServer", "weather://current");

resources.unsubscribe(serverName: string, uri: string)

Unsubscribes from updates for a specific resource on a named server.

async unsubscribe(serverName: string, uri: string): Promise<object>

Example:

await mcpClient.resources.unsubscribe("myWeatherServer", "weather://current");

resources.onUpdated(serverName: string, handler: (params: { uri: string }) => void)

Sets a notification handler that will be called when a subscribed resource on a specific server is updated.

async onUpdated(serverName: string, handler: (params: { uri: string }) => void): Promise<void>

Example:

mcpClient.resources.onUpdated("myWeatherServer", (params) => { console.log(`Resource updated on myWeatherServer: ${params.uri}`); // You might want to re-fetch the resource content here // await mcpClient.resources.read("myWeatherServer", params.uri); });

resources.onListChanged(serverName: string, handler: () => void)

Sets a notification handler that will be called when the overall list of available resources changes on a specific server.

async onListChanged(serverName: string, handler: () => void): Promise<void>

Example:

mcpClient.resources.onListChanged("myWeatherServer", () => { console.log("Resource list changed on myWeatherServer."); // You should re-fetch the list of resources // await mcpClient.resources.list(); });

Examples

Static Tool Configuration

For tools where you have a single connection to the MCP server for you entire app, use getTools() and pass the tools to your agent:

import { MCPClient } from "@mastra/mcp"; import { Agent } from "@mastra/core/agent"; import { openai } from "@ai-sdk/openai"; const mcp = new MCPClient({ servers: { stockPrice: { command: "npx", args: ["tsx", "stock-price.ts"], env: { API_KEY: "your-api-key", }, log: (logMessage) => { console.log(`[${logMessage.level}] ${logMessage.message}`); }, }, weather: { url: new URL("http://localhost:8080/sse"), }, }, timeout: 30000, // Global 30s timeout }); // Create an agent with access to all tools const agent = new Agent({ name: "Multi-tool Agent", instructions: "You have access to multiple tool servers.", model: openai("gpt-4"), tools: await mcp.getTools(), }); // Example of using resource methods async function checkWeatherResource() { try { const weatherResources = await mcp.resources.list(); if (weatherResources.weather && weatherResources.weather.length > 0) { const currentWeatherURI = weatherResources.weather[0].uri; const weatherData = await mcp.resources.read( "weather", currentWeatherURI, ); console.log("Weather data:", weatherData.contents[0].text); } } catch (error) { console.error("Error fetching weather resource:", error); } } checkWeatherResource();

Dynamic toolsets

When you need a new MCP connection for each user, use getToolsets() and add the tools when calling stream or generate:

import { Agent } from "@mastra/core/agent"; import { MCPClient } from "@mastra/mcp"; import { openai } from "@ai-sdk/openai"; // Create the agent first, without any tools const agent = new Agent({ name: "Multi-tool Agent", instructions: "You help users check stocks and weather.", model: openai("gpt-4"), }); // Later, configure MCP with user-specific settings const mcp = new MCPClient({ servers: { stockPrice: { command: "npx", args: ["tsx", "stock-price.ts"], env: { API_KEY: "user-123-api-key", }, timeout: 20000, // Server-specific timeout }, weather: { url: new URL("http://localhost:8080/sse"), requestInit: { headers: { Authorization: `Bearer user-123-token`, }, }, }, }, }); // Pass all toolsets to stream() or generate() const response = await agent.stream( "How is AAPL doing and what is the weather?", { toolsets: await mcp.getToolsets(), }, );

Instance Management

The MCPClient class includes built-in memory leak prevention for managing multiple instances:

  1. Creating multiple instances with identical configurations without an id will throw an error to prevent memory leaks
  2. If you need multiple instances with identical configurations, provide a unique id for each instance
  3. Call await configuration.disconnect() before recreating an instance with the same configuration
  4. If you only need one instance, consider moving the configuration to a higher scope to avoid recreation

For example, if you try to create multiple instances with the same configuration without an id:

// First instance - OK const mcp1 = new MCPClient({ servers: { /* ... */ }, }); // Second instance with same config - Will throw an error const mcp2 = new MCPClient({ servers: { /* ... */ }, }); // To fix, either: // 1. Add unique IDs const mcp3 = new MCPClient({ id: "instance-1", servers: { /* ... */ }, }); // 2. Or disconnect before recreating await mcp1.disconnect(); const mcp4 = new MCPClient({ servers: { /* ... */ }, });

Server Lifecycle

MCPClient handles server connections gracefully:

  1. Automatic connection management for multiple servers
  2. Graceful server shutdown to prevent error messages during development
  3. Proper cleanup of resources when disconnecting

Using SSE Request Headers

When using the legacy SSE MCP transport, you must configure both requestInit and eventSourceInit due to a bug in the MCP SDK:

const sseClient = new MCPClient({ servers: { exampleServer: { url: new URL("https://your-mcp-server.com/sse"), // Note: requestInit alone isn't enough for SSE requestInit: { headers: { Authorization: "Bearer your-token", }, }, // This is also required for SSE connections with custom headers eventSourceInit: { fetch(input: Request | URL | string, init?: RequestInit) { const headers = new Headers(init?.headers || {}); headers.set("Authorization", "Bearer your-token"); return fetch(input, { ...init, headers, }); }, }, }, }, });