An MCP server is only half the equation. The other half is the client that connects to the server, discovers available tools, and makes them available to the AI model.
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
const transport = new StdioClientTransport({
command: "node",
args: ["./my-mcp-server/build/index.js"]
});
const client = new Client({
name: "my-app",
version: "1.0.0"
});
await client.connect(transport);
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
server_params = StdioServerParameters(
command="python",
args=["my_mcp_server.py"]
)
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
tools = await session.list_tools()
After connecting, the client automatically discovers available tools:
// List all available tools
const { tools } = await client.listTools();
for (const tool of tools) {
console.log(`Tool: ${tool.name}`);
console.log(`Description: ${tool.description}`);
console.log(`Schema: ${JSON.stringify(tool.inputSchema)}`);
}
Client Server
│ │
│── initialize ────────────────▶│
│◀── capabilities ─────────────│
│ │
│── tools/list ────────────────▶│
│◀── tool definitions ─────────│
│ │
│── tools/call (get_weather) ──▶│
│◀── result ───────────────────│
During connection setup, client and server exchange their capabilities:
const capabilities = await client.getServerCapabilities();
// Check which features the server supports
if (capabilities.tools) {
// Server offers tools
const tools = await client.listTools();
}
if (capabilities.resources) {
// Server offers resources
const resources = await client.listResources();
}
if (capabilities.prompts) {
// Server offers prompts
const prompts = await client.listPrompts();
}
The real value comes when MCP tools are available to the AI model:
import Anthropic from "@anthropic-ai/sdk";
const anthropic = new Anthropic();
// Convert MCP tools to Anthropic format
const mcpTools = await client.listTools();
const anthropicTools = mcpTools.tools.map(tool => ({
name: tool.name,
description: tool.description,
input_schema: tool.inputSchema
}));
// Call AI model with tools
const response = await anthropic.messages.create({
model: "claude-sonnet-4-20250514",
messages: [{ role: "user", content: "What's the weather in Berlin?" }],
tools: anthropicTools
});
// Execute tool calls via MCP
for (const block of response.content) {
if (block.type === "tool_use") {
const result = await client.callTool({
name: block.name,
arguments: block.input
});
}
}
try {
await client.connect(transport);
} catch (error) {
if (error.code === "CONNECTION_REFUSED") {
console.error("MCP server unreachable");
}
// Fallback without MCP tools
}
try {
const result = await client.callTool({ name: "get_weather", arguments: { city: "Berlin" } });
} catch (error) {
// Return error message to the LLM
return { content: [{ type: "text", text: `Error: ${error.message}` }] };
}
Practical tip: Always implement fallback logic for when an MCP server is unreachable. Your application should degrade gracefully without MCP tools — not crash completely.