适用版本:MCP 官方规范 2026-07-28(资料核对:2026-08-31)。文中的 TypeScript 代码用于说明协议形状,不绑定某个 SDK。
AI 说“我做不到”时,缺的是什么
让 AI 助手总结一篇文章,它可以直接生成文字;让它回答“我在 GitHub 上还有哪些未处理的 issue”,情况就不同了。
模型不知道你的仓库地址,也没有 GitHub token,更不会凭空访问公司的数据库。
应用当然可以为它单独写一个 GitHub 插件,再为另一个模型客户端重写一遍。插件越来越多以后,连接方式、参数描述和权限确认都会各自长成一套。
MCP(Model Context Protocol)要解决的,正是这段连接关系:让 AI 应用用一套公开协议发现外部能力,并按统一消息格式获取上下文或调用工具。1
先把三类参与者摆在桌面上
MCP 不是“模型直接连数据库”。一次连接至少包含三个角色:
查看 Mermaid 源码
flowchart LR
user[用户] --> host[MCP Host<br/>AI 应用]
host --> model[模型]
host --> client1[MCP Client]
host --> client2[MCP Client]
client1 --> server1[MCP Server<br/>GitHub]
client2 --> server2[MCP Server<br/>业务数据库]
server1 --> github[GitHub API]
server2 --> db[(数据库)]- Host:Claude Desktop、Claude Code 或你自己写的 AI 应用,负责协调模型、用户界面和一个或多个 MCP Client。
- Client:由 Host 创建,每个 Client 只维护与一个 Server 的连接,并代为发送 MCP 请求。
- Server:接近真实系统的一侧,可以读取文件、调用 API、查询数据库,或者执行一段计算。
模型只提出“我想调用 search_issues”,真正发出 MCP 请求的是 Host 中的 Client;Server 不直接读取模型状态。
从三个角色到一条消息:MCP 规定了什么
上一节回答了“谁和谁连接”,但还没有回答“连接时说什么”。MCP 规定的是角色之间的共同消息语言:数据层定义 JSON-RPC 消息、能力发现和结果形状。2
传输层只负责把这些消息送到目标进程或网络端点。Server 内部可以使用 Python、TypeScript 或 Go,消息仍然可以通过本地 stdio 或远程 Streamable HTTP 传递。
把一次最小交互画出来,三个角色就能和具体方法对上:
查看 Mermaid 源码
sequenceDiagram
participant H as Host
participant C as MCP Client
participant S as MCP Server
H->>C: initialize
C->>S: tools/list
S-->>C: 工具名称与输入 schema
C->>S: tools/call
S-->>C: CallToolResultinitialize 协商协议版本和双方能力;握手完成后,Client 用 tools/list 发现工具,再用 tools/call 发起调用。Server 返回 MCP 规定的内容块,而不是某个厂商的私有对象。
Server 能声明的能力,正是围绕这组消息组织起来的:
| 能力 | 发现与使用的消息 | 谁驱动 | 返回或产生什么 |
|---|---|---|---|
| Tools | tools/list → tools/call | 模型通过 Host 请求 | 一次计算或操作的 CallToolResult |
| Resources | resources/list → resources/read | Host / 应用决定读取 | 带 URI 的文件、schema 或文档内容 |
| Prompts | prompts/list → prompts/get | 用户选择模板 | 一组可传参的消息模板 |
“Server 暴露能力”具体指三组可发现、可调用的方法。下一节先追踪 Tools 的完整调用;Resources 和 Prompts 使用相同的发现思路,但由不同的方法承载。3 4 5
跟着一次 search_issues 调用走一遍
假设用户问:“帮我找出 rhapsody-site 里本周还没关闭的 issue。”一次调用大致会经过下面的顺序:
查看 Mermaid 源码
sequenceDiagram
participant U as 用户
participant H as Host + 模型
participant C as MCP Client
participant S as GitHub MCP Server
participant G as GitHub API
U->>H: 查找本周未关闭的 issue
H->>C: initialize(协商版本与能力)
C->>S: tools/list
S-->>C: search_issues 的名称、说明、输入 schema
C-->>H: 可用工具清单
H->>H: 模型选择 search_issues
H->>C: tools/call(name, arguments)
C->>S: JSON-RPC 请求
S->>G: 调用 GitHub API
G-->>S: issue 列表
S-->>C: CallToolResult
C-->>H: 结果进入模型上下文
H-->>U: 汇总后的回答tools/list 让 Host 在运行时发现工具;tools/call 只携带工具名和参数,Server 再决定怎样访问 GitHub。模型不需要拿到 GitHub token,也不必理解 GitHub API 的响应结构。
消息长什么样
协议层只需要一组稳定的输入和输出。下面的 TypeScript 类型刻意只保留解释调用链所需的字段:
type Tool = {
name: string;
description?: string;
inputSchema: Record<string, unknown>;
};
type CallToolRequest = {
method: "tools/call";
params: {
name: string;
arguments?: Record<string, unknown>;
};
};
type CallToolResult = {
content: Array<{ type: "text"; text: string }>;
isError?: boolean;
};Tool.inputSchema 描述调用参数的形状;CallToolRequest 携带工具名和参数;CallToolResult.content 承载文本、图片或资源,isError 标记工具执行失败。
在线上传输时,这些字段会放进 JSON-RPC 2.0 的 request / response envelope。为了让调用关系容易阅读,下面省略了规范要求的请求元数据(例如版本、客户端信息和能力声明):
{
"jsonrpc": "2.0",
"id": 7,
"method": "tools/call",
"params": {
"name": "search_issues",
"arguments": {
"repository": "rhapsody-site",
"state": "open",
"created_after": "2026-08-24"
}
}
}Server 返回的 result 只需要是 MCP 规定的内容块。对文本工具来说,Host 可以把 content[0].text 放入下一次模型上下文。
对文件、图片或嵌套资源,Host 应使用对应的内容类型,而不是把所有东西拼成一段字符串。
stdio 和 Streamable HTTP 只是两条路
同一套 MCP 数据层可以放在不同传输上:
| 传输 | 连接方式 | 适合场景 | 需要额外处理 |
|---|---|---|---|
stdio | Host 启动本地 Server 子进程,JSON-RPC 走 stdin/stdout | 本地文件、开发工具、个人环境 | stdout 只能输出协议消息;日志写 stderr |
| Streamable HTTP | Client 通过一个 HTTP endpoint 与远程 Server 通信,可按需使用 SSE | 团队服务、云端数据、多个客户端共享 | 鉴权、Origin 校验、会话和断线恢复 |
换传输时,tools/list 和 tools/call 的语义不变;SDK 只负责握手、序列化和连接管理。
找一个可以拆开的 Server
要把消息和源码对应起来,先选一个能看到实现、能在本机启动的 Server。
我建议先用 MCP 官方的 Everything Server。
它是参考实现,不以生产部署为目标,却把 Tools、Resources、Prompts 等能力集中在一个小项目里。
它不需要 GitHub token、数据库账号或业务数据,直接用 npx 启动即可:
npx -y @modelcontextprotocol/server-everything阅读源码时,可以沿着这条线索走:
查看 Mermaid 源码
flowchart LR
srv[Server 源码] --> reg[注册能力]
reg --> discover[Client 发现能力]
discover --> invoke[Client 发起调用]
invoke --> handle[Server 处理函数]
handle --> output[返回执行结果]例如,Client 先列出工具,再调用一个回显工具。下面的代码使用官方 TypeScript SDK 的 v1 写法;SDK v2 已拆分为新的包名,实际项目应锁定版本并按对应文档调整导入路径:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
const transport = new StdioClientTransport({
command: "npx",
args: ["-y", "@modelcontextprotocol/server-everything"],
});
const client = new Client({ name: "learning-client", version: "0.1.0" });
await client.connect(transport);
const { tools } = await client.listTools();
console.log(tools.map((tool) => tool.name));
const result = await client.callTool({
name: "echo",
arguments: { message: "Hello MCP" },
});
console.log(result);
await client.close();这段 Client 没有导入 Everything 的内部模块,只依赖 MCP 的连接和消息。阅读 Server 源码时,可以把工具注册处对应到 listTools 的返回值,把处理函数对应到 callTool 的结果。
如果希望把练习拆成更小的台阶,官方仓库还有几个可以直接运行的 Server:
- Time(Python):只有时区工具。
- Fetch(Python):展示 URL 参数和分段读取。
- Filesystem(Node.js):展示目录 allow-list 与写入权限。
- Memory(Node.js):展示 Resource 和更新通知。
它们的源码、README 和启动命令都在同一个公开仓库中。
远程连接也有公开例子,但含义要分清。
- GitMCP 提供
https://gitmcp.io/{owner}/{repo},可匿名读取公开 GitHub 仓库。 - GitHub MCP Server 的托管地址
https://api.githubcopilot.com/mcp/虽然公开可访问,实际调用仍需 OAuth 或 PAT。
学习第一条连接路径时,使用本地 Everything 更容易定位问题。掌握 tools/list 和 tools/call 后,再把 transport 换成远程 HTTP,协议层的调用语义仍然不变。
MCP 不是函数调用,也不是 Agent 框架
这几个概念经常一起出现,但解决的层次不同:
| 概念 | 主要回答 | MCP 与它的关系 |
|---|---|---|
| 函数调用(Function Calling) | 模型如何产出一个函数名和参数 | MCP 的 Tool 可以被翻译成函数调用,但函数调用本身不规定 Server 如何发现、连接和返回结果 |
| Agent 框架 | 多步推理、状态、重试和人工介入如何编排 | Agent 可以把 MCP 当作工具来源;MCP 不负责规划循环和持久化 |
| REST API | 服务之间如何设计资源和 HTTP 接口 | MCP Server 内部可以调用 REST API,再把能力包装成 Tools 或 Resources |
| MCP | AI 应用如何发现并使用外部上下文和能力 | 它位于“应用与外部能力之间”的协议层 |
如果只有一个后端函数和一个调用方,直接使用函数调用或普通 HTTP 往往更简单。
Server 能做事,不代表 Host 应该放行
MCP 统一了连接方式,却没有替你完成授权。delete_file、create_ticket 和 send_email 都可能是合法的 Tool,但它们的风险完全不同。
一个可用的 Host 至少要把这几件事分开:
- 发现:Server 声明了哪些工具和参数;
- 授权:当前用户和当前会话是否允许调用;
- 确认:调用会产生外部副作用时,是否需要用户明确批准;
- 审计:记录谁在什么时间、以什么参数调用了哪个工具。
本地 stdio Server 需要限制子进程的文件和环境变量权限;远程 Streamable HTTP Server 需要认证、Origin 校验和会话管理。
不要因为工具出现在 tools/list 结果里,就把它当成可信操作。安全边界仍然属于 Host、Server 和业务系统的实现责任。6
什么时候值得用 MCP
可以先问三个问题:
- 这个能力是否会被两个以上的 AI 应用使用?
- 工具、资源或提示模板是否需要动态发现,而不是写死在一个 prompt 里?
- 是否愿意为权限确认、超时、审计和版本兼容维护一层协议边界?
三个问题大多回答“是”,MCP 值得评估;如果只是一个页面调用一个内部 API,直接使用现有接口通常更省事。
MCP 解决的是互操作性,不会自动解决数据质量、权限设计和工具本身的业务错误。
回到开头的 issue 查询:Host 创建 Client,Client 发现并调用 search_issues,Server 访问 GitHub,再把结果以统一内容块交回 Host。MCP 统一的正是这条连接关系。
参考资料
- MCP Architecture overview(官方文档,核对日期:2026-08-31)
- MCP Tools(官方规范,核对日期:2026-08-31)
- MCP Resources(官方规范,核对日期:2026-08-31)
- MCP Prompts(官方规范,核对日期:2026-08-31)
- MCP Transports(官方规范,核对日期:2026-08-31)
- JSON-RPC 2.0 Specification(规范文档,核对日期:2026-08-31)
Footnotes
-
MCP Architecture overview:MCP 聚焦 AI 应用与外部上下文之间的协议交换,不规定应用如何使用 LLM 或管理上下文。 ↩
-
MCP Transports:规范定义
stdio与 Streamable HTTP 等传输方式,传输承载的是同一数据层消息。 ↩ -
MCP Resources:Resources 通过 URI 暴露文件、schema 等上下文,设计为应用驱动。 ↩
-
MCP Prompts:Prompts 是 Server 提供、用户选择并可传参的结构化提示模板。 ↩
-
MCP Authorization 与各传输规范中的安全要求:认证、Origin 校验、权限范围和会话管理需要由实现负责。 ↩