MCP 终极指南——从协议原理到 Agent 实现全解析

素材来源:B站「马克的技术工作坊」三集视频

本文在视频内容基础上做了结构化梳理,并对讲解中若干不够准确或已过时的部分做了修订与补充(文中以 ⚠️ 勘误 / 补充 标注)。


一、先讲结论:一句话搞懂 MCP

MCP = 让大模型能够”发现并调用外部工具”的标准协议。

全称 Model Context Protocol(模型上下文协议),由 Anthropic 于 2024 年 11 月 25 日发布。名字听起来玄乎,拆开说其实就两件事:

  1. 注册:让宿主程序(MCP Host)知道 MCP Server 提供了哪些函数
  2. 调用:让宿主能按规范调用这些函数、拿回结果

⚠️ 补充澄清:视频一再强调”名字取得不好,有误导性”,但更准确的说法是——MCP 提供的”上下文”是指工具、资源、提示词这些”模型可以感知和使用的外部信息”,是给模型提供”环境感知能力”的统一抽象,而不只是”函数调用协议”。这也是 MCP 规范中同时定义了 Tools / Resources / Prompts / Sampling 四类原语的原因(见下文补充)。


二、角色与名词一次讲清

视频里反复强调了几个”听起来很唬人、实际很朴素”的名词。一张表汇总:

名词 真实身份 举例
MCP Host 支持 MCP 协议的客户端软件 Claude Desktop、Cursor、Cline、Cherry Studio
MCP Server 一个本地/远程程序,符合 MCP 协议规范 weather、fetch、hot-news
Tool MCP Server 内的一个函数 get_forecast(lat, lon)
Resource 可被模型读取的”数据源”(文件、报告等) file://report.pdf
Prompt 预置的提示词模板 "解释这段代码"
Transport Host 与 Server 之间的传输层 stdio / SSE / Streamable HTTP

⚠️ 勘误 1(视频轻微过时):视频说 MCP 只有 stdioSSE 两种 Transport。自 2025 年 3 月 26 日的协议修订版起,SSE 已被弃用,官方推荐的远程传输是 Streamable HTTP(单个 HTTP 端点 + 按需升级成 SSE 流)。现在配置远程 Server 时应优先使用 Streamable HTTP。

⚠️ 勘误 2:视频把 Server 一词解释为”跟传统 Server 没关系”,这不完全准确。MCP 里的 Server 严格来说是协议角色——它是”被动响应 Host 请求”的一方,角色语义是 Server;只不过它的物理形态不一定是远程服务器,可以是本地子进程。


三、整体架构:三个角色的协作

1
2
3
4
5
6
7
8
9
10
11
12
13
┌──────────┐    ① 模型交互         ┌──────────┐
│ LLM │ ◄───────────────────► │ MCP Host │
└──────────┘ (XML / JSON / │ (Cline…) │
Function Calling) └────┬─────┘

② MCP 协议(JSON-RPC 2.0)

┌────────────────────────────────┼────────────────────────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ weather │ │ fetch │ │ hot-news │
│ Server │ │ Server │ │ Server │
└──────────┘ └──────────┘ └──────────┘
  • ① Host ↔ LLM:由 Host 自己定义,MCP 不管这一段。Cline 用 XML、Cherry Studio 用 Function Calling,都可以。
  • ② Host ↔ Server:这一段才是 MCP 协议真正规定的内容,底层是 JSON-RPC 2.0

⚠️ 补充:视频花了大量篇幅强调”MCP 不规定模型交互格式”,这一点非常关键——MCP 是一个”工具协议”,不是”模型协议”。理解这一点,你就不会把 MCP 与 OpenAI 的 Function Calling 搞混。


四、上手:在 Cline 里用第一个 MCP Server

视频以 Cline(VS Code 插件)为例,步骤如下:

  1. VS Code 中安装 Cline 插件
  2. 配置 API Provider(OpenRouter / DeepSeek / Claude 3.7 等均可)
  3. 打开 MCP 配置 JSON,新增条目:
1
2
3
4
5
6
7
8
9
10
11
{
"mcpServers": {
"weather": {
"command": "uv",
"args": ["--directory", "/path/to/weather", "run", "weather.py"],
"transportType": "stdio",
"timeout": 60,
"disabled": false
}
}
}
  1. 保存后 Cline 会自动拉起该进程
  2. 在对话里直接问”纽约明天的天气怎么样”,Cline 会自动匹配工具、征得同意后调用

两种主流启动方式

启动器 背后生态 典型命令
uvx Python uvx mcp-server-fetch
npx Node.js npx -y @modelcontextprotocol/server-filesystem /path

💡 避坑提示uvx / npx 首次运行会下载依赖,Cline 默认 60 秒超时。建议先在终端手动跑一次让依赖下载完,再回到 Host 里加载。


五、自己写一个 MCP Server(Python 版)

5.1 初始化项目

1
2
3
4
5
uv init weather
cd weather
uv venv
source .venv/bin/activate
uv add "mcp[cli]" httpx

5.2 weather.py 最小骨架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from typing import Any
import httpx
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("weather", log_level="ERROR") # ← log_level 很重要,见下方勘误

NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-app/1.0"

async def make_nws_request(url: str) -> dict[str, Any] | None:
# ... httpx 请求美国气象局 API
...

@mcp.tool()
async def get_alerts(state: str) -> str:
"""获取指定美国州的天气预警。
Args:
state: 两位美国州代码,例如 CA、NY
"""
...

@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:
"""获取指定经纬度的未来几天天气预报。
Args:
latitude: 纬度
longitude: 经度
"""
...

if __name__ == "__main__":
mcp.run(transport="stdio")

⚠️ 勘误 / 补充:视频中提到”官方示例不加 log_level=ERROR 跑不起来”。这其实是因为 stdio 传输同时把 stdout 当作协议通道——任何打到 stdout 的日志都会污染 JSON-RPC 消息。正确姿势是把日志输出到 stderr,比如使用 logging.basicConfig(stream=sys.stderr),而不是简单降日志级别。新版 FastMCP 也已默认把 stdio 日志重定向到 stderr,可以不再显式设 log_level

5.3 @mcp.tool() 装饰器的魔法

  • 函数名name
  • docstringdescription
  • 类型注解 + 默认值inputSchema(自动生成符合 JSON Schema 的入参描述)
  • 返回值 → 作为工具结果回传给 Host

这套”注解即协议“的设计,是 MCP 协议能跨语言复用的根本:任何能生成 JSON Schema 的语言,都能写 MCP Server


六、协议帧级拆解:Cline ↔ Server 到底聊了什么

视频里用 mcp_log.py 做了一个”中间人”脚本,拦截 Cline 与 weather Server 之间的输入/输出,得到完整的 JSON-RPC 对话。核心顺序如下:

① 握手 initialize

1
2
3
4
5
6
7
8
9
10
11
12
// Host → Server
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{
"protocolVersion":"2024-11-05",
"clientInfo":{"name":"Cline","version":"3.12.3"}
}}

// Server → Host
{"jsonrpc":"2.0","id":1,"result":{
"protocolVersion":"2024-11-05",
"capabilities":{...},
"serverInfo":{"name":"weather","version":"1.6.0"}
}}

② initialized 通知

1
2
// Host → Server(单向通知,Server 不回复)
{"jsonrpc":"2.0","method":"notifications/initialized"}

③ 工具列表 tools/list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Host → Server
{"jsonrpc":"2.0","id":2,"method":"tools/list"}

// Server → Host
{"jsonrpc":"2.0","id":2,"result":{"tools":[
{
"name":"get_forecast",
"description":"获取指定经纬度的未来几天天气预报……",
"inputSchema":{
"type":"object",
"properties":{
"latitude":{"type":"number"},
"longitude":{"type":"number"}
},
"required":["latitude","longitude"]
}
},
{"name":"get_alerts", ...}
]}}

④ 可选:资源、资源模板询问

1
tools/list → resources/list → resources/templates/list

握手阶段到此结束,后续进入”按需调用”。

⑤ 调用工具 tools/call

1
2
3
4
5
6
7
8
9
10
// Host → Server
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{
"name":"get_forecast",
"arguments":{"latitude":40.7128,"longitude":-74.006}
}}

// Server → Host
{"jsonrpc":"2.0","id":3,"result":{"content":[
{"type":"text","text":"Tonight: 55°F, clear …"}
]}}

手搓 MCP:不用 Host 也能玩

正因为协议是纯文本 JSON-RPC,你完全可以在终端里起一个 Server,手动粘贴这几条消息就能完成一次完整交互——视频里亲自演示过。这也反过来说明:任何能读写 stdin/stdout 的东西都能当 MCP Client,bash 脚本都行。


七、MCP Host 与模型之间:XML 还是 Function Calling?

这是视频番外篇的核心,也是很多人容易混淆的地方。

再强调一次:MCP 不规定 Host 与模型之间的交互格式。

Cline 的做法:XML + 超长 System Prompt

番外篇用中转服务器抓取了 Cline 与 DeepSeek 的真实请求,发现 Cline 的 system prompt 长达 4.8 万字符(约 626 行),结构大致是:

  1. 工具使用格式说明(要求用 XML)
  2. Cline 内置工具列表(read_file、write_to_file、execute_command…)
  3. 工具使用示例
  4. 工具使用指南
  5. 已连接的 MCP 服务器 & 每个 Server 下的工具清单
  6. 格式化/工作流/首选语言等约束

模型按要求输出 XML,例如调用 MCP 工具:

1
2
3
4
5
6
7
<use_mcp_tool>
<server_name>weather</server_name>
<tool_name>get_forecast</tool_name>
<arguments>
{ "latitude": 40.7128, "longitude": -74.006 }
</arguments>
</use_mcp_tool>

Cline 解析这段 XML → 执行 tools/call → 把结果再喂给模型。

几个关键 XML 标签

标签 语义
<thinking> 模型被强制”先思考再行动”
<use_mcp_tool> 调用 MCP Server 工具
<read_file> / <write_to_file> / <execute_command> Cline 内置工具
<ask_followup_question> 向用户提问
<attempt_completion> 最终答案,触发后对话结束

其他 Host 的做法

  • Cherry Studio / Claude Desktop:使用 Function Calling(OpenAI / Anthropic 原生 tools 字段)。模型不用自己拼 XML,平台通过结构化字段传递工具调用。
  • 自定义 Agent:你甚至可以用纯文本格式。

⚠️ 补充:视频暗示 “Function Calling 是 OpenAI 提出来和 MCP 并列的概念”,这容易误解。更准确的说法是:

  • Function Calling 是模型厂商的 API 约定(模型 ↔ Host 这一段)
  • MCP 是工具接入的标准协议(Host ↔ Server 这一段)
  • 两者不冲突,通常配合使用:Host 把 MCP Server 暴露的 Tool 列表翻译成 Function Calling 的 tools 字段发给模型。

八、把视角拉高:MCP 与 ReAct 的关系

番外篇最后揭示了一个”顿悟点”:

Cline 本质上是一个基于 ReAct 范式的 Agent,MCP 只是它调用外部工具时使用的底层协议。

ReAct 三板斧

ReAct 来自 Yao et al., 2022 论文,核心就是让模型循环执行三个步骤:

1
2
3
4
Thought(思考) → Action(行动) → Observation(观察)
↑ │
└────────── 直到得到答案 ←──────────┘
Final Answer

对照 Cline 的 XML 就是:

ReAct Cline XML
Thought <thinking>
Action <use_mcp_tool> / <execute_command> / …
Observation tool 返回结果
Final Answer <attempt_completion>

⚠️ 补充:ReAct 论文原文使用的是纯文本Thought/Action/Observation: 前缀。Cline 用 XML 做的是同一件事,只是表达更精确、鲁棒性更好。你也可以换成 JSON,原理不变。协议形式不重要,”思考—行动—观察”的循环才是精髓

手撸一个 Agent 需要什么

只要具备这三样,你就能造出一个像 Cline 一样的 Agent:

  1. 一份 System Prompt,规定输出格式(XML / JSON / 文本皆可)
  2. 一份可用工具列表(来自 MCP Server、本地能力,甚至直接的 HTTP API)
  3. 一段循环代码:解析模型输出的 Action → 执行 → 把 Observation 回填到上下文 → 再次 invoke 模型 → 直到出现 Final Answer

九、视频未覆盖的重要补充(2025~2026 新特性)

视频是 2025 年 4 月左右的内容,MCP 规范此后有多项重要演进,这里一次性补齐:

1. Streamable HTTP 取代 SSE

  • 单端点(HTTP POST + 可选 SSE 升级)
  • 原生支持断线续传、负载均衡、会话恢复
  • 推荐所有新项目直接使用

2. Sampling(让 Server 反向调用模型)

  • MCP 规范允许 Server 向 Host 发起 sampling/createMessage
  • 含义:Server 可以”请求使用 Host 已连接的模型”来完成子任务
  • 典型场景:一个复杂工具内部自己做一次小推理,而不是自己去调 OpenAI
  • Host 需用户授权后才会真正转发给 LLM

3. Elicitation(请求用户输入)

  • Server 可以向 Host 请求”问用户拿一些额外输入”
  • 例如执行前让用户确认某个参数

4. OAuth 2.1 鉴权

  • 远程 MCP Server 推荐使用 OAuth 2.1 + PKCE
  • 解决”多用户 / 多租户”场景下的身份与权限问题

5. Roots(工作目录隔离)

  • Host 可以声明”模型只能看这些目录”
  • 与文件类 Server 配合,做最小权限沙盒

十、常见误区与最佳实践

误区 正解
「MCP 和 Function Calling 是竞品」 不同层:MCP 管 Server 接入,Function Calling 管模型调用
「MCP Server 必须在云端跑」 绝大多数生产案例都是本地子进程
「stdio 不安全」 stdio 跑在本机子进程里,反而比远程网络传输更安全
「装了 MCP 就等于有 Agent」 MCP 只提供工具通道,Agent 的智能来自 ReAct 循环 + 好的 Prompt
「要让模型调用工具必须改模型」 不需要。关键在于 Host 的系统提示词和循环逻辑

生产环境选型建议

  • 本地开发 / 个人工具链stdio
  • 企业内部共享服务 → Streamable HTTP + OAuth
  • 多模型并行 → 统一用 MCP 抽象工具层,切换模型只需换 Host/API Provider
  • 敏感操作 → 显式加人机 approval,像 Cline 默认那样

十一、一图速记

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
          MCP 全景图

┌─────────────────────────────────────────┐
│ LLM(任意模型) │
└───────────────▲─────────────────────────┘
│ ReAct 循环
│ (XML / JSON / Function Calling)
┌───────────────┴─────────────────────────┐
│ MCP Host (Cline…) │
│ ┌─────────────────────────────────┐ │
│ │ 内置工具:read_file / exec … │ │
│ │ MCP Client:协议桥 │ │
│ └─────────────────────────────────┘ │
└────────▲──────────────▲─────────────────┘
JSON-RPC│ │ JSON-RPC
┌─────────┴─────┐ ┌─────┴──────────┐
│ weather MCP │ │ fetch MCP │
│ Server(stdio) │ │ Server(HTTP) │
└───────────────┘ └────────────────┘
│ │
美国气象局 API 互联网

结语

MCP 真正厉害的地方,不在于协议本身有多复杂,而在于它把”模型如何使用外部能力“这件事标准化了。有了它,Cline、Cursor、Claude Desktop 这些 Host 可以共享同一批 Server 生态;你写一个天气 Server,所有支持 MCP 的软件都能用。

再往深一层——MCP + ReAct = 可落地的 Agent 骨架。读懂了这个链路,你不仅知道怎么用 MCP,更能自己造一个 Agent

📚 推荐资源