Writing an MCP Client in TypeScript
Host apps like Claude Desktop include an MCP client, but you can embed one in your own app too. That lets your product use any MCP server as a tool provider.
Connect and list tools
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
const mcp = new Client({ name: 'my-app', version: '1.0.0' });
await mcp.connect(new StdioClientTransport({ command: 'node', args: ['../github-mcp/build/index.js'] }));
const { tools } = await mcp.listTools();
console.log(tools.map((t) => t.name));
For a remote server, use StreamableHTTPClientTransport from @modelcontextprotocol/sdk/client/streamableHttp.js with the server URL.
Hand the tools to Claude
MCP tool definitions map almost directly onto Claude API tools: inputSchema becomes input_schema.
import Anthropic from '@anthropic-ai/sdk';
const anthropic = new Anthropic();
const claudeTools: Anthropic.Tool[] = tools.map((t) => ({
name: t.name,
description: t.description ?? '',
input_schema: t.inputSchema as Anthropic.Tool.InputSchema,
}));
const messages: Anthropic.MessageParam[] = [{ role: 'user', content: 'How popular is facebook/react?' }];
while (true) {
const res = await anthropic.messages.create({ model: 'claude-opus-5-5', max_tokens: 16000, tools: claudeTools, messages });
messages.push({ role: 'assistant', content: res.content });
if (res.stop_reason !== 'tool_use') break;
const results: Anthropic.ToolResultBlockParam[] = [];
for (const block of res.content) {
if (block.type !== 'tool_use') continue;
const out = await mcp.callTool({ name: block.name, arguments: block.input as Record<string, unknown> });
const text = (out.content as any[]).filter((c) => c.type === 'text').map((c) => c.text).join('\n');
results.push({ type: 'tool_result', tool_use_id: block.id, content: text, is_error: Boolean(out.isError) });
}
messages.push({ role: 'user', content: results });
}
await mcp.close();
Production notes
- Prefix tool names with the server name if you connect several servers, to avoid collisions.
- Cache
listTools()and refresh when the server sends a list-changed notification. - Show users which server a tool comes from, and ask before running untrusted servers.
Was this article helpful?
Comments
No comments yet. Have a question or a tip? Be the first to share it.