548 - 《MCP》
我也来写一篇关于 MCP。包括 MCP 的 What、图解、Server 的写法与调试、Host 的写法等。
1、MCP 大家应该都已经听得很多了,我这里就再粗略介绍一下。
大模型虽然已经很厉害了,但它仍然没有办法访问到一些内部的数据或者文本之外的其他格式的数据。所以就需要定义一些额外的 Tool 来扩展大模型的能力,而大模型调用 Tool 的过程,通常就叫做 Function Call 。同时大模型加上 Tool 的组合也被称为 Agent 。
这种方式有个问题是,这些 Tool 都是封闭系统的,没有办法在多个 Agent 之间做共享。如果大家还有印象,会记得 OpenAI 之前还搞过一个插件市场。然后就像 USB 协议统一不同设备的连接一样,MCP 是大模型和数据和工具之前的协议。
2、图解。
这张图把多个角色之间的关系解释的很清楚。MCP 有三个角色,1)Host,AI 应用,通常包含多个 Client,2)Client,与 server 1:1 连接的 client,3)Server,封装 Tool、Resource 和 Prompt 。(注:很多人把 Client 和 Host 搞混了)
这张是 Server 视角的图。
3、如何写一个 MCP Server 。
我个人目前用到两种,基于 @modelcontextprotocol/sdk 和基于 fastmcp 。前者更稳妥一些;后者是基于前者的封装,代码量更少,但对于 sse 的处理我有遇到问题然后回退到了前者。
1)基于 @modelcontextprotocol/sdk 的方式。初始化 Mcp Server、定义 tool、resource 和 prompt,然后和 stdio 或 sse transport 相连。参考 https://github.com/sorrycc/browsercopilot/blob/master/server/src/mcp2.ts 。
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
const server = new McpServer({ name: '', version: '0.0.0' });
server.tool('openUrl', 'Open a URL', { url: z.string() }, async (args) => {});
const transport = new StdioServerTransport();
await server.connect(transport);
2)基于 fastmcp,简单场景可以用这个。
import { FastMCP } from "fastmcp";
const server = new FastMCP({ name: "", version: "0.0.0" });
server.addTool({ name, description, params, execute: async (args) => {} });
server.start({
transportType: "stdio",
});
4、Server 的调试有几个方法。
1)是用官方的 @modelcontextprotocol/inspector 来调试,启动后,然后 Connect、List Tools 和 Run Tool 即可。
2)写单测,创建 MCP Client,连 Server,测试 Tool 等。见 https://github.com/umijs/umi-mcp/blob/master/src/cli.test.ts 。
3)是找现成的 MCP Host,接入大模型进行测试,比如 Cline、Cursor、takumi 等。模型推荐用 Groq 上便宜且快的 qwen-qwq-32b,或者用 Grok 上 $150/M 免费用的 grok-3-fast-beta 。
5、如何写一个 MCP Host 。
MCP Host 的实现简单说就是这样:创建 MCP client,连接 MCP Server 以获取 tools,然后通过 Function Call 的方式和大模型交互。
支持 Function Call 我理解有几种方式。
1)基于 vercel 的 ai sdk。我在实现 takumi 时,第一个版本是基于 ai 的。他支持 function call,也支持 mcp 。写起来很方便,参考 https://sdk.vercel.ai/docs/ai-sdk-core/tools-and-tool-calling#mcp-tools 。但用封好的框架还是有代价的,你的可控性就会比较差。比如想要达到 Cline、Cursor 和 ChatWise 的效果就会做不到,比如不能在调用 Tool 之前先流式打印意图,比如不能一次只调用一个 Tool 等。同时,由于 ai sdk 基于 Function Call,遇到不支持 Function Call 的大模型就歇菜了。
2)基于结构化数据。后来我把 takumi 的实现改用了这种方式。bolt.new、Cline 和 ChatWise 的实现都是如此。好处除了支持不支持 Function Call 的大模型外,我理解还有个好处是流式,比如可以让模型先输出意图再输出 Tool Use 。
附一份 ChatWise 使用 MCP 时的 PROMPT。
You only have access to the tools provided below. You can only use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. Today is: 2025-04-11
# Tool Use Formatting
Tool use is formatted using XML-style tags. The tool use is enclosed in <use_mcp_tool></use_mcp_tool> and each parameter is similarly enclosed within its own set of t