MCP 小记
MCP(Model Context Protocol,模型上下文协议),是一套开放协议,标准化 AI 应用与外部工具、资源、提示模板之间的交互方式。它采用客户端 - 服务端架构:MCP 服务端承载并对外暴露工具、资源、提示模板能力;MCP 客户端运行在 AI 宿主(Agent/IDE 等)内部,用来和服务端通信、发起能力调用。

官网SDK
https://github.com/modelcontextprotocol/typescript-sdk 官方提供了各种语言版本的sdk, 我们用 ts 版本的 sdk 感受一下怎么自己的mcp工具。
my-mcp-server.mjs
import { McpServer } from "@modelcontextprotocol/server";
import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
import { z } from "zod";
const server = new McpServer({
name: "My Server",
version: "1.0.0",
});
// 注册工具:查询用户信息
server.registerTool(
"add",
{
description: "Add two numbers",
inputSchema: z.object({
a: z.number().describe("第一个数字"),
b: z.number().describe("第二个数字"),
}),
},
async ({ a, b }) => {
return {
content: [
{
type: "text",
text: `结果:${a + b}`,
},
],
};
},
);
// 注册静态资源
// server.registerResource
// 注册提示词
// server.registerPrompt
const transport = new StdioServerTransport();
await server.connect(transport);fastmcp
https://github.com/punkpeye/fastmcp
import { FastMCP } from "fastmcp";
import { z } from "zod";
const server = new FastMCP({
name: "My Server",
version: "1.0.0",
});
server.addTool({
name: "add",
description: "Add two numbers",
parameters: z.object({
a: z.number(),
b: z.number(),
}),
execute: async (args) => {
return String(args.a + args.b);
},
});
server.start({
transportType: "stdio",
});写一个 MCP Client
https://sitor.cc/courses/super-agent/sa-05-mcp
import { spawn, type ChildProcess } from 'node:child_process';
import { createInterface, type Interface } from 'node:readline';
interface MCPTool {
name: string;
description: string;
inputSchema: Record<string, unknown>;
}
interface MCPCallResult {
content: Array<{ type: string; text?: string }>;
isError?: boolean;
}
export class MCPClient {
private process: ChildProcess | null = null;
private rl: Interface | null = null;
private requestId = 0;
private pending = new Map<number, {
resolve: (v: any) => void;
reject: (e: Error) => void;
}>();
private serverName: string;
constructor(
private command: string,
private args: string[],
private env?: Record<string, string>,
) {
this.serverName = args[args.length - 1]?.replace(/^@.*\//, '')
|| 'mcp-server';
}
async connect(): Promise<void> {
this.process = spawn(this.command, this.args, {
stdio: ['pipe', 'pipe', 'pipe'],
env: { ...process.env, ...this.env },
});
this.process.on('error', (err) => {
console.error(` [MCP] 进程启动失败: ${err.message}`);
});
this.process.stderr?.on('data', () => {});
this.rl = createInterface({ input: this.process.stdout! });
this.rl.on('line', (line) => {
try {
const msg = JSON.parse(line);
if (msg.id !== undefined && this.pending.has(msg.id)) {
const p = this.pending.get(msg.id)!;
this.pending.delete(msg.id);
if (msg.error) {
p.reject(new Error(
`MCP error ${msg.error.code}: ${msg.error.message}`
));
} else {
p.resolve(msg.result);
}
}
} catch { /* ignore non-JSON lines */ }
});
await this.send('initialize', {
protocolVersion: '2024-11-05',
capabilities: {},
clientInfo: { name: 'super-agent', version: '0.5.0' },
});
this.process.stdin!.write(JSON.stringify({
jsonrpc: '2.0',
method: 'notifications/initialized',
}) + '\n');
}
private send(method: string, params?: any): Promise<any> {
return new Promise((resolve, reject) => {
const id = ++this.requestId;
const timeout = setTimeout(() => {
this.pending.delete(id);
reject(new Error(`MCP request timeout: ${method}`));
}, 15000);
this.pending.set(id, {
resolve: (v: any) => { clearTimeout(timeout); resolve(v); },
reject: (e: Error) => { clearTimeout(timeout); reject(e); },
});
const msg = JSON.stringify({ jsonrpc: '2.0', id, method, params });
this.process!.stdin!.write(msg + '\n');
});
}
async listTools(): Promise<MCPTool[]> {
const result = await this.send('tools/list', {});
return result.tools || [];
}
async callTool(
name: string, args: Record<string, unknown>
): Promise<string> {
const result: MCPCallResult = await this.send(
'tools/call', { name, arguments: args }
);
const texts = (result.content || [])
.filter(c => c.type === 'text' && c.text)
.map(c => c.text!);
return texts.join('\n') || '(无返回内容)';
}
async close(): Promise<void> {
if (this.rl) this.rl.close();
if (this.process) this.process.kill();
}
}