MCP(Model Context Protocol)是什么:一次讲清协议、传输与写 Server
它到底解决什么问题
没有 MCP 之前,每个 AI 应用想接一个数据源(数据库、GitHub、公司内部 API),都要写一遍适配代码。N 个客户端 × M 个数据源 = N×M 份集成。
MCP 把这件事变成 N+M:数据源方写一次 server,客户端方实现一次 client,中间的协议由 MCP 定义。
类比:MCP 之于 AI 应用,类似 LSP 之于编辑器。写一次语言服务器,VS Code、Vim、JetBrains 都能用。
一、架构:client 和 server
┌──────────────┐ MCP 协议 ┌──────────────┐
│ MCP Client │ ◄─────────────────────► │ MCP Server │
│ (Claude 等) │ │ (你的工具) │
└──────────────┘ └──────────────┘
│ │
模型 + 对话逻辑 真正的数据/API
- Host:用户用的应用(如 Claude Desktop),内部为每个 server 起一个 client
- Client:维护与某个 server 的一对一连接,负责能力协商和消息路由
- Server:暴露工具与数据,通常是本地进程或远程服务
职责划分很关键:模型不直接连 server,是 client 把 server 提供的能力翻译成模型的工具定义。
二、三类服务端原语(最容易混淆的部分)
| 原语 | 谁控制 | 典型用途 | 类比 |
|---|---|---|---|
| tools | 模型决定调用 | 有副作用或需参数的操作 | 函数调用 |
| resources | 应用/用户决定读取 | 可 URI 定位的数据 | GET 请求 / 文件 |
| prompts | 用户主动选择 | 预设的提示词模板 | 斜杠命令 |
判断规则很重要:
- 读数据用 resources,别做成 tool——resource 可缓存、可按 URI 引用、不会污染上下文
- 有副作用、需参数化执行 → tool
- 给用户一个「一键执行某流程」的入口 → prompt
反例:把「读取项目 README」做成 read_readme 工具,是常见的设计错误——那应该是 file://README.md 这个 resource。
三、两种传输方式
| 传输 | 场景 | 特点 |
|---|---|---|
| stdio | 本地进程 | 最简单,client 直接 spawn 子进程,用标准输入输出通信 |
| Streamable HTTP | 远程服务 | 单个 HTTP 端点,支持流式响应与会话;取代了早期的 HTTP+SSE 方案 |
本地方案 99% 用 stdio;要给多个用户共享一个 server 才需要 HTTP。
四、写一个最小可用的 Python server
用官方 SDK:
pip install mcp
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("ticket-server")
@mcp.tool()
def query_ticket(ticket_id: str) -> dict:
"""查询工单详情。
当用户询问某个工单的状态、负责人或处理进度时使用。
不需要参数化写入时不要用本工具。
Args:
ticket_id: 工单号,形如 T-2026-001,必填
"""
return {"ticket_id": ticket_id, "status": "processing", "owner": "张三"}
@mcp.resource("ticket://list")
def list_tickets() -> str:
"""返回当前所有工单的列表(只读)"""
return "T-2026-001 processing
T-2026-002 closed"
@mcp.prompt()
def triage(ticket_id: str) -> str:
"""生成工单分诊提示词"""
return f"请分析工单 {ticket_id} 的紧急程度,并给出处理建议。"
if __name__ == "__main__":
mcp.run() # 默认 stdio 传输
三个细节决定好不好用:
- docstring 就是给模型的 description——写清「做什么 / 何时用 / 何时不要用 / 返回什么」
- 返回体要有上限:列表类接口必须截断(如最多 50 条),否则一次吐几十万字符会把上下文撑爆
- 错误要可区分:抛出的异常信息要能区分「鉴权失败 / 不存在 / 限流 / 参数错」,Agent 才可能自我纠正
五、客户端怎么接
Claude Desktop 的配置(claude_desktop_config.json):
{
"mcpServers": {
"ticket": {
"command": "python",
"args": ["/path/to/server.py"],
"env": { "TICKET_API_KEY": "..." }
}
}
}
远程 HTTP server 则用 URL 形式:
{
"mcpServers": {
"ticket": { "url": "https://mcp.example.com/mcp" }
}
}
六、安全:MCP 不是"开了就安全"
这是最容易被忽略的一节。MCP server 相当于给模型开了系统权限:
| 风险 | 说明 | 对策 |
|---|---|---|
| 提示注入 | 工具返回的内容里藏指令,模型照做 | 工具返回内容标记为「数据」而非「指令」;对返回内容做过滤 |
| 权限过大 | 一个 tool 能删库 | 最小权限;写操作单独工具 + 二次确认 |
| 凭证泄露 | key 写进配置文件被读取 | 用环境变量;配置文件不入库 |
| 数据外泄 | 恶意 server 把数据发到外部 | 只装可信 server;审阅开源 server 代码 |
最实用的三条:不确定的 server 先在隔离环境跑;写操作工具必须与人确认;给 server 的凭证只开必要权限。
七、什么时候值得上 MCP
- 你有一个内部系统要被多个 AI 客户端用到 → 值得
- 你只在自家一个应用里调工具 → 直接用 Function Calling 更简单
- 只是读文档 → 可能一个文件读取工具就够,不必写完整 server
相关技能
- MCP Server 定义编写:帮你设计 tools/resources 划分与 schema
- Agent 工具描述优化
- Function Calling 从原理到实现