MCP 全面解析 —— 类型、原理与使用方式
你在聊天框里敲下:”帮我看看本地 ~/notes 目录下有哪些文件,整理成清单发给我。”
AI 不仅读懂了你的意图,还真的列出了文件、读取了内容、给出了整理结果——它明明只是一个没有操作系统的模型,凭什么能访问你的文件系统?
答案就是 MCP(Model Context Protocol,模型上下文协议)。
2024 年 11 月,Anthropic 开源了 MCP,把它称作”AI 应用的 USB-C 接口”:一个开放标准,让大模型应用能以统一方式连接任意数据源与工具。两年过去,MCP 已经从一个”概念”长成了事实上的行业标准——OpenAI、Google、Microsoft、阿里等主流厂商全部宣布支持,GitHub 官方 MCP 服务器下载量破百万,规范也迭代到了 2026-07-28 版本。
这篇文章会用与之前 Dubbo Triple、ZooKeeper 系列相同的深度剖析风格,回答三个问题:
- 类型:MCP 到底有哪几类?角色、原语、传输、形态分别是什么?
- 原理:MCP 底层是怎么工作的?JSON-RPC 消息如何流转,生命周期如何演进?
- 使用方式:从配置客户端到写一个自己的 MCP Server,完整动手实践。
一、为什么需要 MCP?
1.1 大模型的”能力断层”
大语言模型本质上是一个函数:输入 token,输出 token。它没有任何”副作用”——不能读文件、不能查数据库、不能发 HTTP 请求、不能执行命令。
但真实世界的任务几乎都需要副作用:
| 用户意图 | 需要的副作用 |
|---|---|
| “帮我看看这个项目的 README” | 读文件 |
| “统计一下这个月的订单量” | 查数据库 |
| “把这个 issue 指派给张三” | 调用 GitHub API |
于是业界催生了各种”工具调用”方案:OpenAI 的 Function Calling、Anthropic 的 Tool Use、各家 Agent 框架自己定义的插件体系。它们本质相同:把”外部能力”描述成一个个函数(工具),让模型在推理时决定调用哪个、传什么参数。
1.2 N×M 集成困境
问题在于:每个模型厂商的调用协议是私有的,每个工具也要单独适配。
工具域 模型域
┌──────────────┐
│ MySQL 数据库 │
└──────┬───────┘ ┌───────────┐
┌──────┴───────┐ │ LLM-A │
│ 文件系统 ├────────┤ (OpenAI) │
└──────┬───────┘ └─────┬─────┘
┌──────┴───────┐ ┌─────┴─────┐
│ GitHub API │ │ LLM-B │
└──────┬───────┘ │ (Anthropic)│
┌──────┴───────┐ └─────┬─────┘
│ 浏览器 │ ┌─────┴─────┐
└──────────────┘ │ LLM-C │
│ (国产大模型) │
└───────────┘
N 个工具 × M 个模型 = N×M 个定制适配器,全部要手工维护
每个工具都要为每个模型写一套适配代码,集成成本随规模平方级增长,而且数据与工具被锁定在特定模型生态里——换个模型,全部重来。
1.3 已有方案的局限
在 MCP 出现之前,”让 AI 用工具”有几种主流做法,各有硬伤:
| 方案 | 代表 | 局限 |
|---|---|---|
| Function Calling | OpenAI | 私有协议,只服务自家模型,工具描述格式与调用链绑定 SDK |
| 插件系统 | ChatGPT Plugins、Copilot Extensions | 平台绑定,插件生态由平台方控制,接入方话语权低 |
| 自研 Agent 框架 | LangChain 等 | 框架层解决编排,但工具接入仍是每个框架一套 DSL,框架之间不互通 |
| 代码直连 | 直接调 API | 每次需求变化都要改代码,模型无法动态发现能力 |
核心痛点是一致的:缺少一个中立、开放、双向的”工具连接层”标准。
1.4 MCP 是什么
Model Context Protocol 是一个开放协议,定义了 AI 应用(Host)如何通过标准化的方式连接外部数据源和工具(Server),从而为模型提供上下文(Context)。
三个关键词拆开看:
| 关键词 | 含义 |
|---|---|
| Model | 服务对象是模型——让模型获得它缺失的”上下文”(文件内容、数据库记录、工具能力) |
| Context | MCP 连接的不仅是”工具”,还有数据——工具是动作,资源是数据,提示词是模板,三者共同构成模型的上下文 |
| Protocol | 它是一份协议规范,不是 SDK 也不是平台;任何人可实现自己的客户端与服务器,只要遵守规范就能互通 |
关键里程碑:
| 时间 | 事件 |
|---|---|
| 2024-11-25 | Anthropic 开源 MCP,MIT 协议,首版规范 2024-11-05 |
| 2025-03 | OpenAI 宣布支持 MCP;Anthropic 发布 Java/Kotlin SDK |
| 2025-06 | 规范 2025-06-18:统一 Streamable HTTP 传输、引入 OAuth 2.1;MCP Registry 上线 |
| 2025-11 | 规范 2025-11-25:OIDC 发现、任务(Tasks)实验特性、JSON Schema 2020-12 |
| 2026-07 | 规范 2026-07-28:协议无状态化、Multi Round-Trip Requests、废弃 Roots/Sampling/Logging |
MCP 的设计目标可以总结为四句话:
- 一个协议,处处可用:工具与数据源只需实现一次 MCP,任何 MCP 客户端都能接入。
- 双向通信:不只是客户端调服务端(工具调用),服务端也能向客户端要数据(如请求用户补充输入)。
- 能力自描述:服务器主动声明自己有哪些工具/资源/提示词,客户端动态发现,无需硬编码。
- 安全可审计:所有外部访问都经过协议边界,配合用户确认与权限模型,可观测、可控。
二、MCP 的类型
“类型”这个词在 MCP 语境下有四层含义,很多人混淆。我们一层层拆。
2.1 角色类型:Host / Client / Server
MCP 的架构里只有三种角色:
┌──────────────────────────────────────────────┐
│ Host(宿主) │
│ Claude Desktop / IDE / Agent 应用 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Client 1 │ │ Client 2 │ │ Client 3 │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
└────────┼──────────────┼──────────────┼────────┘
│ │ │
┌─────┴─────┐ ┌─────┴─────┐ ┌─────┴─────┐
│ Server A │ │ Server B │ │ Server C │
│ 文件系统 │ │ MySQL │ │ GitHub │
└───────────┘ └───────────┘ └───────────┘
| 角色 | 职责 | 关键点 |
|---|---|---|
| Host | 用户直接面对的应用(Claude Desktop、Cursor、自研 Agent 等) | 一个 Host 可以连接多个 Server;Host 决定”何时调用哪个工具”以及是否征求用户确认 |
| Client | Host 与某个 Server 之间的一对一协议连接,负责建立会话、收发 JSON-RPC 消息 | 注意:这里的 Client 是协议角色,不是”应用”。一个 Host 内为每个 Server 各维护一个 Client |
| Server | 暴露工具/资源/提示词的一方,连接真实世界(文件系统、数据库、第三方 API) | 一个 Server 可以同时被多个 Client 连接(远程场景);Server 不关心”哪个模型”在用自己 |
一个常见的误解:把 MCP Server 当成”插件”。更准确的说法是:MCP Server 是一个独立的进程/服务,Host 通过 MCP 客户端与之对话。模型本身并不直接连接 Server——中间隔着 Host 的编排逻辑(包括权限判断、用户确认)。
2.2 原语类型:Tools / Resources / Prompts
这是 MCP 最核心的类型划分。服务器通过三种原语(Primitive)向模型暴露能力,而模型通过它们获得”上下文”:
┌────────────────────────── MCP Server ──────────────────────────┐
│ │
│ Tools(工具) Resources(资源) Prompts(提示词) │
│ "我会做什么" "我知道什么" "我该怎么问" │
│ · 可执行的函数 · 只读数据 · 可复用的模板 │
│ · 有副作用 · 无副作用 · 引导交互方式 │
│ · 由模型主动调用 · 由模型/用户按需读取 · 由用户/模型触发 │
│ │
└────────────────────────────────────────────────────────────────┘
三者对比如下:
| 维度 | Tools | Resources | Prompts |
|---|---|---|---|
| 本质 | 动作(副作用) | 数据(只读) | 交互模板 |
| 典型例子 | 发送邮件、执行 SQL、创建 issue | 数据库表内容、项目文件、文档 | “代码评审”模板、”周报生成”模板 |
| 谁触发 | 模型自主决定调用(经用户确认) | 模型发现后按需读取;用户也可直接指定 | 用户或模型选用模板,填入参数 |
| 协议方法 | tools/list、tools/call |
resources/list、resources/read、resources/templates/list |
prompts/list、prompts/get |
| 可变性 | 是(会改变世界状态) | 否(纯读取) | 否(只是拼字符串) |
| 适合场景 | 一切”做事”的需求 | 一切”给模型喂上下文”的需求 | 一切”规范交互方式”的需求 |
Tools(工具)——最常用、最容易被理解的原语。服务器注册一组带 JSON Schema 入参声明的函数,模型在推理时选择调用。协议层只关心两件事:tools/list 拿到工具清单(名字、描述、参数 schema),tools/call 提交参数并拿回结果。工具可以声明自己的属性(注解):是否幂等(idempotentHint)、是否只读(readOnlyHint)、是否破坏性(destructiveHint)——这些会直接影响客户端的安全策略。
Resources(资源)——把”数据”暴露给模型的原语。文件内容、数据库查询结果、API 响应都可以包装成资源。资源用 URI 标识,支持资源模板(URI Template,如 file:///{path} 匹配任意文件),配合 MIME 类型声明格式。模型看到资源引用后,可以调用 resources/read 拉取内容。2026-07-28 版本引入了资源订阅(通过 subscriptions/listen),服务器可以在资源变化时主动推送变更通知。
Prompts(提示词)——服务器预定义的”提示词模板”。例如一个”代码评审”提示词:用户触发后,服务器用 prompts/get 返回一组带角色的消息(system + user),再交给模型执行。Prompts 是三者中唯一”不直接与工具交互”的原语,它本质上是协作规范:让第三方服务器能引导 Host 以特定方式与用户对话。
2026-07-28 之前还有两个原语级能力:Roots(客户端向服务器声明”我可访问哪些目录”)和 Sampling(服务器请求客户端调用模型生成文本)。它们在新版规范中已被废弃——前者建议改用工具参数/资源 URI 显式传路径,后者建议服务器直接集成 LLM 供应商 API。新实现不应再依赖它们。
2.3 传输类型:stdio 与 Streamable HTTP
MCP 定义了两种传输(Transport)方式,解决”本地进程”与”远程服务”两类连接需求:
| 维度 | stdio | Streamable HTTP |
|---|---|---|
| 连接形态 | 客户端拉起服务器子进程,通过 stdin/stdout 通信 | 客户端通过 HTTP POST 调用远程 URL |
| 消息格式 | 换行分隔的 JSON-RPC(每行一条消息) | JSON-RPC 封装在 HTTP 请求/响应中;长连接用响应流 |
| 部署位置 | 本地(开发机、桌面端) | 远程(云端、内网服务、SaaS) |
| 鉴权 | 无(进程边界即信任边界) | OAuth 2.1 等标准授权 |
| 适用场景 | 本地开发工具、桌面应用、安全敏感场景 | 多租户 SaaS、集中式工具网关、团队共享 |
| 日志/调试 | 日志走 stderr(不能污染 stdout 协议流) | 标准 HTTP 日志、OpenTelemetry |
stdio 是 MCP 最初的传输,也是”零配置安全”的典范:客户端直接 spawn 服务器进程,通信不经过网络,权限由操作系统进程隔离天然保证。代价是服务器必须随客户端一起部署(或至少能本地启动)。
Streamable HTTP 是 2025-06-18 版本引入的统一远程传输,取代了早期版本里”HTTP + SSE 双端点”的割裂设计。它允许服务器用响应流下发长任务进度、日志等通知,同时支持请求-响应语义。2026-07-28 进一步演进:移除了会话头 Mcp-Session-Id,服务端主动通知改为通过 subscriptions/listen 订阅流,让协议在 HTTP 层做到无状态、可水平扩展。
2.4 服务器形态类型
按部署与组织方式,MCP Server 还可以分成几类形态:
| 形态 | 说明 | 例子 |
|---|---|---|
| 本地进程服务器 | 随客户端启动的独立进程(stdio) | 文件系统、Git、SQLite、浏览器控制 |
| 远程托管服务器 | 部署在云端的 HTTP 服务,多用户共享 | GitHub 官方 MCP、Sentry、Cloudflare |
| 单工具服务器 | 一个服务器只暴露一个工具,职责单一 | fetch 抓取、时间查询 |
| 聚合网关 | 把多个内部工具统一包装成一个 MCP 端点 | 企业内部”工具中台”、LangGraph/Spring AI 的 MCP 网关 |
| 参考服务器 | 官方/社区维护的开源示例实现 | modelcontextprotocol/servers 仓库 |
形态选择没有对错:本地工具用 stdio 最简单安全;团队共享、需要集中治理的工具做成远程 HTTP 服务器;企业内部多个系统则适合先做聚合网关,再统一暴露。
三、MCP 的原理
这一章我们打开协议的黑盒,看消息是怎么流动的。
3.1 协议基础:JSON-RPC 2.0
MCP 的消息层基于 JSON-RPC 2.0——一个极简的远程调用规范,只有三种消息:
请求(Request): 有 id,期待响应
响应(Response): 与请求 id 对应,携带 result 或 error
通知(Notification):无 id,单向发出,不需要响应
一条完整的 tools/call 请求长这样:
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "query_issues",
"arguments": {
"repo": "mingjunduan/mingjunduan.github.io",
"query": "MCP"
}
}
}
对应的响应:
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{ "type": "text", "text": "共找到 2 个相关 issue:\n#12 MCP 介绍……" }
],
"isError": false
}
}
MCP 在此之上定义了两类扩展:
- 方法命名空间:
tools/*、resources/*、prompts/*、initialize、ping等,按前缀分组。 - 元数据约定:
params._meta携带协议版本、客户端/服务器标识等协议级信息(2026-07-28 起成为版本协商的主通道)。
错误码也遵循 JSON-RPC 规范并做了 MCP 扩展:
| 错误码 | 含义 |
|---|---|
-32700 |
解析错误(非法 JSON) |
-32600 |
无效请求 |
-32601 |
方法不存在 |
-32602 |
参数无效(2026-07-28 起,资源不存在也从 -32002 归入此码) |
-32603 |
内部错误 |
-32020 ~ -32099 |
MCP 规范保留区(-32020 HeaderMismatch、-32021 缺少必需能力、-32022 协议版本不匹配等) |
3.2 生命周期:从”握手”到”无状态”
MCP 的生命周期设计经历了一次重大演进,理解两个阶段才能真正看懂现状。
经典模型(2024-11-05 ~ 2025-11-25):显式握手 + 会话状态
Client Server
│ │
│── initialize(协议版本 + 客户端能力 + 标识)─────→│
│←─ result(协商后的协议版本 + 服务端能力 + 标识)───│
│── notifications/initialized ────────────────→│
│ │
│ ……运行期:工具/资源/提示词交互…… │
│ │
│── 会话结束,关闭连接 ───────────────────────────→│
经典模型三步走:
initialize握手:客户端声明它支持的协议版本、自身能力(如roots、sampling)和标识;服务器回复双方协商一致的版本、自身能力(如tools、resources、prompts、logging)和标识。notifications/initialized:客户端通知服务器初始化完成,可以开始正常交互。- 运行期:双方按能力清单进行工具/资源/提示词交互,期间服务器可通过
notifications/message、notifications/progress推送日志与进度。
这个模型直观,但有明显的分布式系统缺陷:有状态。服务器需要为每个连接维护会话(Session ID、能力上下文),无法水平扩展,也不利于无状态网关与负载均衡。
无状态模型(2026-07-28 起):每请求自带版本与能力
2026-07-28 规范做出了 MCP 历史上最激进的一次重构——移除握手,协议无状态化:
Client Server
│ │
│── server/discover(可选,版本探测)─────────────→│
│←─ 支持的协议版本、能力、身份 ────────────────────│
│ │
│── tools/list(_meta 携带版本+客户端能力)───────→│
│←─ result(_meta 携带服务器信息)────────────────│
│── tools/call(每个请求都带版本)────────────────→│
│←─ result ────────────────────────────────────│
新模型的关键变化:
| 变化点 | 说明 |
|---|---|
没有 initialize 握手 |
每个请求的 _meta 携带 io.modelcontextprotocol/protocolVersion(协议版本)和 io.modelcontextprotocol/clientCapabilities(客户端能力),服务器按请求逐条处理 |
新增 server/discover |
服务器必须实现该 RPC,声明自己支持的版本范围与能力,供客户端在业务请求前做版本选择或向后兼容探测 |
| 移除会话概念 | HTTP 层不再有 Mcp-Session-Id 头;tools/list 等列表接口不再按连接区分 |
| 跨请求状态显式化 | 需要状态时使用服务器签发的”句柄”(handle),作为普通工具参数传递 |
| 版本不匹配 | 返回 -32022 UnsupportedProtocolVersion |
这个演进与 RPC 领域的大趋势完全一致(从有状态会话到无状态幂等接口):牺牲一点点握手便利,换取水平扩展性、网关兼容性和多云可移植性。对新读者,直接按无状态模型理解即可;若你接触的 SDK 还保留 initialize() 调用,那是为兼容旧协议保留的,新版本会逐步迁移。
3.3 通信模式:请求-响应、通知与 MRTR
MCP 的通信模式比普通 RPC 更丰富,2026-07-28 规范将其归纳为几种模式:
| 通信模式 | 方向 | 说明 |
|---|---|---|
| 请求-响应(Request-Response) | 客户端 → 服务器 | 客户端发请求、服务器回结果;工具调用、资源读取、列表查询都属于此类 |
| 服务端通知(Server Notifications) | 服务器 → 客户端 | 分两类:请求作用域通知(notifications/progress、notifications/message 跟随原请求的响应流下发);订阅通知(客户端通过 subscriptions/listen 订阅 toolsListChanged、resourcesListChanged 等变更事件) |
| 多轮请求(Multi Round-Trip Requests,MRTR) | 客户端驱动的多轮往返 | 服务器返回 input_required 中间结果,客户端补齐 inputResponses 后重试原请求;2026-07-28 引入 |
Client Server
│── tools/call(需要额外信息)──────────────────→│
│←─ result: InputRequiredResult │
│ { resultType: "input_required", │
│ inputRequests: [ "请提供用户确认密码" ] } │
│── tools/call(重试原请求,附上 inputResponses)→│
│←─ result: 最终结果 │
过去,服务器需要”额外输入”(比如工具执行到一半需要用户确认、需要更多参数)时,会反过来向客户端发起 sampling/elicitation 等反向请求——这在无状态模型里变得困难。MRTR 把”服务器要东西”统一改为:服务器返回 resultType: "input_required" 的中间结果,客户端补齐 inputResponses 后重试原请求。所有结果都带 resultType 字段("complete" 或 "input_required"),协议语义因此完全收敛为”客户端驱动的多轮往返”。
3.4 原语的协议级交互流程
以一次完整的工具调用为例,看看消息如何流动:
Host(应用) MCP Client MCP Server 真实世界
│ │ │ │
│ 1. 用户提问:帮我统计 │ │ │
│ 仓库的 star 数 │ │ │
│ │ │ │
│ 2. 模型推理:需要调工具 │ │ │
│ tools/list 发现能力 │ │ │
│─────────────────────────→│── tools/list ───────→│ │
│ │←── 工具清单 ──────────│ │
│ 3. 模型选择 query_repo │ │ │
│─────────────────────────→│── tools/call ───────→│ │
│ │ │── 调用 GitHub API ─→│
│ │ │←── 返回数据 ─────────│
│ │←── 工具结果 ──────────│ │
│ 4. 用户确认并展示结果 │ │ │
│←─────────────────────────│ │ │
对应到消息序列(经典协议表示,新版差异仅在元数据携带方式):
tools/list(无参数)→ 返回工具数组,每个工具含name、description、inputSchema(JSON Schema);- 模型依据描述与 schema 生成参数;
tools/call(name + arguments)→ 服务器执行 → 返回content(文本/图片/结构化内容数组)与isError标志;- Host 决定是否展示给用户、是否把结果回灌给模型继续推理。
资源读取与提示词获取的流程同理,只是方法不同:
资源:resources/list → resources/templates/list → resources/read(URI)→ 内容
提示词:prompts/list → prompts/get(name + arguments)→ messages[](含 role 的消息序列)
3.5 版本演进史
把规范版本串起来,能看到 MCP 两年的演进主线:
| 版本 | 核心变化 |
|---|---|
| 2024-11-05 | 首个公开版本:定义三大原语、stdio 与 HTTP+SSE 双传输、initialize 握手 |
| 2025-03-26 | 能力扩展:上下文管理(context management)、根目录 Roots、资源订阅、采样 Sampling 细化 |
| 2025-06-18 | 传输统一:Streamable HTTP 取代 HTTP+SSE;引入 OAuth 2.1 远程鉴权;Elicitation(服务器请求用户输入) |
| 2025-11-25 | 治理与增强:OIDC 发现、工具/资源图标元数据、增量 scope 授权、URL 模式 Elicitation、实验性 Tasks、JSON Schema 2020-12 默认方言、正式化治理结构 |
| 2026-07-28(当前) | 无状态化革命:移除握手与会话、server/discover、MRTR、subscriptions/listen、结果缓存提示(ttlMs/cacheScope);废弃 Roots/Sampling/Logging;Tasks 移入官方扩展 |
两条主线贯穿始终:
| 演进主线 | 具体表现 |
|---|---|
| 走向无状态与可扩展 | 从”每连接一个会话”到”每请求自描述”,让 MCP 服务器能像普通 HTTP 服务一样水平扩展 |
| 走向开放与安全 | 传输统一、OAuth 2.1、OIDC 发现、治理结构正式化——从”Anthropic 家的协议”变成”行业共同治理的标准” |
3.6 安全模型
MCP 把”AI 访问外部世界”这件事从不可控变成了可控,靠的是多层安全设计:
| 安全层 | 核心机制 |
|---|---|
| 用户确认(Human-in-the-loop) | Host 是安全决策中心:模型调用工具时先征求用户同意,尤其是破坏性工具(删除、写入、转账);工具注解(destructiveHint、readOnlyHint)是 Host 做决策的信号 |
| 权限最小化 | stdio 场景服务器进程权限 = 启动用户权限;远程场景只暴露被授权的工具集合;2026-07-28 支持增量 scope 授权,只授予当前任务所需权限 |
| 标准鉴权(远程) | Streamable HTTP 服务器必须支持 OAuth 2.1;客户端通过 OIDC 发现(2025-11-25 起)定位授权服务器、动态注册客户端,用 iss 校验防钓鱼(RFC 9207);凭证绑定签发方,禁止跨授权服务器复用 |
| 数据面与控制面分离 | _meta 等控制信息与业务数据分离;日志走 stderr 或 OpenTelemetry,避免敏感日志混入协议流 |
| 风险提示 | MCP 不阻止服务器在 tools/call 里做任何事——信任边界在 Host;恶意 prompt 注入下若 Host 不做确认风险依然存在,因此工具设计要窄、默认拒绝、一切可审计 |
3.7 性能与缓存
无状态化之后,MCP 在性能上也更友好。2026-07-28 引入 CacheableResult:tools/list、resources/list、resources/read 等只读接口的响应携带 ttlMs(缓存有效期,毫秒)与 cacheScope("public" 可被共享代理缓存 / "private" 仅客户端缓存)两个字段,客户端可以据此缓存结果、减少轮询。同时规范要求服务器确定性排序返回工具列表,以提升 LLM 提示词缓存(prompt cache)的命中率——同一个列表每次顺序一致,模型拿到的上下文前缀才能复用 KV 缓存。
四、MCP 的使用方式
理论讲完,动手。这一章从”用户”到”开发者”两条路径完整走一遍。
4.1 作为用户:在客户端接入 MCP Server
以 Claude Desktop 为例(其他客户端如 Cursor、VS Code、Cherry Studio 大同小异),接入一个 MCP Server 只需编辑配置文件:
macOS:~/Library/Application Support/Claude/claude_desktop_config.json
Windows:%APPDATA%\Claude\claude_desktop_config.json
Linux:~/.config/Claude/claude_desktop_config.json
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/me/workspace"
]
},
"calculator": {
"command": "java",
"args": ["-jar", "/Users/me/mcp-servers/calculator/target/calculator-server-1.0.0.jar"]
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"]
}
}
}
保存后重启客户端,左侧会出现新增的连接图标,点开后能看到该服务器暴露的工具列表——然后你就可以直接对模型说”帮我列出 workspace 目录下的文件”。
远程服务器的配置方式略有不同(以支持 HTTP 的客户端为例):
{
"mcpServers": {
"company-tools": {
"url": "https://mcp.example.com/mcp",
"headers": { "Authorization": "Bearer <token>" }
}
}
}
4.2 作为开发者:十分钟写一个 MCP Server(Java 17)
官方 MCP Java SDK(io.modelcontextprotocol,要求 JDK 17+,已演进到 2.x)提供了完整的服务端与客户端实现。
第一步:加依赖(Maven)
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>mcp-bom</artifactId>
<version>2.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- mcp = mcp-core + Jackson 3 JSON,开箱即用(默认含 stdio / Streamable HTTP 传输) -->
<dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>mcp</artifactId>
</dependency>
</dependencies>
第二步:写服务器。工具/资源/提示词各是一个”定义 + 处理器”的规格(Specification)对象,注册到 McpSyncServer:
// CalculatorServer.java
import java.util.List;
import java.util.Map;
import io.modelcontextprotocol.json.McpJsonDefaults;
import io.modelcontextprotocol.server.McpServer;
import io.modelcontextprotocol.server.McpServerFeatures.SyncPromptSpecification;
import io.modelcontextprotocol.server.McpServerFeatures.SyncResourceSpecification;
import io.modelcontextprotocol.server.McpServerFeatures.SyncToolSpecification;
import io.modelcontextprotocol.server.McpSyncServer;
import io.modelcontextprotocol.server.transport.StdioServerTransportProvider;
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
import io.modelcontextprotocol.spec.McpSchema.GetPromptResult;
import io.modelcontextprotocol.spec.McpSchema.Prompt;
import io.modelcontextprotocol.spec.McpSchema.PromptArgument;
import io.modelcontextprotocol.spec.McpSchema.PromptMessage;
import io.modelcontextprotocol.spec.McpSchema.ReadResourceResult;
import io.modelcontextprotocol.spec.McpSchema.Resource;
import io.modelcontextprotocol.spec.McpSchema.Role;
import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities;
import io.modelcontextprotocol.spec.McpSchema.TextContent;
import io.modelcontextprotocol.spec.McpSchema.TextResourceContents;
import io.modelcontextprotocol.spec.McpSchema.Tool;
public class CalculatorServer {
public static void main(String[] args) {
// 1. 选择 stdio 传输:本地进程,由客户端以子进程方式拉起
var transport = new StdioServerTransportProvider(McpJsonDefaults.getMapper());
// 2. 构建服务器:声明标识与能力(tools / resources / prompts)
McpSyncServer server = McpServer.sync(transport)
.serverInfo("calculator-demo", "1.0.0")
.capabilities(ServerCapabilities.builder()
.tools(true) // 工具能力
.resources(false, false) // 资源能力(subscribe=false, listChanged=false)
.prompts(true) // 提示词能力
.build())
.build();
// 3. 注册工具 / 资源 / 提示词
server.addTool(addTool());
server.addTool(fibTool());
server.addResource(noteResource());
server.addPrompt(codeReviewPrompt());
// 4. 优雅关闭;stdio 传输的非守护线程会保持进程存活,等待客户端调用
Runtime.getRuntime().addShutdownHook(new Thread(server::closeGracefully));
}
// ---- Tool:加法(JSON Schema 声明入参 + 处理器)----
static SyncToolSpecification addTool() {
Map<String, Object> schema = Map.of(
"type", "object",
"properties", Map.of(
"a", Map.of("type", "integer"),
"b", Map.of("type", "integer")),
"required", List.of("a", "b"));
return SyncToolSpecification.builder()
.tool(Tool.builder("add", schema)
.description("求两个整数之和")
.build())
.callHandler((exchange, request) -> {
int a = ((Number) request.arguments().get("a")).intValue();
int b = ((Number) request.arguments().get("b")).intValue();
return CallToolResult.builder()
.content(List.of(new TextContent(String.valueOf(a + b))))
.isError(false)
.build();
})
.build();
}
// ---- Tool:斐波那契 ----
static SyncToolSpecification fibTool() {
Map<String, Object> schema = Map.of(
"type", "object",
"properties", Map.of("n", Map.of("type", "integer")),
"required", List.of("n"));
return SyncToolSpecification.builder()
.tool(Tool.builder("fib", schema)
.description("计算斐波那契数列的第 n 项(n >= 0)")
.build())
.callHandler((exchange, request) -> {
int n = ((Number) request.arguments().get("n")).intValue();
if (n < 0) {
return CallToolResult.builder()
.content(List.of(new TextContent("n 必须 >= 0")))
.isError(true)
.build();
}
int a = 0, b = 1;
for (int i = 0; i < n; i++) {
int next = a + b;
a = b;
b = next;
}
return CallToolResult.builder()
.content(List.of(new TextContent(String.valueOf(a))))
.isError(false)
.build();
})
.build();
}
// ---- Resource:只读数据,用 URI 标识 ----
static SyncResourceSpecification noteResource() {
Resource resource = Resource.builder("notes://todo", "Todo 便签")
.description("按名字读取便签内容")
.mimeType("text/plain")
.build();
return new SyncResourceSpecification(resource, (exchange, request) -> {
String content = switch (request.uri()) {
case "notes://todo" -> "1. 写 MCP 博客\n2. 本地构建验证";
case "notes://idea" -> "下一篇写 Agent 编排";
default -> "未找到该便签";
};
return ReadResourceResult.builder(List.of(
TextResourceContents.builder(request.uri(), content)
.mimeType("text/plain")
.build()))
.build();
});
}
// ---- Prompt:可复用提示词模板 ----
static SyncPromptSpecification codeReviewPrompt() {
Prompt prompt = Prompt.builder("code_review")
.description("生成一段代码评审提示词")
.arguments(List.of(
PromptArgument.builder("language")
.description("编程语言")
.required(false)
.build()))
.build();
return new SyncPromptSpecification(prompt, (exchange, request) -> {
Object lang = request.arguments() == null ? null : request.arguments().get("language");
String language = lang == null ? "Java" : lang.toString();
String text = "请以资深 " + language + " 工程师的视角,从正确性、性能、可读性三个维度审查以下代码:";
return GetPromptResult.builder(List.of(
PromptMessage.builder(Role.USER, new TextContent(text)).build()))
.description("代码评审提示词")
.build();
});
}
}
注意:与 Python 的类型推导不同,Java SDK 中工具入参 Schema 是显式声明的 JSON Schema(Map 形式)——更精确但更啰嗦。SDK 2.x 内置入参校验,参数不符合 Schema 时直接返回 isError 的工具结果,不会进入你的处理器。
第三步:启动
# 打包后以 stdio 模式运行:客户端会以子进程方式拉起本进程
mvn -q package
java -jar target/calculator-server-1.0.0.jar
远程(Streamable HTTP)模式:把传输换成 HttpServletStreamableServerTransportProvider,注册进任意 Servlet 容器(Spring Boot / Jetty 等)即可暴露 /mcp 端点,鉴权走 OAuth 2.1。
验证:HTTP 模式启动后,直接 curl 就能看到协议在跑(端口以容器实际配置为准):
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
4.3 更简洁的 Java 方式:Spring AI 注解版
Java 生态还有一个官方推荐的快捷路径——Spring AI 2.0+ 的 MCP 注解支持(spring-ai-starter-mcp-server-webmvc / webflux 等 starter):一个 @Tool 注解就能把普通 Java 方法暴露成 MCP 工具,方法名、参数名、@ToolParam 描述会自动生成工具 Schema,体验接近 Python FastMCP 的”类型即 Schema”:
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CalculatorTools {
@Tool(description = "求两个整数之和")
public int add(@ToolParam(description = "第一个整数") int a,
@ToolParam(description = "第二个整数") int b) {
return a + b;
}
@Tool(description = "计算斐波那契数列的第 n 项(n >= 0)")
public int fib(@ToolParam(description = "项数 n") int n) {
if (n < 0) {
throw new IllegalArgumentException("n 必须 >= 0");
}
int a = 0, b = 1;
for (int i = 0; i < n; i++) {
int next = a + b;
a = b;
b = next;
}
return a;
}
}
Spring 容器扫描到 @Tool Bean 后自动注册进 MCP 服务器,starter 直接暴露 /mcp 端点,无需手写 Specification。如何选择:需要精细控制传输、协议元数据,或不想引入 Spring 的项目,用官方 SDK;Spring 系项目用注解版,开发效率最高。
4.4 写一个 MCP Client 调用自己的服务器
客户端是协议的另一半。用官方 Java SDK 调用上面的服务器:
// McpClientDemo.java
import java.time.Duration;
import java.util.Map;
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.McpSyncClient;
import io.modelcontextprotocol.client.transport.ServerParameters;
import io.modelcontextprotocol.client.transport.StdioClientTransport;
import io.modelcontextprotocol.json.McpJsonDefaults;
import io.modelcontextprotocol.spec.McpSchema.CallToolRequest;
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
import io.modelcontextprotocol.spec.McpSchema.GetPromptRequest;
import io.modelcontextprotocol.spec.McpSchema.GetPromptResult;
import io.modelcontextprotocol.spec.McpSchema.ListToolsResult;
import io.modelcontextprotocol.spec.McpSchema.ReadResourceRequest;
import io.modelcontextprotocol.spec.McpSchema.ReadResourceResult;
import io.modelcontextprotocol.spec.McpSchema.TextContent;
import io.modelcontextprotocol.spec.McpSchema.TextResourceContents;
import io.modelcontextprotocol.spec.McpSchema.Tool;
public class McpClientDemo {
public static void main(String[] args) {
// 1. 声明要拉起的服务器进程(stdio)
ServerParameters params = ServerParameters.builder("java")
.args("-jar", "target/calculator-server-1.0.0.jar")
.build();
// 2. 创建 stdio 传输 + 同步客户端
McpSyncClient client = McpClient.sync(
new StdioClientTransport(params, McpJsonDefaults.getMapper()))
.requestTimeout(Duration.ofSeconds(10))
.build();
try {
// 3. 初始化握手(SDK 自动协商协议版本与能力)
client.initialize();
// 4. 发现工具
ListToolsResult tools = client.listTools();
System.out.println("可用工具: " + tools.tools().stream().map(Tool::name).toList());
// 5. 调用工具
CallToolResult result = client.callTool(CallToolRequest.builder("add")
.arguments(Map.of("a", 40, "b", 2))
.build());
System.out.println("add(40, 2) = " + ((TextContent) result.content().get(0)).text());
// 6. 读取资源
ReadResourceResult note = client.readResource(
ReadResourceRequest.builder("notes://todo").build());
System.out.println("便签内容:\n" + ((TextResourceContents) note.contents().get(0)).text());
// 7. 获取提示词模板
GetPromptResult prompt = client.getPrompt(GetPromptRequest.builder("code_review")
.arguments(Map.of("language", "Java"))
.build());
System.out.println("提示词: " + ((TextContent) prompt.messages().get(0).content()).text());
} finally {
client.closeGracefully();
}
}
}
运行:java -jar target/client-demo.jar,输出类似:
可用工具: [add, fib]
add(40, 2) = 42
便签内容:
1. 写 MCP 博客
2. 本地构建验证
提示词: 请以资深 Java 工程师的视角,从正确性、性能、可读性三个维度审查以下代码:
调用远程服务器时,把 StdioClientTransport 换成 HttpClientStreamableHttpTransport,传入服务器 URL 即可:
var transport = HttpClientStreamableHttpTransport.builder("http://localhost:8080")
.endpoint("/mcp")
.build();
McpSyncClient client = McpClient.sync(transport).build();
4.5 调试:MCP Inspector
官方提供 MCP Inspector 可视化调试工具,无需写代码就能与任意服务器交互:
# 调试 stdio 服务器(命令 + 参数)
npx @modelcontextprotocol/inspector java -jar target/calculator-server-1.0.0.jar
# 或显式指定传输
npx @modelcontextprotocol/inspector --transport stdio -- java -jar target/calculator-server-1.0.0.jar
启动后浏览器打开 http://localhost:6274,你可以:查看服务器能力、手动调用每个工具、读取资源、查看原始 JSON-RPC 消息、模拟客户端行为。写服务器时的第一道调试手段,永远是它。
调试远程 Streamable HTTP 服务器时,同样可以在 Inspector 中选择 HTTP 传输并填入服务器 URL 连接。
4.6 生态盘点:开箱即用的服务器
不想从零写?官方与社区已经提供了大量成熟服务器:
| 类别 | 代表服务器 | 接入方式 |
|---|---|---|
| 官方参考实现 | filesystem、git、memory、fetch、everything、sequentialthinking、time | npx -y @modelcontextprotocol/server-* |
| 代码托管 | GitHub(官方)、GitLab | 远程/本地均支持,token 鉴权 |
| 数据库 | PostgreSQL、SQLite、Redis(官方参考) | npx -y @modelcontextprotocol/server-* |
| 浏览器/爬虫 | Puppeteer(官方参考)、Playwright | npx -y @modelcontextprotocol/server-puppeteer |
| 办公协作 | Google Drive、Slack、Notion | 各家官方发布 |
| 可观测 | Sentry、Cloudflare、Grafana | 远程 HTTP + OAuth |
| 云厂商 | AWS、Azure、阿里云百炼等 | 各云厂商 MCP 网关 |
官方还在 2025 年 6 月推出了 MCP Registry(registry.modelcontextprotocol.io),用于服务器的发现与共享——就像 npm 之于 JavaScript、Docker Hub 之于容器,MCP Registry 之于 AI 工具生态。
4.7 常见坑与最佳实践
| 分类 | 最佳实践 | 原因/说明 |
|---|---|---|
| 工具设计 | 一个工具只做一件事,参数少而明确;名字用动词开头(create_issue 优于 issue) |
避免歧义,符合规范的工具命名指导 |
| 工具设计 | 入参 schema 描述要详细 | 模型只靠 description 理解工具,描述模糊 = 模型不会用 |
| 工具设计 | 校验失败返回工具执行错误(isError: true + 可读信息),而非协议错误 |
模型能根据错误信息自我纠正 |
| 健壮性 | 工具必须幂等或至少可安全重试 | MRTR 会重试原请求,无状态 HTTP 下网络重试是常态 |
| 健壮性 | 长任务用 notifications/progress 汇报进度 |
避免客户端误判超时 |
| 健壮性 | stdio 服务器不要往 stdout 打印日志,日志一律走 stderr | 避免污染协议流 |
| 安全 | 默认拒绝一切不确定操作;破坏性工具声明 destructiveHint |
Host 端配合用户确认 |
| 安全 | 远程服务器必须实现 OAuth 2.1,不要自造 token 方案 | 标准鉴权,可审计 |
| 安全 | 不信任模型传来的路径/URL | 服务器侧做路径穿越与 SSRF 防护 |
| 性能 | 只读接口合理设置 ttlMs 让客户端缓存 |
减少无谓轮询 |
| 性能 | 工具列表保持稳定顺序 | 提升 LLM 提示词缓存命中率 |
五、总结与展望
MCP 与 Function Calling 的关系
很多人问:有了 Function Calling,为什么还要 MCP?答案是层次不同:
| 维度 | Function Calling | MCP |
|---|---|---|
| 层次 | 模型 API 的一个参数(怎么把工具描述给模型) | 模型之外的应用层协议(工具如何被发现、连接、授权) |
| 谁来实现 | 模型厂商 | 任何工具方 |
| 绑定 | 绑定特定厂商的模型 API | 中立,跨模型、跨厂商 |
| 可替换性 | 换模型就要换适配 | 工具实现一次,处处可用 |
事实上两者是互补的:MCP Server 暴露工具 → 客户端通过 MCP 拿到工具清单 → 翻译成某个模型厂商的 Function Calling 格式 → 模型推理调用。MCP 解决”连接”问题,Function Calling 解决”推理”问题。
上面是”精确版”答案;还没完全 get 到的话,直接看第六节的”大白话版”。
现状与未来方向
截至 2026 年中,MCP 已经走完”从概念到标准”的阶段,正在走”从标准到基础设施”的阶段:
| 方向 | 现状 |
|---|---|
| 协议侧 | 无状态化让 MCP 服务器可以像普通微服务一样部署运维;MRTR 让”服务器向人要输入”成为一等公民;Tasks 作为官方扩展支持异步长任务 |
| 生态侧 | 主流模型厂商、云厂商、开发工具全线接入;MCP Registry 成为工具分发的公共设施 |
| 治理侧 | 规范进入行业共同治理轨道,版本节奏稳定,废弃机制(feature lifecycle)成熟——Roots/Sampling 的优雅退役就是例证 |
可以预见的方向:MCP 会继续向 Agent 互操作(多个 Agent 通过 MCP 协作)、结构化输出(outputSchema 进一步收紧)、企业级治理(细粒度权限、审计、多租户)演进。对开发者而言,MCP 正在成为 AI 时代的”标准库接口”——就像当年 REST 统一了前后端,MCP 正在统一”模型与世界的对话方式”。
一句话总结
MCP 用一套基于 JSON-RPC 的开放协议,把”模型、数据、工具”三者解耦:类型上分清角色/原语/传输/形态,原理上依靠能力自描述与多轮消息模式完成动态集成,使用上十分钟就能写一个服务器接入任何客户端——它是 AI 应用连接真实世界的通用插座。
现在,打开你的编辑器,写一个属于自己的 MCP Server 吧。
六、番外:大白话讲清楚 MCP 与 Function Calling、SKILL 的关系
前面的对比表”精确但抽象”,这一节用大白话把两者讲透。先记住一句话:
Function Calling 是”教模型怎么用工具”,MCP 是”让工具谁都能接”。
6.1 一个比喻:插座与插头
想象你要给手机充电:
- Function Calling 是插头规格——你的手机用 Type-C 还是 Lightning,决定了”电”(工具调用)怎么进到手机(模型)里。麻烦在于每家手机厂商的插头规格还不一样,换个牌子就得换线。
- MCP 是插座标准——充电器(工具方)把电接到统一的国标插座上,不管什么手机,插上就能充。插座是墙上的公共基础设施,跟你是哪个牌子的手机没关系。
再换个更生活化的场景——点外卖:
- 没有 MCP 时:想让 AI 帮你点餐,得分别装美团、饿了么、肯德基三个 App,记住三套完全不同的操作流程——对应各家模型私有的 Function Calling 格式,每个工具都要单独适配;
- 有了 MCP 后:三家店统一入驻外卖平台(MCP Server),AI 只需要学会平台这一套标准话术,就能点任何一家店。
6.2 一张图看清”谁管哪一层”
┌──────────────────────────────────────────────┐
│ 模型推理:选哪个工具、传什么参数(大脑做决策) │
│ ↑ 这一层是 Function Calling 的地盘 │
├──────────────────────────────────────────────┤
│ 连接层:工具怎么注册、发现、鉴权、调用(管道) │
│ ↑ 这一层是 MCP 的地盘 │
├──────────────────────────────────────────────┤
│ 真实世界:文件、数据库、GitHub、业务系统 │
└──────────────────────────────────────────────┘
- Function Calling 管最上面一层:把工具描述(名字、参数、用途)翻译成模型能理解的格式,让模型在推理时选对工具、填对参数。它是模型 API 自带的一个参数,跟模型厂商深度绑定。
- MCP 管中间一层:工具怎么被发现、怎么连上、谁有权限用、结果怎么回来。它是模型之外的独立协议,跟用哪个模型无关。
6.3 大白话对比表
| 对比点 | Function Calling | MCP |
|---|---|---|
| 一句话 | 教模型”怎么用工具” | 让工具”谁都能接” |
| 本质 | 模型 API 里的一个参数格式 | 一个独立的开放协议 |
| 谁说了算 | 模型厂商(OpenAI、Anthropic 各有一套) | 开放社区共同治理的标准 |
| 换个模型 | 工具适配代码要重写 | 工具实现一次,处处可用 |
| 管的事 | 推理层:模型怎么理解工具、怎么决定调用 | 连接层:工具怎么注册、发现、授权、调用 |
| 类比 | 手机充电的插头规格 / 各家 App 的专属操作流程 | 统一的国标插座 / 统一的外卖平台 |
6.4 最常见的两个误区
误区一:”有了 MCP 就不需要 Function Calling 了”——错,两者是配合关系,不是替代关系。真实链路是这样的:
MCP Server 暴露工具
→ MCP Client 拿到工具清单
→ 翻译成当前模型的 Function Calling 格式
→ 模型推理并调用工具
MCP 解决”连接”问题,Function Calling 解决”推理”问题——一个管管道,一个管大脑,缺一不可。
误区二:”MCP 是 Anthropic 家的私有协议”——早期确实如此,现在早已不是。2025 年起 OpenAI、Google、Microsoft 等主流厂商全部宣布支持,规范进入行业共同治理,谁都能实现、谁都能接入。
6.5 那模型到底先调哪个?——实际调用顺序
一个常见追问:”模型是先调 Function Calling,再调 MCP 吗?”答案是:不是先后两步,而是”模型表态、宿主执行”的分工。模型根本不会直接碰 MCP,完整的调用时序是这样的:
① 用户提问
↓
② 宿主把"问题 + 工具清单"发给模型
(工具清单是初始化时通过 MCP 的 tools/list 发现、
再翻译成该模型 Function Calling 格式的结果)
↓
③ 模型推理,决定"我要用 add(40,2)"
(返回 tool_calls —— 这是 Function Calling 的地盘,模型只"表态")
↓
④ 宿主收到调用意图,找到对应的 MCP Server
(这是宿主应用干的活,不是模型干的)
↓
⑤ 宿主的 MCP Client 向 MCP Server 发送 tools/call(JSON-RPC)
↓
⑥ MCP Server 真正执行:读文件 / 查数据库 / 调 GitHub API
↓
⑦ 结果沿 MCP 返回宿主
↓
⑧ 宿主把结果按 Function Calling 的 tool result 格式回传给模型
↓
⑨ 模型拿着结果继续推理,可能再发起下一轮工具调用
一句话总结:Function Calling 负责”表态”(模型说要用什么工具),MCP 负责”干活”(宿主去找服务器真正执行)。一个发生在模型 API 里,一个发生在应用层,二者在每一轮”工具调用 → 结果回传”的循环里交替配合,而不是先后串联的两步。
6.6 顺带聊聊:SKILL 和 MCP 是什么关系?
经常有人把 SKILL 和 MCP 搞混——它们都是”增强 AI 能力”的东西,但层次完全不同。
先明确 SKILL 指什么:Anthropic 推出的 Claude Skills(Agent Skills),把”做某件事的方法论”打包成一个文件夹:SKILL.md(说明”这事该怎么按流程做、按什么标准做”)+ 可选脚本和参考文件。模型加载后,就像拿到一本老师傅的经验笔记,立刻知道该怎么干活。
| 维度 | MCP | SKILL |
|---|---|---|
| 本质 | 通信协议:连接外部工具和数据 | 知识包:封装做事的流程与经验 |
| 组成 | Server 暴露 tools / resources / prompts | SKILL.md 指令 + 可选脚本/资源 |
| 解决什么 | 能力从哪来(读文件、查库、调 API) | 方法是什么(先做什么、后做什么、按什么标准) |
| 作用对象 | 外部世界(数据、动作、系统) | 模型自身的行为(怎么思考、怎么执行) |
| 类比 | 工具箱 / 插座 | 使用说明书 / 老师傅的经验笔记 |
一句话:MCP 给 AI”装手和眼睛”,SKILL 给 AI”塞操作手册”——MCP 解决”能不能连到外面的世界”,SKILL 解决”活儿该怎么干、干得好不好”。
两者经常配合使用:比如一个”写周报”的 SKILL 会写明”先收集本周 commit → 按模块归类 → 突出风险项 → 用固定模板输出”;其中”收集 commit”这一步,SKILL 会指示模型去调用 GitHub 的 MCP 工具。SKILL 是剧本,MCP 是舞台上的道具与通道——SKILL 教模型什么时候用、怎么用工具,MCP 负责把工具真正接进来。反过来,MCP 完全不依赖 SKILL:一个 MCP Server 没有 SKILL 也能被正常调用。
记忆口诀:MCP 让 AI 够得着世界(工具、数据),SKILL 让 AI 知道怎么做(流程、规范、经验)——一个管”手脚”,一个管”方法论”。
一句话收尾:Function Calling 让模型”会用手”,MCP 让模型”有手可用”——一个管大脑,一个管手脚,合在一起,AI 才算真正”手脚并用”。
参考资料:
- Model Context Protocol — 官方规范
- MCP Specification 2026-07-28 — Key Changes
- MCP Specification 2025-11-25 — Key Changes
- Model Context Protocol — 中文规范站
- Anthropic 官方博客 — Introducing the Model Context Protocol
- MCP 官方 Java SDK
- MCP Java SDK 文档(Quickstart / Server / Client)
- MCP 官方 Python SDK(FastMCP)
- MCP 官方参考服务器
- MCP Registry
- MCP Inspector 使用文档
- MCP 安全最佳实践