在上一篇文章中,我们探讨了大模型是如何通过“工具调用 (Function Calling)”拥有双手的。但随着工具越来越多,痛点也随之而来:如果每个 AI 平台(如 ChatGPT、Claude、甚至你自研的 Copilot)都需要一套特定的代码来接入你的天气接口或知识库,这无疑是一场维护灾难。
为了解决这个“各自为战”的局面,MCP(Model Context Protocol,模型上下文协议) 诞生了。它就像是 AI 界的 USB-C 接口——只要你的工具遵循 MCP 标准暴露出去,任何支持 MCP 的 AI 都能直接即插即用!
本文将结合实际项目代码,带你深入理解 MCP 的核心原理,并实战演示两种最主流的 MCP 通信方式:Stdio (标准输入输出) 与 SSE (Server-Sent Events)。
一、 MCP 的核心架构与原理
MCP 的本质是一个 客户端-服务端 (Client-Server) 架构。它将系统解耦为两部分:
MCP Server (服务端):负责定义工具 (Tools)、提供资源 (Resources) 和执行具体逻辑(比如真正去查天气或搜数据库)。它是能力的提供者。
MCP Client (客户端):通常由大模型应用(如我们的 AI Copilot)扮演。它负责将模型生成的意图翻译为 MCP 协议格式,向 Server 发起请求,并将结果返回给模型。它是能力的消费者。
在我们的项目中,我们首先需要创建一个通用的 MCP Server 实例,把我们之前写的工具(如查天气、搜 React 文档)统统注册进去:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { SCRAPE_TOOLS } from "../tools";
export function createScrapeMcpServer() { const server = new McpServer({ name: "ai-copilot-scrape-tools", version: "1.0.0", });
for (const [toolName, definition] of Object.entries(SCRAPE_TOOLS)) { server.registerTool( toolName, { description: definition.description, inputSchema: definition.schema.shape, annotations: definition.annotations, }, async (input: unknown) => { const text = await definition.execute(input as never); return definition.toMcpResult(text); } ); }
return server; }
|
亮点:这段代码是纯粹的业务逻辑,它不关心网络是如何传输的。这就是 MCP 设计的美妙之处:逻辑与通信解耦。
二、 实践方式一:基于命令行的 Stdio 传输流
适用场景:本地脚本、后台守护进程、Electron 桌面应用。 原理:AI 客户端通过运行一段终端命令(如 node server.js)唤起一个子进程。它们之间不走 HTTP 网络,而是直接通过操作系统的 stdin (标准输入) 和 stdout (标准输出) 进行 JSON-RPC 消息交互。
1. 搭建 Stdio Server
服务器端只需要将刚才创建的逻辑 Server 挂载到 StdioServerTransport 上即可:
1 2 3 4 5 6 7 8 9 10 11 12 13
| import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { createScrapeMcpServer } from "./create-server";
async function main() { const server = createScrapeMcpServer(); const transport = new StdioServerTransport();
await server.connect(transport); console.error("[MCP] scrape stdio server running"); }
main().catch((error) => process.exit(1));
|
2. 客户端接入 Stdio
客户端需要通过指定执行命令(如 tsx 运行入口文件)来连接:
1 2 3 4 5 6 7 8 9 10 11 12
| if (transport === "stdio") { const tsxCommand = process.platform === "win32" ? "tsx.cmd" : "tsx"; const mcpServerEntry = path.join(process.cwd(), ...MCP_STDIO_ENTRY_PATH);
return createMCPClient({ transport: new StdioClientTransport({ command: tsxCommand, args: [mcpServerEntry], }), }); }
|
三、 实践方式二:基于 Web 标准的 SSE 传输流
适用场景:Web 应用、前后端分离架构、Next.js 路由、分布式微服务。 原理:Web 环境下无法直接拉起子进程,因此 MCP 提供了一套基于 HTTP 的网络方案。它非常巧妙地运用了两种 HTTP 机制:
1. 搭建 SSE Server (Next.js Api Route)
在 Next.js 中,我们需要处理复杂的会话管理,因为 HTTP 是无状态的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
const transports = global.__scrapeMcpSseTransports__ ?? {};
export default async function handler(req, res) { if (req.method === "GET") { const transport = new SSEServerTransport("/api/scrape/mcp", res); transports[transport.sessionId] = transport;
transport.onclose = () => delete transports[transport.sessionId];
const server = createScrapeMcpServer(); await server.connect(transport); return; }
if (req.method === "POST") { const sessionId = req.query.sessionId as string; const transport = transports[sessionId];
if (!transport) return res.status(404).send("Session not found"); await transport.handlePostMessage(req, res, req.body); return; } }
|
2. 客户端接入 SSE
而在客户端侧,接入变得异常简单,只需要填入后端的 API 地址即可:
1 2 3 4 5 6 7
| return createMCPClient({ transport: { type: "sse", url: `${requestUrl.origin}/api/scrape/mcp`, }, });
|
四、 总结与最佳实践
从项目源码可以看出,MCP 通过极为优雅的抽象层,让我们只写一次业务逻辑(create-server.ts),就能适配本地进程(Stdio)和网络微服务(SSE)两种完全不同的场景。
在实际开发中:
掌握了 MCP,你的大模型应用就不再是一座孤岛,而是能够随时随地接入万物 API 的超级中枢。
源码已托管至🫱 GitHub,期待你的 Star🌟。