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?:
servers:
timeout?:
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?:
args?:
env?:
url?:
requestInit?:
eventSourceInit?:
logger?:
timeout?:
capabilities?:
enableServerLogs?:
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 theservers
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:
- Creating multiple instances with identical configurations without an
id
will throw an error to prevent memory leaks - If you need multiple instances with identical configurations, provide a unique
id
for each instance - Call
await configuration.disconnect()
before recreating an instance with the same configuration - 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:
- Automatic connection management for multiple servers
- Graceful server shutdown to prevent error messages during development
- 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,
});
},
},
},
},
});
Related Information
- For creating MCP servers, see the MCPServer documentation.
- For more about the Model Context Protocol, see the @modelcontextprotocol/sdk documentation .