MCP 是近一年最值得深入理解的 AI 协议。不是因为它复杂,恰恰是因为它极其简单------简单到你可以在一小时内手写一个 MCP Server,却能解决 AI 工具生态碎片化的根本问题。
一个比喻说清 MCP 的价值
在 USB-C 标准化之前,手机充电口是一场灾难:MicroUSB、Lightning、各种私有接口------每换一台设备就要换一套线。
MCP 之前的 AI 工具生态,就是这个状态。
每个 AI 应用(Cursor、Cline、Claude Desktop)想要连接外部工具(数据库、文件系统、搜索引擎),都要单独开发适配代码。工具提供方也要为每个 AI 应用写专门的插件。
css
MCP 之前(碎片化):
Cursor ─── 自定义适配 ──→ 数据库
Cursor ─── 自定义适配 ──→ GitHub
Cline ─── 自定义适配 ──→ 数据库(重新写一遍)
Cline ─── 自定义适配 ──→ GitHub(再写一遍)
Anthropic 在 2024 年 11 月发布 MCP,提供了一个标准化协议:
arduino
MCP 之后(标准化):
Cursor ─── MCP 协议 ──→ MCP Server(数据库)
Cline ─── MCP 协议 ──→ MCP Server(数据库)
Claude ─── MCP 协议 ──→ MCP Server(GitHub)
工具只需实现一次,所有支持 MCP 的 Host 都能用。
这就是 MCP 的核心价值:一次实现,到处可用。
MCP 的三个核心角色
1. MCP Host(宿主)
支持 MCP 协议的客户端软件。它是"协调者",负责:
- 启动和管理 MCP Server
- 把用户请求、可用工具列表发给 LLM
- 接收 LLM 的工具调用指令并执行
典型 MCP Host:Claude Desktop、Cline(VSCode 插件)、Cursor、OpenClaw
2. MCP Server
实现了 MCP 协议的服务程序,提供具体的工具能力。它回答一个问题:"我能做什么?"
MCP Server 可以暴露三类能力:
| 类型 | 描述 | 示例 |
|---|---|---|
| Tool | 可执行的函数,主动操作 | 查询天气、执行 SQL、发送邮件 |
| Resource | 可读取的数据,被动提供 | 文件内容、数据库 schema |
| Prompt | 预定义的提示词模板 | 代码审查模板、翻译模板 |
3. LLM(大语言模型)
MCP 协议本身不直接与 LLM 交互。LLM 是通过 Host 的 System Prompt 间接了解有哪些工具可用,然后"请求"Host 去调用。
arduino
用户输入
↓
MCP Host(把问题 + 工具列表一起发给 LLM)
↓
LLM 决策(我需要调用 get_weather 工具)
↓
MCP Host(向 MCP Server 发送调用请求)
↓
MCP Server(执行,返回结果)
↓
MCP Host(把结果返回给 LLM)
↓
LLM 生成最终答案
MCP 协议的底层通信
MCP 基于 JSON-RPC 2.0 标准,传输层支持两种方式:
- stdio:通过标准输入输出通信(本地进程,最常用)
- HTTP/SSE:通过 HTTP 长连接(远程服务)
完整的交互流程
第一步:初始化握手
json
// Client → Server
{
"jsonrpc": "2.0",
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"clientInfo": {"name": "Cline", "version": "3.0"}
}
}
// Server → Client
{
"jsonrpc": "2.0",
"result": {
"serverInfo": {"name": "weather-server", "version": "1.0"},
"capabilities": {"tools": {}}
}
}
第二步:工具发现
json
// Client → Server
{"jsonrpc": "2.0", "method": "tools/list"}
// Server → Client
{
"jsonrpc": "2.0",
"result": {
"tools": [{
"name": "get_forecast",
"description": "获取指定经纬度的天气预报",
"inputSchema": {
"type": "object",
"properties": {
"latitude": {"type": "number"},
"longitude": {"type": "number"}
},
"required": ["latitude", "longitude"]
}
}]
}
}
第三步:工具调用
json
// Client → Server(LLM 决定调用工具后,Host 发起)
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "get_forecast",
"arguments": {"latitude": 39.9042, "longitude": 116.4074}
}
}
// Server → Client
{
"jsonrpc": "2.0",
"result": {
"content": [{"type": "text", "text": "北京今天晴,气温 22°C,东风 3 级..."}]
}
}
动手写一个 MCP Server
用 Python + FastMCP 实现一个天气查询 MCP Server,是理解 MCP 最快的方式。
环境准备
bash
# 使用 uv 管理环境(推荐)
pip install uv
uv init weather-mcp
cd weather-mcp
uv add "mcp[cli]" httpx
核心代码(weather.py)
python
from typing import Any
import httpx
from mcp.server.fastmcp import FastMCP
# 初始化 FastMCP,log_level=ERROR 避免日志干扰 stdio 通信
mcp = FastMCP("weather", log_level="ERROR")
NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-mcp/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_forecast(latitude: float, longitude: float) -> str:
"""
获取指定经纬度的天气预报
Args:
latitude: 纬度(-90 到 90)
longitude: 经度(-180 到 180)
"""
points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"
points_data = await make_nws_request(points_url)
if not points_data:
return "无法获取该位置的预报数据"
forecast_url = points_data["properties"]["forecast"]
forecast_data = await make_nws_request(forecast_url)
if not forecast_data:
return "无法获取详细天气预报"
periods = forecast_data["properties"]["periods"][:5]
forecasts = []
for period in periods:
forecasts.append(
f"{period['name']}: {period['temperature']}°{period['temperatureUnit']}, "
f"{period['shortForecast']}"
)
return "\n".join(forecasts)
if __name__ == "__main__":
mcp.run(transport='stdio')
关键点解析
@mcp.tool() 装饰器的魔法:
- 自动读取函数的参数类型注解,生成 JSON Schema
- 自动读取 docstring,作为工具的
description(这个 description 非常重要,LLM 靠它决定"要不要调用这个工具") - 自动注册到 MCP Server
为什么 log_level="ERROR":
- MCP 通过 stdio 通信,日志输出会干扰 JSON-RPC 消息
- 必须确保 stdout 只输出 JSON,不能混入其他内容
在 Cline 中配置使用
json
// ~/.vscode/cline_mcp_settings.json
{
"mcpServers": {
"weather": {
"command": "uv",
"args": [
"--directory",
"/path/to/weather-mcp",
"run",
"weather.py"
],
"transportType": "stdio"
}
}
}
配置完成后,在 Cline 中输入:
北京今天天气怎么样?(纬度 39.9042,经度 116.4074)
Cline 会自动识别需要调用 get_forecast 工具,执行后将天气数据返回给 LLM,LLM 给出自然语言回答。
一个容易混淆的关键点
MCP 不规定 Host 如何与 LLM 交互。
MCP 协议只定义了 Host ↔ Server 之间的通信格式。Host 如何把工具信息告诉 LLM,完全由 Host 自行决定:
- Cline 用 XML 格式把工具信息注入 System Prompt
- CherryStudio 用 Function Calling 格式
- Claude Desktop 有自己的实现方式
这就是为什么"MCP" 的名字里有"Model",但 MCP 协议本身不直接与 Model 通信------它让 Host 拥有了标准化的"工具获取能力",但 Host 怎么把工具给 Model 用,各家有各家的做法。
总结
MCP 的核心价值:
- 标准化:统一了 AI 工具的"接口标准"
- 可复用:MCP Server 写一次,所有支持 MCP 的 Host 都能用
- 可扩展:任何语言都能实现 MCP Server,生态快速扩张
- 简单:协议本身很简单,用 FastMCP 几十行就能跑起来
下一步建议:动手写一个 MCP Server,把自己常用的工具(读本地文件、查数据库、调用某个 API)封装进去,亲身体会"AI + 工具"的威力。