【MCP 快速入门指南】—— 从原理到实战,带你深入掌握MCP(进阶篇)
前言
本文并非单纯的理论讲解,而是一份“实战+原理”双驱动的完整指南,专为希望快速掌握 MCP 协议、搭建可落地 MCP 服务的开发者打造。我们将从最基础的环境准备入手,手把手教你搭建一个可运行的 Weather MCP Server,再通过“中间人日志”拆解 MCP 协议的底层通信细节,带你理解 Client 与 Server 之间的交互逻辑,甚至教会你绕过第三方客户端,直接与 MCP Server 进行通信。
第一部分:创建你的第一个 MCP Server
1.1 环境准备
必需工具
| 工具 | 用途 | 安装说明 |
|---|---|---|
| Python | MCP Server 运行环境 | macOS 自带;Windows 需从官网 File 下载 |
| uv | Python 包管理器 | 用于项目和依赖管理 |
| VS Code | 代码编辑器 | 开发 MCP Server |
| Claude Client | VS Code 插件 | MCP Host,用于测试 MCP Server |
版本要求:Python >= 3.10
1.2 创建 Weather MCP Server
步骤整体流程图
开始创建项目目录初始化虚拟环境安装依赖包编写 weather.py 配置 MCP Client 测试运行完成
1.2.1 项目初始化
# 创建项目
uv init weather
# 进入项目目录
cd weather
# 创建虚拟环境
uv venv
# 激活虚拟环境
source .venv/bin/activate # macOS/Linux
# 或
.venv\Scripts\activate # Windows
# 安装依赖
uv add mcp httpx
依赖说明:
mcp- MCP 协议核心库httpx- HTTP 客户端,用于调用天气 API
1.2.2 编写 weather.py
创建 weather.py 文件,完整代码如下:
# 1. 导入必要的库
from mcp import FastMCP
import httpx
from typing import Any
# 2. 创建 MCP Server 实例
# ⚠️ 重要:添加 log_level="error" 防止日志干扰通信
mcp = FastMCP("weather", log_level="error")
# 3. 定义常量
NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-app/1.0"
# 4. 工具函数:HTTP 请求封装
async def make_nws_request(url: str) -> dict[str, Any] | None:
"""向美国国家气象局 API 发起请求"""
headers = {"User-Agent": USER_AGENT}
async with httpx.AsyncClient() as client:
response = await client.get(url, headers=headers, timeout=30.0)
response.raise_for_status()
return response.json()
# 5. 工具函数:格式化预警信息
def format_alert(alert: dict[str, Any]) -> str:
"""格式化单条预警信息"""
properties = alert.get("properties", {})
return f"""
事件: {properties.get('event', 'Unknown')}
区域: {properties.get('areaDesc', 'Unknown')}
严重性: {properties.get('severity', 'Unknown')}
描述: {properties.get('description', 'No description available')}
指示: {properties.get('instruction', 'No instructions available')}
""".strip()
# 6. Tool 1: 获取天气预警
@mcp.tool()
async def get_alerts(state: str) -> str:
"""
获取美国某个州的天气预警信息
Args:
state: 美国州代码(两个字母,如 CA, NY, TX)
Returns:
该州的天气预警信息
"""
url = f"{NWS_API_BASE}/alerts?area={state}"
data = await make_nws_request(url)
if not data:
return f"无法获取 {state} 的预警信息"
features = data.get("features", [])
if not features:
return f"{state} 当前没有天气预警"
# 格式化所有预警
alerts = [format_alert(alert) for alert in features[:5]] # 最多5条
return "\n\n---\n\n".join(alerts)
# 7. Tool 2: 获取天气预报
@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 f"无法获取坐标 ({latitude}, {longitude}) 的信息"
# 第二步:从返回数据中提取预报 URL
forecast_url = points_data.get("properties", {}).get("forecast")
if not forecast_url:
return "无法获取预报 URL"
# 第三步:请求天气预报
forecast_data = await make_nws_request(forecast_url)
if not forecast_data:
return "无法获取天气预报"
# 格式化预报信息
periods = forecast_data.get("properties", {}).get("periods", [])
if not periods:
return "没有可用的预报数据"
# 格式化输出
forecasts = []
for period in periods[:5]: # 最多5个时段
forecasts.append(f"""
{period.get('name', 'Unknown')}:
温度: {period.get('temperature', 'N/A')}°{period.get('temperatureUnit', 'F')}
天气: {period.get('shortForecast', 'N/A')}
详情: {period.get('detailedForecast', 'N/A')}
""".strip())
return "\n\n".join(forecasts)
# 8. 启动 MCP Server
if __name__ == "__main__":
# stdio 是默认传输方式,可省略 transport 参数
mcp.run()
1.2.3 代码关键点解析
📌 装饰器 @mcp.tool()
这个装饰器做了三件事:
- 注册函数为 Tool - 让 MCP Server 知道这个函数可以被调用
- 提取函数注释 - 将 docstring 转换为 Tool 描述
- 生成参数规范 - 根据类型注解生成 JSON Schema
示例:
@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:
"""获取指定坐标的天气预报"""
pass
会被转换为:
{
"name": "get_forecast",
"description": "获取指定坐标的天气预报",
"inputSchema": {
"type": "object",
"properties": {
"latitude": { "type": "number" },
"longitude": { "type": "number" }
},
"required": ["latitude", "longitude"]
}
}
📌 Transport: stdio
mcp.run()
stdio= Standard Input/Output(标准输入输出),是默认传输- MCP Server 通过 stdin 接收请求,通过 stdout 发送响应
- 这是目前最常用、最稳定的 MCP 通信方式
1.3 配置 Claude Client
配置步骤
配置文件内容
打开 Claude Client 配置(通常在 ~/.config/claude/config.json),添加:
{
"mcpServers": {
"weather": {
"command": "uv",
"args": ["run", "weather.py"],
"cwd": "/your/path/to/weather",
"env": {}
}
}
}
配置说明:
command: 启动命令(使用 uv 运行)args: 命令参数cwd: 工作目录(必须是绝对路径)env: 环境变量(可选)
1.4 测试运行
- 保存配置 - Claude Client 会自动重启并加载 MCP Server
- 新建对话 - 在 Claude Client 中创建新对话
- 测试查询:
纽约明天的天气怎么样?
- 观察结果
- Claude 会识别到
get_forecast工具 - 提示是否批准调用
- 点击 “Approve” 后执行
- 返回天气预报数据
成功标志
✅ 看到 Tool 调用提示
✅ 参数自动提取(纬度: 40.7128, 经度: -74.006)
✅ 返回天气预报数据
✅ Claude 总结并展示结果
第二部分:MCP 协议底层分析
2.1 问题:如何看到协议通信?
虽然我们成功创建了 MCP Server,但有个问题:
我们并不清楚 Client 和 Server 之间到底交换了什么数据?
- MCP Server 是由 Claude Client 启动的
- 它们的通信通过 stdin/stdout 进行
- 我们无法直接看到这些数据流
2.2 解决方案:MCP Logger 中间人
架构设计
核心思路:
- 在 Client 和 Server 之间插入一个 中间人脚本
- 这个脚本透明转发所有数据
- 同时将数据记录到日志文件
mcp_logger.py 实现
#!/usr/bin/env python3
"""
MCP Logger - 截获并记录 MCP 协议通信
"""
import sys
import subprocess
import threading
import json
LOG_FILE = "mcp.log"
def log_message(direction: str, data: str):
"""记录消息到日志文件"""
with open(LOG_FILE, "a", encoding="utf-8") as f:
f.write(f"{direction}: {data}\n")
def forward_stream(input_stream, output_stream, direction: str):
"""转发数据流并记录"""
try:
for line in input_stream:
log_message(direction, line.strip())
output_stream.write(line)
output_stream.flush()
except Exception as e:
log_message("ERROR", str(e))
def main():
server_command = sys.argv[1:]
process = subprocess.Popen(
server_command,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
universal_newlines=True,
bufsize=1
)
input_thread = threading.Thread(
target=forward_stream,
args=(sys.stdin, process.stdin, "输入")
)
output_thread = threading.Thread(
target=forward_stream,
args=(process.stdout, sys.stdout, "输出")
)
input_thread.start()
output_thread.start()
process.wait()
input_thread.join()
output_thread.join()
if __name__ == "__main__":
main()
使用方法
修改 Claude Client 配置:
{
"mcpServers": {
"weather": {
"command": "python",
"args": ["/path/to/mcp_logger.py", "uv", "run", "weather.py"],
"cwd": "/path/to/weather"
}
}
}
2.3 协议通信完整解析
启动 Server 后,查看 mcp.log 文件,我们会看到以下交互过程:
阶段 1:握手(Handshake)
① Client 发送初始化请求
输入: {
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-03-07",
"clientInfo": {
"name": "claude-client",
"version": "3.12.3"
},
"capabilities": {}
}
}
② Server 响应初始化
输出: {
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-03-07",
"serverInfo": {
"name": "weather",
"version": "1.0.0"
},
"capabilities": {
"tools": {}
}
}
}
③ Client 确认
输入: {
"jsonrpc": "2.0",
"method": "notifications/initialized"
}
阶段 2:工具发现(Tool Discovery)
④ Client 请求工具列表
输入: {
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list"
}
⑤ Server 返回工具定义
输出: {
"jsonrpc": "2.0",
"id": 2,
"result": {
"tools": [
{
"name": "get_alerts",
"description": "获取美国某个州的天气预警信息\n\nArgs:\n state: 美国州代码(两个字母,如 CA, NY, TX)",
"inputSchema": {
"type": "object",
"properties": {
"state": {
"type": "string"
}
},
"required": ["state"]
}
},
{
"name": "get_forecast",
"description": "获取指定坐标的天气预报\n\nArgs:\n latitude: 纬度\n longitude: 经度",
"inputSchema": {
"type": "object",
"properties": {
"latitude": {
"type": "number"},
"longitude": {
"type": "number"}
},
"required": ["latitude", "longitude"]
}
}
]
}
}
🔍 深入理解:JSON Schema
inputSchema 遵循 JSON Schema 规范,用于描述参数结构:
{
"type": "object",
"properties": {
"latitude": { "type": "number" },
"longitude": { "type": "number" }
},
"required": ["latitude", "longitude"]
}
含义:
- 参数是一个对象
- 必须包含
latitude和longitude两个字段 - 两个字段都是数字类型
- 两个字段都是必需的
阶段 3:工具调用(Tool Call)
⑥ Client 调用工具
输入: {
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "get_forecast",
"arguments": {
"latitude": 40.7128,
"longitude": -74.006
}
}
}
⑦ Server 返回结果
输出: {
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "Tonight:\n温度: 45°F\n天气: Partly Cloudy\n详情: Partly cloudy, with a low around 45...\n\nFriday:\n温度: 58°F\n天气: Sunny\n详情: Sunny, with a high near 58..."
}
]
}
}
2.4 协议通信时序图
2.5 关键发现
通过分析日志,我们发现了 MCP 协议的几个核心特点:
| 特点 | 说明 |
|---|---|
| 基于 JSON-RPC 2.0 | 所有消息都遵循 JSON-RPC 规范 |
| 请求 - 响应模式 | Client 主动请求,Server 响应 |
| 标准化格式 | Tool 定义使用 JSON Schema |
| 异步支持 | 支持异步函数调用 |
| 传输无关 | 可以使用 stdio、HTTP 等多种传输方式 |
第三部分:直接与 MCP Server 通信
3.1 不需要 MCP Host 也能通信
通过前面的协议分析,我们已经完全理解了 MCP 的通信格式。现在一个有趣的问题是:
我们能不能绕过 Claude Client,直接与 MCP Server 对话?
答案是:完全可以!
MCP Server 只是一个遵循协议规范的程序,它不关心对面是谁。只要你发送的消息符合 MCP 协议格式,它就会正常响应。
3.2 手动通信演示
启动 MCP Server
在终端中直接运行 MCP Server:
cd /path/to/weather
uv run weather.py
现在 Server 正在等待 stdin 输入。
重要提醒
手动输入必须使用单行压缩 JSON,不能换行,否则 Server 无法解析。
步骤 1:发送初始化请求
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-03-07",
"clientInfo": { "name": "手动测试", "version": "1.0.0" },
"capabilities": {}
}
}
Server 响应:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-03-07",
"serverInfo": { "name": "weather", "version": "1.0.0" },
"capabilities": { "tools": {} }
}
}
步骤 2:发送初始化完成通知
{ "jsonrpc": "2.0", "method": "notifications/initialized" }
(Server 不会响应此消息)
步骤 3:请求工具列表
{ "jsonrpc": "2.0", "id": 2, "method": "tools/list" }
步骤 4:调用工具
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "get_forecast",
"arguments": { "latitude": 40.7128, "longitude": -74.006 }
}
}
3.3 使用脚本自动化测试
创建 test_mcp.py 进行自动化测试:
import subprocess
import json
def send_message(process, message):
process.stdin.write(json.dumps(message) + '\n')
process.stdin.flush()
def read_response(process):
line = process.stdout.readline()
return json.loads(line)
# 启动 Server
process = subprocess.Popen(
['uv', 'run', 'weather.py'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
universal_newlines=True,
bufsize=1
)
# 1. 初始化
send_message(process, {
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-03-07",
"clientInfo": {"name": "test", "version": "1.0"},
"capabilities": {}
}
})
print("初始化响应:", read_response(process))
# 2. 初始化完成
send_message(process, {
"jsonrpc": "2.0",
"method": "notifications/initialized"
})
# 3. 获取工具列表
send_message(process, {
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list"
})
print("工具列表:", read_response(process))
# 4. 调用工具
send_message(process, {
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "get_forecast",
"arguments": {"latitude": 40.7128, "longitude": -74.006}
}
})
print("预报结果:", read_response(process))
process.terminate()
3.4 关键发现
通过手动通信实验,我们证明了:
| 发现 | 说明 |
|---|---|
| 协议独立性 | MCP 协议与具体的 Host 实现无关 |
| 纯文本协议 | 所有通信都是 JSON 文本,易于调试 |
| 会话式设计 | 必须按顺序完成握手才能正常调用 |
| 可编程性 | 可以用任何语言实现 MCP Server |
第四部分:MCP 协议本质解析
4.1 重新审视 MCP 协议的范围
在深入研究了 MCP 的底层通信后,我们需要回答一个核心问题:
MCP 协议到底规定了什么?它与 AI 模型的关系是什么?
4.2 MCP 协议的真实作用域
🎯 MCP 协议规定的内容
MCP 协议只规定了 Client 和 Server 之间的通信:
| 规定内容 | 说明 |
|---|---|
| 工具注册 | Server 如何告诉 Client 有哪些工具可用 |
| 工具定义 | 使用 JSON Schema 描述参数格式 |
| 工具调用 | Client 如何请求执行工具 |
| 结果返回 | Server 如何返回执行结果 |
| 传输协议 | 基于 JSON-RPC 2.0 |
| 传输方式 | stdio、HTTP、WebSocket 等 |
❌ MCP 协议不规定的内容
MCP 协议完全没有规定以下内容:
| 不规定的内容 | 说明 |
|---|---|
| 与模型的交互 | Host 如何把工具信息传给模型 |
| 工具选择逻辑 | 模型如何决定使用哪个工具 |
| 参数提取方式 | 模型如何从用户问题中提取参数 |
| 结果处理 | 模型如何总结工具返回的结果 |
4.3 “模型上下文协议” 名称解析
为什么叫 “模型上下文协议”?
模型(Model)
指 AI 大语言模型。
上下文(Context)
指模型可访问的外部环境与能力集合,包括工具、资源、提示模板,而非对话历史上下文。
协议(Protocol)
一套标准化的通信规范。
真正的含义
MCP 是一个让模型标准化感知与使用外部环境资源的协议
它统一了 AI 应用调用外部工具的方式,实现一次编写、随处调用。
4.4 MCP 在 AI 应用架构中的定位
4.5 核心要点总结
| 要点 | 说明 |
|---|---|
| MCP ≠ 模型交互协议 | MCP 只负责工具的注册、发现、调用 |
| MCP = 工具标准化 | 让任何工具都能以统一方式暴露给 AI 应用 |
| 与语言无关 | 可用 Python、Node.js、Go 等任何语言实现 |
| 与模型无关 | 适用于任何支持工具调用的 AI 模型 |
| 单 Client 架构 | 一个 Host 只有一个 Client,可连接多 Server |
总结与最佳实践
5.1 核心知识回顾
通过本文,我们从实战到原理全面掌握了 MCP 协议:
- 从零搭建可运行的 MCP Server
- 抓包分析完整协议通信流程
- 理解 MCP 在 AI 架构中的真实定位
- 掌握调试、手动调用、自动化测试方法
5.2 开发最佳实践
-
完整类型注解 + 清晰文档
让 AI 准确理解工具用途与参数含义。
-
控制日志输出
使用
log_level="error"避免干扰 stdio 通信。 -
绝对路径配置
Host 配置中 cwd 必须使用绝对路径。
-
输入校验与安全
防止路径穿越、非法调用等风险。
-
异步优先
提升并发与 IO 密集型工具性能。
5.3 常见问题与解决方案
- Server 启动失败:检查路径、Python 版本、依赖安装
- 工具不显示:检查
@mcp.tool()、docstring、类型注解 - 参数错误:优化描述与 JSON Schema
- 通信异常:检查是否有多余日志污染 stdio
5.4 进阶方向
- 多工具组合与复杂业务封装
- 资源(Resource)与提示模板(Prompt)使用
- 自定义 MCP Server开发
- HTTP / WebSocket 传输模式
- 安全、权限、多租户扩展
5.5 参考资源
官方
Python SDK(https://pypi.org/project/mcp/)是「便捷工具」,不是「强制要求」:它封装了 MCP 协议的核心逻辑(如 FastMCP 类、@mcp.tool() 装饰器),能帮开发者快速搭建符合规范的 MCP Server,减少重复编码,而非必须依赖的「外壳」。
本质逻辑:大模型只认「MCP 协议格式」,不认「开发工具 / 语言」—— 哪怕你不使用任何 SDK,手动编写代码实现 MCP 协议的握手、工具调用、结果返回逻辑,开发的 Server 也能正常与大模型通信。
结语
MCP 协议的本质很简单: 一个让 AI 应用标准化访问外部工具的统一协议。
MCP 本质就是 AI 应用(大模型客户端)与外部工具之间的标准化
通信中间层,目的是让不同开发的工具、不同大模型,能通过同一套规则互相通信。
它不依赖特定模型、不绑定特定客户端,真正实现了工具生态的跨平台、可移植、可扩展。
理解它,你就掌握了下一代 AI Agent 工具化的核心基础设施。
更多推荐



所有评论(0)