02 MCP:给 AI 装一个“万能 USB 接口”
该节点所属的教程路径尚未上线,仅能预览本节内容。
MCP 精简解读:给 AI 装一个“万能 USB 接口”
你有没有遇到过这种场面:想让 AI 帮你查一下 Git 提交记录,它一脸无辜地说“我看不到你的本地仓库”。你给它接上文件工具、数据库工具、内部工单工具,结果每个 AI 应用都要重新接一遍,参数格式还各不相同。MCP 就是来收拾这个烂摊子的。
MCP 到底是个啥
MCP 全称是 Model Context Protocol,中文一般叫“模型上下文协议”。名字拆开看就挺好懂:Model 面向大模型应用,Context 把外部上下文、工具和数据源带给模型,Protocol 用一套标准协议把交互方式定下来。
但它不是“给模型加插件”那么简单。更准确地说,MCP 是 MCP Client 和 MCP Server 之间的通信协议。Host 负责承载用户交互和模型调用,Client 负责和 Server 说话,Server 负责把具体能力暴露出来。
举个很常见的场景。用户问:“帮我看看这个项目最近一次提交改了什么。”模型当然不知道你本地 Git 仓库的提交记录,它得借助外部能力读取 Git 日志。没有 MCP 时,每个 AI 应用都得自己定义一套“怎么连 Git 工具、怎么传参数、怎么拿结果”的方式。有了 MCP 之后,Git 相关能力可以被封装成一个 MCP Server。Host 里的 MCP Client 连上它,先发现有哪些工具,再按协议调用工具,最后把结果交给模型继续分析。
你可以把 MCP 想成 USB 接口协议。在没有 USB 之前,鼠标用 PS/2 接口,打印机用并口,键盘可能又是另一种接口,每换一个设备就得换一套线。MCP 就像 USB 标准:不管你是 U 盘、鼠标还是硬盘,统统插同一个口就能用。AI 应用是电脑,MCP Server 是各种外设,MCP 协议就是那根统一的 USB 线。Git 工具的协议适配集中在 Server 一侧,Agent 或 AI 应用只需理解用户问题、选择工具并组织结果,两边不必为每个客户端重新约定一套私有接口。
它和 Function Calling、Agent 不是一桌菜
一次“读取仓库最新提交”的任务,Function Calling、MCP 和 Agent 可能同时出现,但分别卡在不同位置。
模型先给出结构化的调用意图,比如:
json
{
"name": "read_file",
"arguments": { "path": "/repo/README.md" }
}
OpenAI 把这类机制称为 Function Calling,Anthropic 称为 Tool Use。模型借它输出“调用 read_file,参数是这个路径”这样的结构化数据。MCP 负责把这个意图接到外部系统:工具从哪个 Server 发现、请求如何传输、结果如何返回。Agent 关心任务的下一步,它会读取工具结果,继续调用、结束任务,或等待人工确认。
把三者放在一条请求链路里看更直观:Function Calling 产生命令,MCP 传递命令并连接工具,Agent 决定这条链路何时继续、何时结束。
你可以这样理解:Function Calling 是“点菜”,模型写出菜名和备注;MCP 是“传菜口”,负责把菜单从厨房递到配料区,保证格式统一;Agent 是“大堂经理”,看到菜做好了,决定是直接端给顾客,还是再问问顾客要不要加辣。三者各管一段,谁也替代不了谁。
不同场景关注点也不一样。让模型判断要不要查天气,关键在 Function Calling,重点是模型把意图转成结构化参数;让 Claude Desktop 读取本地文件,关键在 MCP,重点是宿主和本地文件系统之间有标准接口;让 AI 自动排查线上故障,关键在 Agent,重点是多步决策、工具调用和结果反馈。实际项目里三者通常会一起出现,只是主次不同。
Host、Client、Server 各站什么位置
MCP 的通信链路由 Host、Client 和 Server 组成。
Host 是用户使用的 AI 应用,例如 Claude Desktop、Cursor、VS Code 中的 AI 插件或自建 Agent 平台。Client 位于 Host 内部,负责与 MCP Server 建立会话和交换协议消息。一个 Host 可以连多个 Server,通常每个 Server 对应一个 Client 会话。
开发者主要编写 Server。文件读取、SQL 查询、GitHub Issue 查询和内部工单查询等能力,都可以由它向 Host 暴露。Server 后面才是实际的数据源:本地文件、数据库、内部平台、GitHub 或第三方 API。它们不属于 MCP 的协议角色。Host 只通过 Client 调用 Server;查库、请求 API 等底层实现留在 Server 内部处理。
这就像一家餐厅。顾客是用户,餐厅是 Host,服务员是 Client,厨房是 Server。厨房后面才是真正的食材仓库、调料架和灶台,也就是数据源。顾客不需要知道厨房怎么炒菜,只需要告诉服务员“我要什么”,服务员按标准流程把需求传进去,再把做好的菜端出来。
一次 MCP 调用大概怎么走
还是拿“分析这个仓库的最新提交”举例。模型发现自己缺少 Git 日志后,先生成工具调用。Host 把调用交给 MCP Client,Client 通过 JSON-RPC 请求 Server;Server 查询 Git,再把结果沿原路径返回,模型据此组织回答。
工具的名称、description、参数说明和禁用场景会直接影响模型的选择。Server 接收到的参数也必须视为不可信输入:文件读取要限制目录,SQL 要参数化,高危操作要审批,返回数据要脱敏。
还有一步容易被忽略:Client 和 Server 在正式调用工具前,会先完成初始化握手。Client 发送 initialize 请求,带上自己支持的协议版本和能力列表;Server 返回自己支持的协议版本、能力和基础信息。确认之后,Client 再发 initialized 通知,双方才进入可用状态。这一步的意义在于:Client 能通过它知道 Server 支持哪些能力,Server 也能知道 Client 的限制。很多“Server 配好了但工具没出现”的问题,排查时都应该先看初始化阶段有没有失败。
这就像两个特工见面,先各自报出“我是哪条线的”“我会什么技能”“我用的是哪个版本的通话密码”。确认对上了,才开始交换情报。如果暗号对不上,后面的一切都免谈。
MCP 暴露的能力只有 Tools 吗
很多读者聊 MCP 时只讲 Tools,这也正常,因为工具调用最直观。但 MCP 里不只有工具。Server 可以提供 Resources、Tools 和 Prompts 三类能力。
Resources 用于提供只读上下文,例如本地文件、日志片段、数据库 Schema 或配置记录。Tools 用于执行动作,例如查询数据库、发送消息、创建工单或调用业务接口。会主动执行逻辑、可能改变外部状态的能力,应当放在 Tools 中。Prompts 是可复用的提示词模板,例如“按团队规范做代码审查”或“把接口文档整理成测试用例”。Tools 通常由模型选择并调用;Resources 和 Prompts 的展示、选择方式还可以由 Host、用户界面或应用逻辑决定。
用一个生活例子理解这三者。用户说:“我想吃凉拌黄瓜。”LLM 扮演厨师,它知道凉拌黄瓜大概怎么做,但还需要外部条件:Resources 像食材和菜谱,比如冰箱里有什么、家里有没有黄瓜、调料放在哪里;Tools 像具体动作,比如切菜、拌料、开火、下单买菜;Prompts 像家里的固定偏好,比如少放辣、必须放香菜、不能放蒜。
如果工具描述写错了,比如把“黄瓜”描述成“西红柿”,模型就可能选错东西。落到生产环境,工具名、参数描述和返回结构都直接影响 Agent 的选择和后续判断。Server 能启动只是开始,能力边界还要让模型能准确理解。
还有几个进阶能力:Roots 由 Host 通过 Client 告诉 Server 当前会话预期在哪些文件系统根目录内工作;Sampling 允许 Server 请求 Host 侧的 LLM 做一次生成;Elicitation 则是 Server 在执行过程中向用户补充询问信息的能力。平时不一定用得上,但知道它们存在,排查和设计时心里会更有底。
为什么 MCP 用 JSON-RPC
MCP 底层通信使用 JSON-RPC 2.0。REST 更偏资源,比如 /users/1、/orders/100。JSON-RPC 更偏方法调用,比如 tools/call、resources/read。AI 工具调用天然就是“我要执行某个动作”,所以 JSON-RPC 和 MCP 的使用场景比较贴。
一个工具调用请求大概长这样:
json
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "read_file",
"arguments": { "path": "/path/to/file.txt" }
},
"id": 1
}
响应可能是这样:
json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{ "type": "text", "text": "文件内容..." }
]
}
}
失败时才返回 error。JSON-RPC 的消息是文本格式,便于记录日志,也不绑定具体传输方式。代价是它没有 gRPC 那样的强 IDL 和编译期类型约束。
你可以这么区分:REST 是“去图书馆借书”,你关心的是某个资源;JSON-RPC 是“打电话叫外卖”,你关心的是让对方做一个动作。AI 工具调用更接近后者——不是去“获取”某个东西,而是让某个东西“做”一件事。
stdio 和 Streamable HTTP 怎么选
本地 Server 通常使用 stdio。Host 将它作为子进程启动,再通过 stdin/stdout 交换消息;Claude Desktop 中的很多本地 Server 都采用这种方式。它没有额外的网络部署成本,但 Server 运行在本机,文件、Shell 和数据库权限要单独收紧。stdio 模式下,stdout 是 JSON-RPC 消息通道,不能用于打印调试日志。一行 print() 输出就可能破坏消息格式,导致 Host 解析失败或 Server 断连。调试日志应写入 stderr 或文件。
远程 Server 更适合使用 Streamable HTTP。MCP 早期远程传输常见 HTTP + SSE,后来逐步转向 Streamable HTTP。消息收敛到统一端点后,认证、负载均衡和网关接入可以沿用普通 HTTP 服务的运维方式。
选择传输方式时,可以按部署位置和访问范围判断:本地工具、本地文件、个人使用,优先 stdio;团队服务、远程 API、多用户访问,优先 Streamable HTTP;涉及写操作和敏感数据时,不管哪种传输方式,都要额外做鉴权、限流和审计。
说白了,stdio 像“家里自己做饭”,快、不依赖外部网络,但厨房就是你家,用火用电都得自己小心。Streamable HTTP 像“点外卖”,适合多人一起吃饭,但要过平台、骑手、商家好几道手,每个环节都得有规矩。
MCP 接进来就能上生产吗
不能。Demo 中“装一个 Server,问一句话,拿到结果”的链路很短;生产环境要补齐接口约束、审计和运行治理。
Schema 和时间格式:时间字段是 ISO-8601 还是时间戳、金额单位是元还是分、分页默认值是什么,都要写进 Schema、字段说明和示例。Server 要据此校验参数,并返回模型能够据以修正请求的错误信息。
可观测性:一条 Agent 回答可能经过多个 Server 和工具。Trace ID、结构化日志和调用链需要记录调用参数、耗时、结果摘要与错误码,才能定位哪一步影响了最终回答。
权限和安全:本地 stdio 可能获得用户机器上的文件权限,远程 Server 可能连到内部系统。文件目录、可查询的表、是否可写生产 API、是否允许发送邮件都应明确授权。删除、修改、发送和生产调用等写操作还需要二次确认、审计和回滚预案。Server 的 description、Prompt 模板和返回内容同样需要审核:恶意或粗糙的内容可能夹带提示词注入,引导模型读取更多文件或外传信息。
成本归因:模型 Token、向量检索、第三方 API 和云资源都会产生费用。调用应能关联到用户、业务线和工具,否则费用上升时无法判断成本来自哪里。
依赖治理:工具接口的字段、枚举或返回结构发生不兼容变更,也会改变模型的判断。工具级版本、灰度、旧版本保留和自动化兼容性测试应与 Server 一起维护。
企业落地 MCP 前,应该先检查这些维度:Schema 和版本是否完整、权限和安全边界是否清晰、可观测性是否到位、成本归因是否可拆分、依赖治理是否有维护者和更新记录。这些检查项和普通后端服务没有本质区别。MCP 改变了工具接入方式,不会替代鉴权、审计、日志、版本和限流。
MCP 就像给房子新装了一道门。门开了不等于安全,你仍然需要装锁、装监控、做访客登记。不能因为门是标准化的,就以为安全也是标准化的。
写个最小 Server 看看
用官方 Python SDK 写一个天气 Server,大概是这样:
python
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("weather-server")
@mcp.tool()
def get_weather(city: str) -> str:
"""获取指定城市的天气信息"""
return f"{city} 今天晴天,温度 25°C"
@mcp.resource("weather://forecast")
def weather_forecast() -> str:
"""返回未来一周天气预报"""
return "未来七天天气预报..."
if __name__ == "__main__":
mcp.run()
Claude Desktop 里可以这样配:
json
{
"mcpServers": {
"weather-server": {
"command": "uv",
"args": ["run", "--with", "mcp", "/path/to/weather_server.py"]
}
}
}
本地调试建议直接用 MCP Inspector:
bash
npx @modelcontextprotocol/inspector uv run --with mcp /path/to/weather_server.py
它可以模拟 Host 发请求。Server 初始化有没有问题、工具能不能被发现、参数校验有没有报错,基本都能先在这里看出来。
生产环境别依赖全局 Python 里刚好装了 mcp。用虚拟环境解释器,或者像上面这样用 uv run --with mcp 显式声明依赖,会稳一点。如果 Claude Desktop 启动失败,先看 mcp.log,别一上来怀疑协议有问题,很多时候只是路径或依赖没配对。
说到底
MCP 的本质,就是给 AI 应用和外部工具之间定一套统一的“插口标准”。工具提供方只需写一次 Server,所有支持 MCP 的 AI 应用都能按同一套方式发现和调用它。
它不是 Function Calling 的替代品,也不是 Agent 的替代品,而是 Agent 执行链路中负责“连接和传递”的那一层。就像 USB 不负责你拿 U 盘存什么,但它让所有设备插上就能用。理解了这一点,你就不会再把 MCP 当成“又一个新概念”,而是知道它该待在哪个位置上。