| name | mcp-client |
| description | Building an application that connects to and consumes MCP servers, or when the user mentions consuming MCP tools programmatically from their own ap... |
MCP Client
When to activate
Building an application that connects to and consumes MCP servers, or when the user mentions consuming MCP tools programmatically from their own application.
When NOT to use
- Building an MCP server that exposes tools — use
mcp-server-builder instead
- Configuring Claude Code to use an existing MCP server — that is
settings.json configuration, not code
- Using MCP tools from within a Claude Code session — those are already available via
/mcp
Instructions
Client vs Server Distinction
- MCP Server: exposes tools, resources, and prompts that AI models can call
- MCP Client: connects to servers, discovers tools, and calls them — this skill covers the client side
- Most applications that use MCP are clients; most published MCP packages are servers
TypeScript Client Setup
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
Local server (stdio transport):
const transport = new StdioClientTransport({
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-filesystem', '/tmp'],
});
const client = new Client({ name: 'my-app', version: '1.0.0' }, { capabilities: {} });
await client.connect(transport);
Remote server (SSE transport):
const transport = new SSEClientTransport(new URL('https://mcp.example.com/sse'));
const client = new Client({ name: 'my-app', version: '1.0.0' }, { capabilities: {} });
await client.connect(transport);
Client Lifecycle
await client.connect(transport);
const { tools } = await client.listTools();
const result = await client.callTool({
name: 'read_file',
arguments: { path: '/tmp/data.json' },
});
await client.close();
Tool Discovery and Caching
Tools do not change while a server is running. Cache the tool list on connect — do not re-fetch per request:
class MCPClientWrapper {
private toolCache: Tool[] | null = null;
async getTools(): Promise<Tool[]> {
if (!this.toolCache) {
const { tools } = await this.client.listTools();
this.toolCache = tools;
}
return this.toolCache;
}
}
Error Handling
try {
const result = await client.callTool({ name: 'unknown_tool', arguments: {} });
} catch (err) {
if (err instanceof McpError) {
}
}
Handle server disconnect by catching transport errors and reconnecting with backoff.
Multi-Server Client
Connect to multiple MCP servers and merge their tool lists into a single routing layer:
const servers = [filesystemClient, githubClient, postgresClient];
async function callTool(name: string, args: unknown) {
for (const server of servers) {
const tools = await server.getTools();
if (tools.find(t => t.name === name)) {
return server.callTool({ name, arguments: args });
}
}
throw new Error(`Tool not found: ${name}`);
}
Meta-MCP Pattern (Agent Using MCP Tools)
Pass discovered tools directly to Claude as the tools array — Claude will call them, you proxy the call to the MCP server:
const { tools } = await client.listTools();
const claudeTools = tools.map(t => ({
name: t.name,
description: t.description,
input_schema: t.inputSchema,
}));
const response = await anthropic.messages.create({
model: 'claude-sonnet-4-5',
tools: claudeTools,
messages: [{ role: 'user', content: userMessage }],
});
for (const block of response.content) {
if (block.type === 'tool_use') {
const mcpResult = await client.callTool({ name: block.name, arguments: block.input });
}
}
Example
An application that connects to three MCP servers (filesystem, GitHub, Postgres), merges their tools, and lets Claude use all of them in a single agentic loop:
- Connect to all three servers on startup, cache tool lists
- Build merged tool array — 23 tools total across three servers
- Pass all 23 tools to Claude with
defer_loading: true for the rarely-used ones
- Claude calls
query_database — client routes to Postgres MCP server
- Claude calls
create_pull_request — client routes to GitHub MCP server
- Result from each tool returned as
tool_result message to Claude
- On shutdown, close all three transports cleanly