Loading

AIGC标识 MCP 终极指南完整笔记 —— 基础篇 + 进阶篇 + 番外篇

本文整理自 B 站 UP 主「马克的技术工作坊」的《MCP 终极指南》完整系列,并结合 UP 主开源的配套代码仓库 MarkTechStation/VideoCode 整理而成。

篇目 视频链接 时长 发布时间
基础篇 BV1uronYREWR 27 分钟 2025-04-15
进阶篇 BV1Y854zmEg9 27 分钟 2025-04-19
番外篇 BV1v9V5zSEHA 44 分钟 2025-05-02

第一部分 · 基础篇:MCP 是什么、怎么用

1. MCP 简要介绍

MCP(Model Context Protocol,模型上下文协议)是 Anthropic 于 2024 年 11 月开源的标准协议,用于让大语言模型(LLM)与外部工具和数据源标准化通信。

  • 痛点:M 个模型 × N 个工具 = M × N 套定制集成,重复造轮子
  • 解法:MCP 成为"AI 应用的 USB-C 接口"——工具方实现一次 MCP Server,所有支持 MCP 的 Host 都能使用,问题降为 M + N

M×N 到 M+N

2. 核心概念

  • MCP Host(宿主):运行 LLM、面向用户的应用,如 Cline、Claude Desktop、Cursor
  • MCP Client(客户端):Host 内部的连接器,与 MCP Server 保持 1:1 有状态连接
  • MCP Server(服务器):暴露能力的进程,提供三类原语:
    • Tools:模型可主动调用的功能(查天气、执行 SQL、读写文件)
    • Resources:只读上下文数据(文件内容、数据库记录)
    • Prompts:可复用的提示词模板

MCP 架构

3. 环境搭建与配置

  1. 安装 MCP Host:VS Code 扩展市场安装 Cline
  2. 配置 API Key:Cline 设置中选择 API Provider(Anthropic / OpenRouter / DeepSeek 等),填入 Key
  3. 配置 MCP Server:编辑 ~/.cline/cline_mcp_settings.json
{
  "mcpServers": {
    "weather": {
      "command": "uvx",
      "args": ["mcp-server-weather"],
      "env": {}
    }
  }
}

4. 使用他人制作的 MCP Server

  • Python 系:用 uvx 临时下载运行(需先安装 uv
  • Node 系:用 npx -y @modelcontextprotocol/server-xxx(需先装 Node.js)

5. MCP 交互流程

用户提问 → Host 把问题 + Tools 清单发给 LLM
        → LLM 返回 Tool Call(工具名 + 参数)
        → 用户确认 → Client 转发给 Server
        → Server 执行并返回结果 → 结果回传 LLM → 生成最终回答

MCP 工具调用时序图

要点:LLM 全程不直接接触 MCP Server;Tools 列表会占用上下文窗口,按需安装;用户确认是安全底线。


第二部分 · 进阶篇:自己动手写一个 MCP Server

时间轴:00:00 视频介绍 → 01:12 手写 MCP Server → 11:00 底层协议分析原理 → 12:48 实战协议剖析 → 20:54 直接用协议与 Server 交互 → 23:08 MCP 的真实含义与定位

1. 用 FastMCP 手写一个天气 Server

UP 主用官方 Python SDK 中的 FastMCP 高层封装,接美国国家气象局(NWS)的免费 API,写了一个天气 MCP Server。核心代码(来自配套仓库 MCP终极指南-进阶篇/weather/weather.py):

from typing import Any
import httpx
from mcp.server.fastmcp import FastMCP

# 初始化 FastMCP server
mcp = FastMCP("weather", log_level="ERROR")

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

async def make_nws_request(url: str) -> dict[str, Any] | None:
    """请求 NWS API,带错误处理"""
    headers = {"User-Agent": USER_AGENT, "Accept": "application/geo+json"}
    async with httpx.AsyncClient() as client:
        try:
            response = await client.get(url, headers=headers, timeout=30.0)
            response.raise_for_status()
            return response.json()
        except Exception:
            return None

@mcp.tool()
async def get_alerts(state: str) -> str:
    """Get weather alerts for a US state.

    Args:
        state: Two-letter US state code (e.g. CA, NY)
    """
    url = f"{NWS_API_BASE}/alerts/active/area/{state}"
    data = await make_nws_request(url)
    if not data or "features" not in data:
        return "Unable to fetch alerts or no alerts found."
    if not data["features"]:
        return "No active alerts for this state."
    alerts = [format_alert(f) for f in data["features"]]
    return "\n---\n".join(alerts)

@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:
    """Get weather forecast for a location."""
    points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"
    points_data = await make_nws_request(points_url)
    if not points_data:
        return "Unable to fetch forecast data for this location."
    forecast_url = points_data["properties"]["forecast"]
    forecast_data = await make_nws_request(forecast_url)
    # ...格式化并返回未来 5 个时段的预报
    ...

if __name__ == "__main__":
    mcp.run(transport="stdio")  # 以 stdio 传输方式启动

关键要点:

  1. @mcp.tool() 装饰器:把一个普通 Python 函数变成 MCP Tool——函数签名即参数 Schema,docstring 即工具描述(会被发给 LLM 帮它决定何时调用)
  2. 一个函数一个 Toolget_alerts 查警报、get_forecast 查预报,粒度清晰
  3. mcp.run(transport="stdio"):本地 MCP Server 的标准启动方式——作为 Host 的子进程运行,通过标准输入/输出通信
  4. 项目工程化:用 uv init 创建项目、pyproject.toml 管理依赖(mcp[cli]httpx),uv run weather.py 直接运行

配置到 Cline 时,把这个 Server 注册进去:

{
  "mcpServers": {
    "weather": {
      "command": "uv",
      "args": ["--directory", "/path/to/weather", "run", "weather.py"]
    }
  }
}

2. MCP 底层协议分析的原理与方法

MCP Server 用 stdio 传输时,Host 与 Server 之间的所有通信都走标准输入/输出。这就给抓包提供了一个绝妙的思路:

写一个"中间人"代理——Host 以为在跟真 Server 说话,实际数据先经过代理,代理原样转发的同时把每一行记到日志:

Cline (Host)  ──stdio──▶  mcp_logger.py(中间人,只记录不修改) ──stdio──▶  weather.py (Server)

mcp_logger.py 的核心逻辑(来自配套仓库):

# 用 subprocess 启动真正的 MCP Server,用管道接管它的 stdin/stdout
process = subprocess.Popen(
    target_command,
    stdin=subprocess.PIPE, stdout=subprocess.PIPE,
    stderr=subprocess.PIPE, bufsize=0
)

# 两个线程分别转发并记录两个方向的数据
def forward_and_log_stdin(proxy_stdin, target_stdin, log_file):
    while True:
        line_bytes = proxy_stdin.readline()   # 读 Host 发来的数据
        if not line_bytes: break
        log_file.write(f"输入: {line_str}")   # 记录
        target_stdin.write(line_bytes)        # 原样转发给 Server

def forward_and_log_stdout(target_stdout, proxy_stdout, log_file):
    while True:
        line_bytes = target_stdout.readline() # 读 Server 的返回
        if not line_bytes: break
        log_file.write(f"输出: {line_str}")   # 记录
        proxy_stdout.write(line_bytes)        # 原样转发给 Host

Cline 配置里把 command 换成这个 logger,真正的命令放进 args

{
  "mcpServers": {
    "weather": {
      "command": "uv",
      "args": ["run", "mcp_logger.py", "--", "uv", "run", "weather.py"]
    }
  }
}

这样一次完整的交互后,mcp_io.log 里就躺着 Host 与 Server 的全部对话记录。

stdio 中间人抓包原理

3. 实战解析:协议的完整剖析过程

分析日志可以发现,Host 与 Server 的通信是 JSON-RPC 2.0 格式,每行一条 JSON 消息。完整流程分四步:

JSON-RPC 四步通信

第 1 步 · 初始化握手(initialize)

Host → Server:

{
  "jsonrpc": "2.0", "id": 1, "method": "initialize",
  "params": {
    "protocolVersion": "2024-11-05",
    "capabilities": { "roots": { "listChanged": true } },
    "clientInfo": { "name": "Cline", "version": "..." }
  }
}

Server 回复自己支持的协议版本和能力:

{
  "jsonrpc": "2.0", "id": 1, "result": {
    "protocolVersion": "2024-11-05",
    "capabilities": { "tools": {} },
    "serverInfo": { "name": "weather", "version": "1.0.0" }
  }
}

随后 Host 再发一条 notifications/initialized 通知,握手完成。

第 2 步 · 工具发现(tools/list)

Host → Server:{"jsonrpc": "2.0", "id": 2, "method": "tools/list"}

Server 返回所有 Tool 的清单——每个工具的名称、描述、参数 Schema。这正是 FastMCP 从函数签名和 docstring 自动生成的

{
  "jsonrpc": "2.0", "id": 2, "result": {
    "tools": [
      {
        "name": "get_forecast",
        "description": "Get weather forecast for a location.",
        "inputSchema": {
          "type": "object",
          "properties": {
            "latitude": { "type": "number" },
            "longitude": { "type": "number" }
          },
          "required": ["latitude", "longitude"]
        }
      }
    ]
  }
}

第 3 步 · 工具调用(tools/call)

用户提问后,LLM 决定调用工具,Host → Server:

{
  "jsonrpc": "2.0", "id": 3, "method": "tools/call",
  "params": {
    "name": "get_forecast",
    "arguments": { "latitude": 37.7749, "longitude": -122.4194 }
  }
}

Server 执行函数,返回结果:

{
  "jsonrpc": "2.0", "id": 3, "result": {
    "content": [
      { "type": "text", "text": "Today: Temperature: 18°C, Wind: ..." }
    ]
  }
}

第 4 步 · 结果回传

Host 拿到工具结果,塞进对话历史发回给 LLM,LLM 生成最终自然语言回答。

4. 使用底层协议直接与 MCP Server 交互

既然协议只是"按行发送 JSON-RPC",那完全可以不借助任何 Host,自己在终端里手动扮演 Client:启动 Server 进程,逐行粘贴 initialize → tools/list → tools/call 的 JSON 消息,观察返回。这一步的演示印证了 MCP 的本质:它就是一套约定好的消息格式,没有任何魔法

5. 超越表象:MCP 的真实含义与定位

剥开实现细节,MCP 的定位可以概括为:

  • MCP 是协议,不是框架——它只规定"消息长什么样"(JSON-RPC + 一组约定方法名),不管你怎么实现
  • 解决的是"发现"与"调用"的标准化——工具如何声明自己(tools/list)、如何被调用(tools/call)、结果如何回传,全部有统一格式
  • 类比 USB-C:手机(Host)、数据线(Client)、外设(Server)各司其职,任何符合标准的设备都能即插即用
  • 为什么用 stdio:本地场景下最简单可靠——Server 是 Host 的子进程,管道通信无需网络端口、无认证负担

第三部分 · 番外篇:抓包分析 Cline 与模型的交互协议(Agent 实现原理)

本篇回答一个更深层的问题:MCP Server 之上,Host(Cline)与 LLM 之间是怎么通信的?Agent 到底是怎么实现的?

1. 抓包方法

Cline 与 LLM 之间的通信走 HTTPS,UP 主的方法是搭一个本地代理服务器,把 Cline 的 API Base URL 指向本地代理,代理转发请求到真实 API 并记录全程(配套仓库 MCP终极指南-番外篇/llm_logger.py):

python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python llm_logger.py   # 启动本地记录代理

然后在 Cline 里把 API 地址改指向 http://localhost:xxxx,就能看到 Cline 发给模型的每一个请求、模型的每一次返回。

2. 发现一:Cline 的请求里藏着一个巨大的系统提示词

抓包看到的第一件事:Cline 发给模型的请求体中,system 字段是一篇超长的系统提示词(数千 token),内容大致包括:

  • 你是 Cline,一个资深软件工程师,运行在 VS Code 里
  • 你可以通过工具一步一步完成任务:read_file、write_to_file、search_files、execute_command、ask_followup_question、attempt_completion……
  • 每个工具的调用格式(XML 风格标签)和参数说明
  • 行为准则:一次只用一个工具、必须等工具结果返回再继续、不要假设结果……

关键洞察:所谓"Cline 会用工具",不是模型原生会,而是系统提示词教出来的。

3. 发现二:LLM 并不真正"调用"函数

这是全片最核心的认知纠偏。很多人以为 LLM 原生支持 function calling,真相是:

  1. Cline 把用户问题 + 系统提示词(含工具说明)+ 对话历史打包发给模型
  2. 模型返回的仍然只是文本——一段"我想使用 read_file 工具,参数是 Hello.cs"的格式化文本(XML 标签包裹)
  3. Cline 应用层用字符串解析识别出工具名和参数,在本地执行(读文件)
  4. 把工具执行结果作为新的对话内容再次发给模型
  5. 循环往复,直到模型输出的不再是工具调用,而是 attempt_completion(任务完成的最终回答)

也就是说:

模型输出文本 → 应用层解析文本 → 执行 → 结果拼回对话 → 再发给模型……这就是 Agent 循环的全部真相。
(现代 API 的 "tool use" 能力本质上是把这个模式标准化了——模型经过专门训练,能稳定输出结构化的工具调用格式,但"解析-执行-回传"的循环依然由应用层完成。)

4. 发现三:这就是 ReAct 模式

Cline 系统提示词遵循的模式,学术界叫 ReAct(Reason + Act,2022 年提出)。它的提示词模板长这样(来自配套仓库 ReAct系统提示词.md):

你需要解决一个任务。为此,你需要将任务分解为多个步骤。对于每个步骤,
首先使用 `Thought:` 思考要做什么,然后使用可用工具之一决定一个 `Action:`。
接着,你将根据你的行动从环境/工具中收到一个 `Observation:`。
持续这个思考和行动的过程,直到你有足够的信息来提供 `FinalAnswer:`。

示例:
Question: 埃菲尔铁塔有多高?
Thought: 我需要找到埃菲尔铁塔的高度。可以使用搜索工具。
Action: get_height("埃菲尔铁塔")
Observation: 埃菲尔铁塔的高度约为330米(包含天线)。
Thought: 搜索结果显示了高度。我已经得到答案了。
FinalAnswer: 埃菲尔铁塔的高度约为330米。

请严格遵守:
- 输出Action后立即停止生成
- 等待返回真实的Observation
- 擅自生成Observation将导致错误

一次完整的 ReAct 循环:

Thought(推理)→ Action(行动)→ [应用层执行工具] → Observation(观察)→ Thought → … → FinalAnswer

ReAct 循环与 Agent 实现原理

MCP 在这个图景里的位置:ReAct 循环中的 "Action 执行" 环节,如果调的是外部工具,就经由 MCP Client → MCP Server 完成。两层协议各管一段:

Cline ⇄ LLM         :自然语言 + 系统提示词 + 工具调用文本(ReAct 循环)
Cline ⇄ MCP Server  :JSON-RPC 标准化协议(initialize / tools/list / tools/call)

5. Agent 实现原理总结

把三篇视频的知识串成一句话:

Agent = 一个 while 循环 + 一个解析器 + 一堆工具。
LLM 负责"想"(输出下一步行动),应用层负责"做"(解析、执行、回传),MCP 负责"连接"(标准化地发现和调用外部工具)。

真正需要智能的只有 LLM;循环、解析、调度、协议——其余一切都是工程。


全系列总结

层次 问题 答案
概念层 MCP 是什么 LLM 与外部工具通信的开放标准,"AI 的 USB-C"
使用层 怎么用别人的 Server uvx(Python)/ npx(Node)配到 Host 里
开发层 怎么写自己的 Server FastMCP:装饰器 + 函数签名 = Tool
协议层 底层是什么 JSON-RPC 2.0 over stdio,initialize → tools/list → tools/call
原理层 Host 和 LLM 怎么交互 系统提示词教模型输出工具调用文本,应用层解析执行(ReAct 循环)
本质层 Agent 是什么 while 循环 + 解析器 + 工具集;智能只在 LLM,其余是工程

配套代码仓库https://github.com/MarkTechStation/VideoCode
(含 MCP终极指南-进阶篇/weather/ 完整可运行的天气 Server 与协议 logger、MCP终极指南-番外篇/ 的 Cline 中英文系统提示词与 ReAct 模板)

附录:评论区与弹幕精华补充

本附录整理自三集视频的评论区与 1200+ 条弹幕,都是观众在学习过程中提出的真问题、踩过的真坑和达成的关键共识,与正文互补阅读效果最佳。

一、高频概念辨析:Function Calling 与 MCP 到底什么关系?

这是基础篇弹幕区争论最激烈的问题(02:00 前后刷屏),达成的共识值得记下来:

  • Function Calling(FC)是"能力":它让大模型拥有了"决定使用外部工具"的可能性——模型先有这个意识,才有后面的一切。
  • MCP 是"协议/标准":它规范的是工具如何注册、如何被发现、如何被调用——解决的是"更好、更统一地用工具"的工程问题。
  • 一句话:FC 让模型"会想用工具",MCP 让工具"有个统一的插口"。弹幕金句:"fc 是能力,mcp 是能力协议"。

另一个翻译陷阱:弹幕指出 "Server" 在程序员语境里是"服务器/服务端",不要被"服务者"这类直译带偏。

二、工具到底是谁调用的?(弹幕反复确认的点)

番外篇 17 分钟处观众密集提问"工具接口是大模型调用的还是 agent 主机调用的?",弹幕共识非常一致:

  • 大模型从不真正调用任何东西——它只输出文本:"我要调用某工具,参数是 XXX"。
  • 实际执行者是 Agent(MCP Host):解析模型输出 → 调用工具 → 把结果回传给模型。
  • 弹幕金句:"大模型只是个大脑,它只会说话,干啥都只能通过说话的方式指使别人干。"
  • 这也解释了正文番外篇的结论:function calling 的本质是提示词约定 + 字符串解析,不是模型内建的神秘机制。

三、Token 消耗:MCP 为什么越用越烧钱?(弹幕"token 爆炸"名场面)

番外篇后半段(21:00–34:00)弹幕哀嚎一片"token 爆炸""为了查个天气花了好几百块",背后是三个重要机制:

  1. 大模型是无状态的:每次请求都要把系统提示词 + 全部历史对话重新发一遍。"记忆"全靠客户端把上下文撑着,每次提问 prompt 都会再发一遍。
  2. MCP Server 越多,system prompt 越膨胀:每个 Server 的 tools 描述(名称、参数、说明)都会塞进上下文。有观众提问"加载越多 MCP 是不是越耗 token"——答案是肯定的。Cline 长期霸榜 OpenRouter token 消耗榜第一就是这个原因。
  3. 省钱机制——prefix cache(前缀缓存):重复的 prompt 前缀(系统提示词等固定内容)命中输入缓存后费用大幅降低,这也是为什么各家 API 都强调 prompt caching。

实用建议(来自弹幕):问的问题和当前上下文无关时,果断新开会话,避免无用历史跟着每次请求重发。

四、静态知识 vs 动态信息("纽约经纬度哪来的"之问)

多集弹幕反复出现"模型怎么知道纽约的经纬度"的疑问,答案很清晰:

  • 经纬度属于静态知识,早就在模型的训练数据里了,模型直接"想"出来即可,不用调工具。
  • 天气属于动态信息,训练数据里不可能有"明天"的天气,才需要 MCP 工具去查。
  • 弹幕总结很精辟:"只有动态变量部分是需要 MCP 的方式去调用,静态的都可以依赖模型本身训练的知识。"

五、SSE vs WebSocket(番外篇 04:27–05:30 弹幕热议)

视频里 Cline 与 LLM 之间使用 SSE(Server-Sent Events)流式返回,弹幕补充了关键对比:

SSE WebSocket
方向 单向(服务端 → 客户端推送) 双向
底层 基于 HTTP 独立协议(TCP 升级)
典型场景 LLM 流式返回、行情推送 聊天室、实时游戏
  • 补充细节:LLM API 实际是"HTTP 请求上行 + SSE 下行"的组合——只有模型到客户端是 SSE,客户端发起请求用的是普通 HTTP。
  • 关于流式返回的顺序问题,弹幕也给出答案:SSE 基于 TCP,底层协议栈保证按序到达,应用层无需关心 sequence。
  • 另有观众指出:流式返回里大量 JSON 字段(<thinking> 等结构)中只有 content 部分计入输出 token,其余是传输的数据结构。

六、实操避坑清单(弹幕里的血泪经验)

  • Windows 激活虚拟环境:不用 source,命令是 .venv\Scripts\activate(注意是反斜杠)。
  • FastMCP 版本兼容坑log_level 参数在部分版本会导致 Server 启动失败(有观众反馈报 619 错误);新版本 mcp 库的导入路径也变了(from mcp.server.mcpserver import MCPServer),运行报错先查库版本
  • uv 其实可以省掉 source 这步uv add 会自动检查当前虚拟环境。
  • Cline 早已进化:视频里的 XML 格式工具调用是当时的实现,现在 Cline 已升级为原生 function calling 模式——概念不变,格式与时俱进。
  • 抓包时把 log 文件在编辑器里打开实时刷新,观察更方便(UP 主演示用的 Vim 正则替换格式化 JSON)。

七、观众总结思维导图

基础篇评论区有观众用 Freeform 绘制了一张「Agent = LLM + Tools」全景总结图,与本文观点高度一致,附此供参考:

观众总结思维导图:Agent = LLM + Tools

图中要点:LLM 分析问题并决定用什么工具(结构化格式输出调用请求,如 xml/json);MCP Client(Claude Code、Cursor、Cline、Minima 等)将 LLM 生成的请求发给 MCP Server 或执行本地函数,并把结果回传;底层对应 ReAct 的 Thought → Act → Observation 循环。

参考

posted @ 2026-09-22 18:58  木子七  阅读(53)  评论(0)    收藏  举报