MCP(Model Context Protocol)
MCP 是一套开放协议,用来规范“模型/Agent 如何连接外部能力”。可以把它理解成 AI 世界的 USB-C:以前每个工具、每个数据源都要为每个客户端单独写一遍对接代码(M 个模型 × N 个工具 = M×N 种适配),有了统一接口后,工具只要实现一次 MCP,就能被所有支持 MCP 的客户端使用(M+N)。
本质:MCP 不是模型,也不是某个具体工具,而是工具与模型之间的通信标准。它解决的是“接入方式碎片化”的问题,而不是“要不要用工具”的问题。
关联:工具调用(tool call)解决的是“模型能不能影响外部世界”,见
tool_calling.md。MCP 更进一步,解决“这些工具如何以统一、可复用的方式被接入”。
为什么需要 MCP
没有统一协议时的痛点:
- 每个 Agent 框架都有自己的一套工具定义方式,工具无法跨平台复用。
- 每接一个新数据源(数据库、文件系统、第三方 API),都要重写一遍胶水代码。
- 工具的发现、鉴权、调用格式各不相同,难以维护。
MCP 把这些约定标准化,工具作者写一次 MCP Server,就能被 Claude、Kiro、各类 IDE 和 Agent 直接调用。
核心架构
MCP 采用 Client-Server 架构:
- Host(宿主):用户实际使用的应用,例如 IDE、聊天客户端。它内部集成了模型。
- MCP Client(客户端):由 Host 启动,和某个 Server 建立一对一连接,负责收发协议消息。
- MCP Server(服务端):真正提供能力的一方,把具体功能按协议暴露出来。一个 Server 可以是本地进程,也可以是远程服务。
Host (含模型)
└── MCP Client ──── 协议 ──── MCP Server ──── 外部能力(DB / API / 文件系统 …)
Server 能提供的三类能力
- Tools(工具):可被模型调用的动作,例如查询数据库、发起请求。对应传统的 tool call。
- Resources(资源):可被读取的上下文数据,例如文件内容、数据库记录,通常由应用/用户控制加载。
- Prompts(提示模板):预置的、可复用的提示词模板,供用户主动选用。
简单记:Tools 是“能做的动作”,Resources 是“能读的数据”,Prompts 是“能套用的模板”。
传输方式(Transport)
- stdio:Server 作为本地子进程运行,通过标准输入输出通信。适合本地工具(如访问本地文件系统)。
- HTTP / SSE:Server 作为远程服务运行,通过网络通信。适合云端、多用户共享的服务。
与普通工具调用的关系
| 维度 | 普通 tool call | MCP |
|---|---|---|
| 定位 | 单个框架内定义并调用工具 | 跨框架、跨应用的统一接入协议 |
| 复用 | 工具绑定在具体代码里,难迁移 | 一次实现,多客户端复用 |
| 内容 | 通常只有“动作” | Tools + Resources + Prompts |
| 连接 | 进程内函数调用 | Client-Server,可本地可远程 |
MCP 底层依然依赖 tool call 那套“模型吐出符合 schema 的 JSON”的机制,见
tool_calling.md;MCP 只是把它标准化并加上了连接、发现、鉴权等约定。
使用场景
- 想让同一个工具在多个 Agent / IDE 里复用,而不是每个平台重写一遍。
- 需要把本地资源(文件、数据库)安全地暴露给模型。
- 生态化:使用社区已经写好的 MCP Server(如文档检索、浏览器操作),无需自己开发。
应用示例(具体场景)
光看协议容易抽象,下面用几个真实场景说明 MCP 解决了什么、怎么用。
场景总览:用 MCP 前 vs 用 MCP 后
| 场景 | 没有 MCP(各自为政) | 有 MCP(一次实现,处处可用) |
|---|---|---|
| 读本地笔记/代码 | 每个 Agent 各自写文件读写插件 | 一个 filesystem Server,所有客户端共用 |
| 查数据库 | 手写 SQL + 专属接口 | postgres Server 暴露查询工具,模型用自然语言查 |
| 操作网页 | 自己封装浏览器脚本 | playwright Server 直接让模型点击、抓取、截图 |
| 管 GitHub | 调 REST API 写胶水代码 | github Server 让模型建 issue、查 PR、读 diff |
| 检索文档/代码库 | 每个产品内置一套 RAG | 一个检索 Server,IDE 和聊天端都能接 |
示例 1:文件系统 MCP —— 把”我的笔记文件夹”变成模型的工具
痛点:你希望 Agent 能读你 ~/notes 下的 Markdown,但又不希望它碰全盘文件。
做法:装一个 filesystem MCP Server,只把 ~/notes 这一个目录授权给它。
配置(以常见的 mcpServers 配置为例):
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/ava/notes"]
}
}
}接上后,模型就能”读 ~/notes/foo.md""把总结写回 ~/notes/bar.md”,且越界访问会被 Server 拒绝。这就是把”外部能力”安全地标准化暴露。
示例 2:浏览器 MCP(Playwright)—— 让模型真正”打开网页”
这正好接上 ../web/headless-browser.md:无头浏览器的能力,通过一个 MCP Server 就变成了模型能直接调用的工具。
端到端流程:
你:"去 example.com 把首页标题和首图链接给我"
│
▼
Host(模型) 决定调用 playwright MCP 的 tool:navigate / screenshot / extract
│
▼
MCP Client ──协议──▶ Playwright MCP Server
│ 启动无头 Chromium
│ 打开页面、执行 JS、截图、取 DOM
▼
返回:标题="..."、图片url="..."
│
▼
模型把结果整理成你的回答
好处:模型不再只能”猜”网页内容,而是像人一样真实加载并渲染页面,拿到 JS 跑完后的结果。
示例 3:GitHub MCP —— 用自然语言管仓库
你:“帮我看看昨天提的那个 PR 有没有人 review。” 背后发生的事:
- 模型从 GitHub MCP 的 tool 清单里选
list_pull_requests(或get_review_comments)。 - MCP Client 把请求发给 GitHub MCP Server。
- Server 用你配置的 token 调 GitHub API,拿到 PR 列表和 review 状态。
- 结果回传模型,模型组织成:“你的 PR #123 已有 2 条 review 评论,其中 1 条要求改
login.ts……”
关键:你作为用户只在配置时授权一次 token,之后”查 PR / 建 issue / 读 diff”都是标准工具调用,换一个支持 MCP 的客户端不用重写。
示例 4:数据库 MCP —— 用大白话查数据
你:“上个月的活跃用户比这个月少多少?”
没有 MCP:你打开 DB 客户端,自己写 SELECT ... WHERE date BETWEEN ...,再心算差值。
有 MCP:postgres Server 暴露了查询工具,并可读 schema(Resources)。模型生成查询、Server 执行(带权限约束)、返回数字,模型算出差值并解释。
安全点:Server 可以限制只能跑只读查询、只能访问特定库,避免模型误执行
DROP。
这些例子共同的套路
无论文件、网页、GitHub 还是数据库,模式都一样:
用户用自然语言提需求
▼
模型挑一个 MCP Server 暴露的 tool
▼
MCP Client ↔ Server(协议通信)
▼
Server 调用真实外部能力(文件/API/浏览器/DB)
▼
结果回模型 → 组织成回答/动作
你作为使用者,关注点从”怎么写对接代码”变成了”选哪个 Server、授什么权”。
动手:写一个最小 MCP Server
光看例子不过瘾,下面用 Python 的官方 SDK(mcp)写一个能跑的最小 Server:暴露一个工具(加法)+ 一个资源(问候语),走 stdio 传输。
1. 安装
pip install "mcp[cli]" # 含 FastMCP 与命令行工具2. 写一个 demo_server.py
from mcp.server.fastmcp import FastMCP
# 创建一个 Server 实例,名字随便起
mcp = FastMCP("demo")
# —— Tools:模型能调用的"动作" ——
@mcp.tool()
def add(a: int, b: int) -> int:
"""把两个数相加,返回结果"""
return a + b
# —— Resources:模型能读取的"数据" ——
@mcp.resource("greeting://{name}")
def greeting(name: str) -> str:
"""按名字返回一句问候(name 由调用方传入)"""
return f"你好,{name}!"
if __name__ == "__main__":
mcp.run() # 默认以 stdio 方式运行(作为子进程被 Host 拉起)就这几行:用装饰器把普通函数标成 tool / resource,SDK 自动帮你生成协议所需的 schema、负责收发消息。你完全不用手写 JSON-RPC 细节。
3. 把它接进客户端
在客户端的 mcpServers 配置里加一项,指向这个脚本:
{
"mcpServers": {
"demo": {
"command": "python",
"args": ["/绝对路径/demo_server.py"]
}
}
}保存后客户端会自动拉起这个进程并通过标准输入输出通信,然后从 @mcp.tool() / @mcp.resource() 里发现你暴露的能力。
4. 之后怎么用
回到对话里(以本仓库的 Agent 为例):
- 你说”算一下 3 加 5” → 模型发现
demo的add工具 → 通过 MCP Client 调用 → Server 算出8→ 模型回答。 - 你说”用 demo 给我来句问候,我叫 ava” → 模型读
greeting://ava资源 → 拿到”你好,ava!“。
运行时发生了什么
Host 启动 → 拉起 python demo_server.py(子进程)
│
▼ 通过 stdin/stdout 走协议
Host 问:"你有哪些 tool / resource?" → Server 回:add、greeting://{name}
│
▼ 用户提需求时
模型决定调用 add(3,5) → Client 发包给 Server → Server 执行函数 → 回传 8
关键点:你写的只是”能力本身”(一个加法函数、一句问候),协议、发现、传输、鉴权约定全由 SDK 和客户端接管。换一个支持 MCP 的客户端,这个 Server 不用改一行就能复用——这正是 MCP 的价值。
备选:TypeScript / Node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({ name: "demo", version: "1.0.0" });
server.tool("add", { a: z.number(), b: z.number() },
async ({ a, b }) => ({ content: [{ type: "text", text: String(a + b) }] }));
const transport = new StdioServerTransport();
await server.connect(transport);思路一样,只是用 zod 显式声明参数 schema。
工具是怎么”export”给模型的(发现 + 翻译)
前面写过最小 Server,但”模型怎么知道有哪些 tool”这件事还没拆开。它分两步:发现(discovery) 与 翻译(translation)。
1. 发现:Server 自曝能力(MCP 协议内部)
Host 拉起 Server 后,MCP Client 会主动发 JSON-RPC 请求去”问”Server 有哪些能力;Server 把能力清单回传:
Client ── tools/list ──────────▶ Server
Client ◀── [{name, description, inputSchema}, ...] ── Server
Client ── resources/list / prompts/list ──▶ Server (同样的机制)
Server 回的每条 tool 定义,正是 schema.md 第 4 节说的 (B) 工具参数 schema(JSON Schema):
{
"name": "add",
"description": "把两个数相加",
"inputSchema": {
"type": "object",
"properties": { "a": {"type": "integer"}, "b": {"type": "integer"} },
"required": ["a", "b"]
}
}这一步对应「动手」第 4 步的”发现机制”,也是常见误区里”工具清单是运行时由 Server 连上后自报的”的底层实现——靠的就是
tools/list。
2. 翻译:Client 把 MCP 格式改成模型要的格式
关键点——模型从不直接读 MCP 协议。 MCP 是 Client↔Server 之间的事;模型只认自家 API 的 function-calling 格式(如 OpenAI 的 tools 字段)。所以 Client 拿到 tools/list 的结果后,要翻译成模型 API 的格式再注入请求:
// 发给模型 API 的(以 OpenAI 兼容格式为例)
{
"model": "gpt-4o",
"messages": [ /* … */ ],
"tools": [
{
"type": "function",
"function": {
"name": "add",
"description": "把两个数相加",
"parameters": { "type":"object", "properties":{/* … */}, "required":["a","b"] } // ← 直接从 inputSchema 搬来
}
}
]
}这一步就是”export 给模型”的本质:Client 把 Server 的 inputSchema 原样塞进模型请求里的 tools[].function.parameters。模型看到的是一份”函数签名清单”,不是 MCP 报文。底层依旧是函数调用那套机制,见 tool_calling.md。
3. 调用时再翻译回来
模型决定调 add(3,5) → 吐 tool_call JSON
▼
Client 按 schema 校验参数 → 翻译成 MCP 的 tools/call
▼
Server 执行真实函数 → 回 content
▼
Client 把结果包成 tool 消息 → 喂回模型 → 模型继续生成回答
一句话:MCP 通过
tools/list把工具的 name + description + JSON Schema 暴露给 Client;Client 把它翻译成模型 API 的 function-calling 格式注入每次请求。模型只知道”有哪些函数、参数长啥样”,至于背后是 stdio 子进程还是远程 HTTP、用没用 MCP——模型一概不知。这正好印证常见误区:模型训练时不知道这些工具,全是运行时动态发现的。
MCP vs 自己写方法:什么时候需要配置 MCP
同一个外部能力(如浏览器、数据库)通常有两种用法,判断是否要配 MCP,看”调用方是谁”:
- 直接写代码(用库):在你的程序里
import一个库(如playwright、requests、psycopg2),直接调函数。控制逻辑写在你的代码里,运行在你的进程里。 - 配 MCP:把能力暴露成一个 MCP Server,让 AI 模型在运行时发现并调用。
| 调用方 | 推荐方式 | 原因 |
|---|---|---|
| 你自己写的脚本 / 程序 | 直接写代码(用库) | 确定性强、可控、无额外进程与协议开销 |
| AI 模型 / Agent 要灵活调用 | 配 MCP | 模型运行时动态发现工具,跨客户端复用 |
| 多个 AI 客户端都要同一能力 | 配 MCP | 一次实现,处处可用(M+N) |
| 想让模型 / 非开发者自助选工具 | 配 MCP | 标准化发现机制 |
以 Playwright 为例(两种用法不冲突)
- 自己写方法(你之前的做法):
pip install playwright→ 脚本里from playwright.sync_api import sync_playwright→ 你当调用方,控制浏览器。不需要 MCP。 - 配 MCP:起一个 Playwright MCP Server,把浏览器变成模型的工具,让 IDE 里的 AI 助手自己去开网页、点击、抓渲染后内容(见上文「应用示例 2」)。
一句话:MCP 是”给 AI 模型用的 USB-C 接口”;你自己写代码调库走的是普通
import,两者不互相替代。你项目从没配过 MCP,通常是因为调用方一直是”你写的代码”,而不是”模型”。
常见误区
-
❌ “模型在训练时就知道有哪些流行的 MCP Server。” ✅ 模型训练时最多从网上文档学到”MCP 协议是什么”,但不知道你项目接了哪些 Server、暴露什么工具。工具清单是运行时由 Server 连上后”自报”的(见上文「动手」第 4 步的发现机制)。换个没见过的 Server,模型第一次也能用,因为它读的是实时返回的 schema,不是记忆里的名单。
-
❌ “在 IDE 里『配置 MCP』是给模型上课,让它记住这些工具。” ✅ 配置只是一份静态接线清单(命令、参数、环境变量、授权 token、作用目录),告诉 IDE 启动时去拉起哪些 Server、怎么连。模型本身不”记住”任何工具;每次会话开始,IDE 按配置拉起 Server,模型再经协议动态发现可用能力。配置是”接线”,不是”训练”。
-
❌ “MCP 让模型变聪明 / 学会新领域知识。” ✅ MCP 只负责”接上外部能力”,不往模型里塞知识。模型能不能用好一个工具,取决于它自身的推理与函数调用能力;MCP 解决的是”能不能方便地调到”这个问题。
-
❌ “MCP 就是应用商城里的应用。” ✅ MCP 是协议/标准(类比 USB-C 规范),不是应用;“应用”对应的是 Skill,ClawHub 才是应用商店。见
skills.md。
延伸阅读
- tool_calling — MCP 底层仍依赖 tool call;见
tool_calling.md - skills / slash_commands — 另一种给 Agent 加能力的方式,与 MCP 互补;见
skills.md、slash_commands.md - 无头浏览器 — 浏览器类 MCP 的运行基础;见
../web/headless-browser.md - Figma MCP — 把 Figma 接成 AI 工具源,用自然语言把想法画进 Figma;见
../产品prd/figma/figma-mcp.md - 官方生态 — modelcontextprotocol.io 的 Server 列表,可直接复用社区实现