MCP 学习笔记

什么是 MCP?

  • 全称:Model Context Protocol
  • 作用:让 AI 助手(如 Claude、Cline 等)在对话过程中,动态调用外部工具(Tool)完成复杂任务(读写文件、查询数据库、调用 API 等)。
  • 组成
    1. MCP Host(宿主,如 Cline、Claude Desktop)
    2. MCP Server(提供 Tool 的后台服务)
    3. Tool(具体功能单元,如 read_file, exec_command 等)

核心概念速记

  • MCP Server
    • 一个独立进程,提供 1-N 个 Tool。
    • 可以用任何语言编写,只要暴露标准 MCP 接口。
  • Tool
    • 最小执行单元,必须包含:
      • name(唯一)
      • description(让 LLM 理解何时调用)
      • input schema(参数结构,JSON Schema)
  • 交互流程(重点)
    • 在启动mcp server时,server将tool信息传送给host
    • 用户在 Host 输入自然语言需求。
    • Host 将需求 + 可用 Tool 列表发给 LLM。
    • LLM 判断调用哪个 Tool,并填充参数。
    • Host 通过 MCP 协议向对应 Server 发送请求。
    • Server 执行 Tool 并返回结果。
    • Host 将结果合并上下文,继续对话。

mcp和fuction calling的区别

维度 Function Calling(FC) MCP(Model Context Protocol)
本质 能力 —— 某个大模型原生就带的一种「调用函数」功能 协议 —— 定义 AI 与外部世界如何长期、标准、可复用地交互
工作方式 模型在一次推理里主动决定要调用哪个函数,并吐出结构化参数 通过「客户端-服务器」架构,由 MCP Server 被动等待模型或 Agent 的请求
是否标准化 否。OpenAI、Anthropic、百度等各家接口格式不同 是。统一 JSON-RPC 2.0 协议,跨模型通用
上下文管理 单次调用,无状态;复杂多轮任务需自己维护 协议层面支持会话、状态、长链路任务
复用/共享 函数代码往往紧耦合在项目里,换模型就得重写 一次写成 MCP Server,可被任何支持 MCP 的模型/IDE/Agent 直接插用

一句话总结: Function Calling 是「某个模型自带的快捷指令」,MCP 是「让任何模型都能统一插拔工具的工业标准」。 二者并非互斥——MCP 的实现里仍然可以用 Function Calling 去触发具体函数,但它把「怎么描述工具、怎么发现工具、怎么保持会话」这些事都标准化了,从而解决了 FC 带来的碎片化、难维护、难共享的问题 。

安装mcp

在mcp server市场查找自己想用的mcp服务,如Fetch MCP Server

复制mcp配置

1
2
3
4
5
6
7
8
9
10
{
"mcpServers": {
"fetch": {
"command": "uvx",
"args": [
"mcp-server-fetch"
]
}
}
}

在mcp host 中安装,如trae

host会自动完成对mcp的配置

创建一个mcp server

初始化项目

1
2
3
4
5
6
7
8
uv init weather

uv sync

source .venv/bin/activate

#添加依赖
uv add "mcp[cli]" httpx

创建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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# 导入类型提示模块,用于类型注解
from typing import Any

# 导入httpx库,用于发送HTTP请求
import httpx

# 从mcp.server.fastmcp模块导入FastMCP类
# FastMCP是一个快速构建MCP(Model Control Protocol)服务器的框架
from mcp.server.fastmcp import FastMCP

# 创建FastMCP实例,命名为"weather",日志级别设置为ERROR(只显示错误信息)
mcp = FastMCP("weather", log_level="ERROR")


# 常量定义
# NWS(National Weather Service)API的基础URL
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发起请求并处理错误。

Args:
url: 要请求的API URL

Returns:
成功时返回解析后的JSON数据字典,失败时返回None
"""
# 设置请求头信息
headers = {
"User-Agent": USER_AGENT, # 用户代理标识
"Accept": "application/geo+json" # 接受的数据格式
}

# 创建异步HTTP客户端
async with httpx.AsyncClient() as client:
try:
# 发起GET请求,设置超时时间为30秒
response = await client.get(url, headers=headers, timeout=30.0)
# 如果响应状态码不是2xx,抛出异常
response.raise_for_status()
# 返回解析后的JSON数据
return response.json()
except Exception:
# 捕获所有异常,返回None表示请求失败
return None


def format_alert(feature: dict) -> str:
"""将警报数据格式化为可读的字符串。

Args:
feature: 包含警报信息的字典

Returns:
格式化后的警报字符串
"""
# 获取警报属性
props = feature["properties"]
# 格式化警报信息,使用get方法提供默认值防止键不存在
return f"""
事件: {props.get('event', '未知')}
区域: {props.get('areaDesc', '未知')}
严重程度: {props.get('severity', '未知')}
描述: {props.get('description', '无描述信息')}
指示: {props.get('instruction', '无具体指示')}
"""


# 使用@mcp.tool()装饰器将函数注册为MCP工具
@mcp.tool()
async def get_alerts(state: str) -> str:
"""获取指定美国州的天气警报。

Args:
state: 两个字母的美国州代码(例如:CA, NY)

Returns:
格式化后的警报信息字符串
"""
# 构建获取州警报的URL
url = f"{NWS_API_BASE}/alerts/active/area/{state}"
# 发起API请求获取数据
data = await make_nws_request(url)

# 检查数据是否有效
if not data or "features" not in data:
return "无法获取警报或未找到警报。"

# 检查是否有警报
if not data["features"]:
return "该州无活动警报。"

# 格式化所有警报
alerts = [format_alert(feature) for feature in data["features"]]
# 用分隔符连接所有警报
return "\n---\n".join(alerts)


# 注册为MCP工具的天气预报函数
@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:
"""获取指定位置的天气预报。

Args:
latitude: 位置的纬度
longitude: 位置的经度

Returns:
格式化后的天气预报字符串
"""
# 首先获取预报网格端点
points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"
points_data = await make_nws_request(points_url)

# 检查点数据是否获取成功
if not points_data:
return "无法获取此位置的预报数据。"

# 从点响应中获取预报URL
forecast_url = points_data["properties"]["forecast"]
forecast_data = await make_nws_request(forecast_url)

# 检查预报数据是否获取成功
if not forecast_data:
return "无法获取详细预报。"

# 将时间段格式化为可读的预报
periods = forecast_data["properties"]["periods"]
forecasts = []
# 只显示接下来的5个时间段
for period in periods[:5]:
forecast = f"""
{period['name']}:
温度: {period['temperature']}°{period['temperatureUnit']}
风力: {period['windSpeed']} {period['windDirection']}
预报: {period['detailedForecast']}
"""
forecasts.append(forecast)

# 用分隔符连接所有预报
return "\n---\n".join(forecasts)


# 程序入口点
if __name__ == "__main__":
# 初始化并运行服务器,使用stdio传输方式
mcp.run(transport='stdio')

@mcp.tool()可以将函数内的字符串,参数类型等信息传给大模型,以供大模型决定何时调用这个tool

mcp.run(transport=‘stdio’)说明mcp server和host的传输方式是输入和输出

mcp server 配置信息

1
2
3
4
5
6
7
8
9
10
11
12
"weather": {
"disabled": false,
"timeout": 60,
"command": "uv",
"args": [
"--directory",
"/Users/joeygreen/PycharmProjects/weather",
"run",
"weather.py"
],
"transportType": "stdio"
}

“disabled”: false表示该服务是否被禁用。false 表示该服务是启用状态,可以正常运行。

“timeout”: 60设置该服务的超时时间,单位为秒。

“command”: “uv”指定执行该服务时使用的命令。

“args”出了执行 command 时需要传递的参数。

“transportType”: “stdio”指定服务的通信方式。stdio 表示标准输入输出流(Standard Input Output),通常用于进程间通信。

解析mcp server与host的通信

image-20250727154739013

输入为host对server发送,输出为server对host发送,以下将列举几个重要的说明

输入中:method字段为host告诉server接下来要干什么,如初始化 (Initialization)通知已初始化 (Notification)查询可用工具 (Listing Tools)调用工具 (Calling a Tool)

protocolVersion说明了mcp使用的协议版本

以下见server返回的tool信息,其中的一个参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"name": "get_forecast",
"description": "Get weather forecast for a location.\n\nArgs:\n latitude: Latitude of the location\n longitude: Longitude of the location\n",
"inputSchema": {
"properties": {
"latitude": {
"title": "Latitude",
"type": "number"
},
"longitude": {
"title": "Longitude",
"type": "number"
}
},
"required": [
"latitude",
"longitude"
],
"title": "get_forecastArguments",
"type": "object"
}
}

可以和定义的函数对比学习

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
33
34
35
36
37
38
39
40
41
42
43
# 注册为MCP工具的天气预报函数
@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:
"""获取指定位置的天气预报。

Args:
latitude: 位置的纬度
longitude: 位置的经度

Returns:
格式化后的天气预报字符串
"""
# 首先获取预报网格端点
points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"
points_data = await make_nws_request(points_url)

# 检查点数据是否获取成功
if not points_data:
return "无法获取此位置的预报数据。"

# 从点响应中获取预报URL
forecast_url = points_data["properties"]["forecast"]
forecast_data = await make_nws_request(forecast_url)

# 检查预报数据是否获取成功
if not forecast_data:
return "无法获取详细预报。"

# 将时间段格式化为可读的预报
periods = forecast_data["properties"]["periods"]
forecasts = []
# 只显示接下来的5个时间段
for period in periods[:5]:
forecast = f"""
{period['name']}:
温度: {period['temperature']}°{period['temperatureUnit']}
风力: {period['windSpeed']} {period['windDirection']}
预报: {period['detailedForecast']}
"""
forecasts.append(forecast)

# 用分隔符连接所有预报
return "\n---\n".join(forecasts)

description内容即为我们在定义这个tool的时候写的文档字符串(Documentation String),通常简称为 docstring

inputSchema 是在MCP(Model Control Protocol)中用来描述工具(tool)所需参数的结构和类型的规范。它本质上是一个JSON Schema。

JSON Schema 是一个用于描述和验证 JSON 数据结构的规范。你可以把它理解为 JSON 数据的“蓝图”或“模板”。

required指明哪些参数是必需的,哪些是可选的。

理解mcp的本质

以上内容皆是server与host直接的交互,本质上可以理解成host对server提供的工具进行注册与使用。这其中并不涉及到host与大模型的交互,也就是大模型是如何使用host提供的信息。实际上不同的mcp host与模型的交互协议也不同,如cline使用的是xml格式;cherry studio使用的则是fuction calling

再看mcp的全称Model Context Protocol,模型上下文协议,也就是mcp增加模型的扩展性,使他可以获取更多信息,而server就是为模型提供更多信息的工具

mcp host与模型的交互

使用中转服务器截获日志

image-20250727163811531

以下为cline发送给模型的请求

image-20250727164527797

messages包含了系统提示词与用户输入

先来看系统提示词,cline提供的提示词包括工具使用格式,工具信息,工具使用方法等。这里重点说一下,cline的工具使用格式xml

结构如下:

1
2
3
4
5
<tool_name>
<parameter1_name>value1</parameter1_name>
<parameter2_name>value2</parameter2_name>
...
</tool_name>

例如:

1
2
3
<read_file>
src/main.js
</read_file>

再举个例子

use_mcp_tool 描述:请求使用由连接的 MCP 服务器提供的工具。每个 MCP 服务器可以提供具有不同功能的多个工具。工具有定义的输入模式,用于指定必需和可选参数。 参数:

server_name: (必需) 提供该工具的 MCP 服务器的名称 tool_name: (必需) 要执行的工具的名称 arguments: (必需) 一个 JSON 对象,包含工具的输入参数,遵循工具的输入模式 用法:

1
2
3
4
5
6
7
8
9
10
<use_mcp_tool>
<server_name>服务器名称在此</server_name>
<tool_name>工具名称在此</tool_name>

{
"param1": "value1",
"param2": "value2"
}

</use_mcp_tool>

模型返回响应如下

image-20250727174504506

sse连接,流式输出

SSE 是一种基于标准 HTTP只允许服务器向客户端单向推送文本流的实时通信技术,浏览器原生支持,自动重连,常用于AI 流式回答实时日志股价/监控推送等场景。

即客户端发送一次请求,连续接受多次响应直到结束

mcp的三种传输协议

协议名称 通信方式 适用场景 优势 局限
Stdio(标准输入输出) 使用进程的标准输入(stdin)和标准输出(stdout)进行本地通信,基于 JSON-RPC 2.0 格式 本地开发、调试、IDE插件、命令行工具 简单易实现、跨平台、低延迟 仅支持本地通信,无法跨网络,低并发
SSE(Server-Sent Events) 客户端通过 HTTP POST 发送请求,服务器通过 SSE 单向推送流式响应 实时监控、新闻推送、远程服务调用 基于 HTTP,浏览器友好,支持流式数据 仅支持单向通信,MCP官方已标记为“即将废弃”
Streamable HTTP(新型流式HTTP) 支持双向流式通信的现代 HTTP 协议,替代 SSE,支持会话恢复、OAuth 认证等 分布式系统、高并发、双向实时交互 双向通信、高性能、企业级安全机制 实现较复杂,生态仍在发展中

ReAct

ReAct 是一种用于增强大型语言模型(LLMs)推理和行动能力的技术框架,它通过结合“推理”(Reasoning)和“行动”(Acting)来提升模型处理复杂任务的能力。

其工作流程通常包括以下几个步骤:

  1. 思考(Reasoning):模型对当前问题进行分析,思考下一步需要采取的行动。
  2. 行动(Acting):模型决定调用哪些工具或函数,并提供必要的参数。
  3. 观察(Observation):工具执行后返回结果,模型对结果进行观察。
  4. 响应(Response):根据观察结果,模型生成最终的用户响应。

实际应用上就是告诉大模型用ReAct这种模式来思考

参考资料

MCP终极指南 - 从原理到实战,带你深入掌握MCP(基础篇)_哔哩哔哩_bilibili