| title | Connecting to MCP Servers |
|---|---|
| description | Learn how to connect to local and remote MCP servers |
| icon | plug |
The MCPClientManager is your gateway to MCP servers. It handles connections, manages multiple servers, and provides a unified interface for calling tools, reading resources, and accessing prompts.
Use MCPClientManager when you need to:
- Connect to one or more MCP servers
- Execute tools programmatically (without an LLM)
- Build applications that aggregate tools from multiple servers
- Set up test environments for your MCP server
MCP servers come in two transport types:
STDIO servers run as subprocesses on your machine. You provide a command and arguments, and the SDK spawns the process.
import { MCPClientManager } from "@mcpjam/sdk";
const manager = new MCPClientManager({
myServer: {
command: "node",
args: ["./my-server.js"],
env: {
API_KEY: process.env.MY_API_KEY,
},
},
});Best for:
- Local development
- Servers you're building
- CLI-based MCP servers
- Servers requiring local resources
HTTP servers connect via SSE (Server-Sent Events) or Streamable HTTP over the network.
const manager = new MCPClientManager({
asana: {
url: "https://mcp.asana.com/sse",
requestInit: {
headers: {
Authorization: `Bearer ${process.env.ASANA_TOKEN}`,
},
},
},
});Best for:
- Production MCP servers
- Third-party integrations (Asana, Slack, etc.)
- Shared team servers
- Serverless deployments
For HTTP servers, MCPClientManager supports three auth patterns:
accessTokenfor a static bearer tokenrefreshToken+clientIdfor non-interactive OAuth token exchange with automatic refreshauthProviderwhen you need to provide a custom MCP SDK OAuth provider
If you already have a refresh token, you can let the SDK handle access token exchange and refresh for you:
const manager = new MCPClientManager({
asana: {
url: "https://mcp.asana.com/mcp",
refreshToken: process.env.ASANA_REFRESH_TOKEN!,
clientId: process.env.ASANA_CLIENT_ID!,
clientSecret: process.env.ASANA_CLIENT_SECRET,
},
});With this configuration, the SDK:
- exchanges the refresh token for an access token during connection
- automatically retries with a fresh access token when the server returns an auth challenge
- stores rotated refresh tokens returned by the authorization server
After configuring servers, you must explicitly connect:
// Connect to a specific server
await manager.connectToServer("myServer");
// Now you can use it
const tools = await manager.listTools("myServer");
console.log("Available tools:", tools.tools.map((tool) => tool.name));
// Always disconnect when done
await manager.disconnectServer("myServer");A key strength of MCPClientManager is managing multiple servers simultaneously:
const manager = new MCPClientManager({
math: {
command: "npx",
args: ["-y", "@modelcontextprotocol/server-everything"],
},
asana: {
url: "https://mcp.asana.com/sse",
requestInit: {
headers: { Authorization: `Bearer ${token}` },
},
},
github: {
url: "https://mcp.github.com/sse",
requestInit: {
headers: { Authorization: `Bearer ${ghToken}` },
},
},
});
// Connect to all
await Promise.all([
manager.connectToServer("math"),
manager.connectToServer("asana"),
manager.connectToServer("github"),
]);
// Aggregate tools from all servers
const allTools = await manager.getTools();
// Now allTools contains tools from math, asana, and githubYou can call tools without involving an LLM—useful for unit tests:
// Direct tool execution
const result = await manager.executeTool("math", "add", { a: 5, b: 3 });
console.log(result); // 8
// Test your server's tools deterministically
expect(result).toBe(8);The most common pattern is connecting servers, then creating a TestAgent with their tools:
const manager = new MCPClientManager({
myServer: { command: "node", args: ["./server.js"] },
});
await manager.connectToServer("myServer");
// Get tools for TestAgent
const tools = await manager.getTools();
// Create agent with those tools
const agent = new TestAgent({
tools,
model: "anthropic/claude-sonnet-4-20250514",
apiKey: process.env.ANTHROPIC_API_KEY,
});
// Now prompt the agent
const result = await agent.prompt("Add 2 and 3");Connection failures throw errors. Wrap in try/catch for production code:
try {
await manager.connectToServer("myServer");
} catch (error) {
console.error("Failed to connect:", error.message);
// Handle gracefully - maybe fall back to another server
}Verify a server is responsive:
try {
await manager.pingServer("myServer");
} catch {
console.warn("Server not responding, reconnecting...");
await manager.disconnectServer("myServer");
await manager.connectToServer("myServer");
}