深入理解 Model Context Protocol (MCP):完整指南
- 深入理解 Model Context Protocol (MCP):完整指南
深入理解 Model Context Protocol (MCP):完整指南
本文全面介绍 MCP 协议:从基础概念、架构设计、通信机制,到 Python/Go 实战代码和生产环境最佳实践。
目录
一、MCP 是什么?
1.1 核心概念
2024 年 11 月,Anthropic 发布了 Model Context Protocol (MCP),这是一个开放标准协议,用于在 AI 应用和外部数据源之间建立安全的双向连接。
形象比喻:Anthropic 将 MCP 比作 "AI 的 USB-C 接口"
- USB-C 提供了连接设备与外设的标准方式
- MCP 提供了连接 AI 模型与数据源的标准方式
1.2 解决的核心问题
M × N 问题:
传统方式:
- M 个 AI 应用
- N 个数据源
- 需要 M × N 个自定义集成 ❌
MCP 方式:
- M 个应用实现 MCP 客户端
- N 个数据源实现 MCP 服务器
- 任意组合无缝工作 ✅
1.3 应用场景
- 🗂️ 业务工具集成:Google Drive、Slack、GitHub
- 🗄️ 数据库访问:PostgreSQL、MongoDB、MySQL
- 🌐 API 服务:天气、地图、支付
- 📁 文件系统:本地文件读写
- 💻 开发工具:IDE、代码编辑器
二、架构与机制
2.1 三层架构
┌─────────────────────────────────────────┐
│ Host (主机应用) │
│ (Claude Desktop, Cursor IDE) │
│ ┌───────────────────────────────────┐ │
│ │ MCP Client (客户端) │ │
│ │ - 管理连接 │ │
│ │ - 处理协议 │ │
│ │ - 路由请求 │ │
│ └──────────────┬────────────────────┘ │
└─────────────────┼──────────────────────┘
│ JSON-RPC 2.0
│ (stdio/HTTP/SSE)
▼
┌─────────────────────────────────────────┐
│ MCP Server (服务器) │
│ ┌──────────┐ ┌──────────┐ │
│ │Resources │ │ Tools │ │
│ │ (资源) │ │ (工具) │ │
│ └──────────┘ └──────────┘ │
│ ┌──────────┐ │
│ │ Prompts │ │
│ │(提示词) │ │
│ └──────────┘ │
└─────────────────────────────────────────┘
2.2 三大核心能力
📚 Resources (资源)
- 类比:REST API 的 GET 端点
- 用途:提供上下文数据
- 特点:只读、静态或动态
- 示例:
resource://files/report.pdf resource://database/users/{id} resource://api/weather/current
🔧 Tools (工具)
- 类比:REST API 的 POST 端点
- 用途:执行操作、产生副作用
- 特点:可执行、需用户批准
- 示例:发送邮件、创建文件、查询数据库
💬 Prompts (提示词)
- 用途:预定义的交互模板
- 特点:可重用、支持参数化
- 作用:引导 AI 对话
2.3 通信协议
JSON-RPC 2.0 消息格式
Request (请求):
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_weather",
"arguments": {"location": "Singapore"}
}
}
Response (响应):
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{"type": "text", "text": "Temperature: 28°C"}
]
}
}
传输层
传输方式 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
stdio | 本地工具 | 简单、安全、低延迟 | 仅本地、单客户端 |
HTTP+SSE | 远程服务 | 跨网络、多客户端 | 需配置、较复杂 |
三、MCP vs Function Calling
对比表格
维度 | Function Calling | MCP |
---|---|---|
标准化 | 供应商特定 | 开放标准 |
重用性 | 应用特定 | 跨应用重用 |
连接 | 无状态 | 有状态持久连接 |
能力 | 仅函数 | 工具+资源+提示词 |
发现 | 编译时 | 运行时 |
场景 | 简单临时 | 企业级可重用 |
形象类比
- Function Calling:把工具箱焊在车上(每辆车都要单独配)
- MCP:USB 接口(一个工具插入所有设备)
四、Hello World 实战
4.1 Python 实现
安装
uv init mcp-hello-python
cd mcp-hello-python
uv add mcp
完整代码 (server.py)
from mcp.server.fastmcp import FastMCP
import sys
import logging
# 配置日志(重要:输出到 stderr)
logging.basicConfig(
level=logging.INFO,
handlers=[logging.StreamHandler(sys.stderr)]
)
mcp = FastMCP("Hello MCP Server")
@mcp.tool()
def greet(name: str = "World") -> str:
"""向指定的人打招呼"""
return f"Hello, {name}! Welcome to MCP!"
@mcp.tool()
def add(a: int, b: int) -> int:
"""两数相加"""
return a + b
@mcp.resource("greeting://static")
def get_greeting() -> str:
"""静态问候语"""
return "This is a static greeting!"
@mcp.resource("greeting://dynamic/{name}")
def get_dynamic_greeting(name: str) -> str:
"""动态问候语"""
return f"Hello {name}, personalized greeting!"
if __name__ == "__main__":
mcp.run(transport="stdio")
Claude Desktop 配置
{
"mcpServers": {
"hello-python": {
"command": "uv",
"args": ["run", "/path/to/server.py"]
}
}
}
4.2 Go 实现
安装
mkdir mcp-hello-go && cd mcp-hello-go
go mod init mcp-hello-go
go get github.com/mark3labs/mcp-go
完整代码 (main.go)
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
func main() {
// 重要:日志输出到 stderr
log.SetOutput(os.Stderr)
s := server.NewMCPServer(
"Hello MCP Server (Go)",
"1.0.0",
server.WithToolCapabilities(false),
)
// 工具:打招呼
greetTool := mcp.NewTool(
"greet",
mcp.WithDescription("向指定的人打招呼"),
mcp.WithString("name", mcp.Required()),
)
s.AddTool(greetTool, greetHandler)
// 工具:加法
addTool := mcp.NewTool(
"add",
mcp.WithDescription("两数相加"),
mcp.WithNumber("a", mcp.Required()),
mcp.WithNumber("b", mcp.Required()),
)
s.AddTool(addTool, addHandler)
// 资源:静态
staticResource := mcp.NewResource(
"greeting://static",
"Static Greeting",
)
s.AddResource(staticResource, staticHandler)
log.Println("Starting MCP Server...")
if err := server.ServeStdio(s); err != nil {
log.Fatalf("Server error: %v", err)
}
}
func greetHandler(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
name, _ := req.RequireString("name")
return mcp.NewToolResultText(fmt.Sprintf("Hello, %s!", name)), nil
}
func addHandler(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
a, _ := req.RequireNumber("a")
b, _ := req.RequireNumber("b")
return mcp.NewToolResultText(fmt.Sprintf("%.0f", a+b)), nil
}
func staticHandler(ctx context.Context, req mcp.ReadResourceRequest) (string, error) {
return "Static greeting from Go!", nil
}
编译运行
go build -o mcp-hello-go
./mcp-hello-go # 测试
⚠️ 关键注意事项
stdio 服务器日志规则:
# ❌ 错误:会破坏 JSON-RPC 消息
print("Starting server...")
# ✅ 正确:输出到 stderr
import logging, sys
logging.basicConfig(handlers=[logging.StreamHandler(sys.stderr)])
logging.info("Starting server...")
Go 版本:
log.SetOutput(os.Stderr) // 必须设置
log.Println("Starting...")
五、调试与故障排查
5.1 常见问题
问题 1:服务器无法启动
排查步骤:
# 1. 检查配置路径
cat ~/Library/Application\ Support/Claude/claude_desktop_config.json
# 2. 查看日志
tail -f ~/Library/Logs/Claude/mcp*.log
# 3. 手动测试
uv run server.py # 应该等待输入,不立即退出
问题 2:工具调用失败
原因 A:参数类型不匹配
@mcp.tool()
def divide(a: int, b: int) -> float: # 明确类型
"""除法运算"""
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
原因 B:stdout 污染
# ❌ 错误
@mcp.tool()
def fetch(url: str) -> str:
print(f"Fetching {url}") # 破坏协议!
return requests.get(url).text
# ✅ 正确
import logging
@mcp.tool()
def fetch(url: str) -> str:
logging.info(f"Fetching {url}") # 使用 logging
return requests.get(url).text
5.2 调试技巧
结构化日志
import json
from datetime import datetime
def log_tool_call(tool_name, params, result=None, error=None):
entry = {
'timestamp': datetime.utcnow().isoformat(),
'tool': tool_name,
'params': params,
'success': error is None
}
if result: entry['result'] = str(result)
if error: entry['error'] = error
logging.info(json.dumps(entry))
@mcp.tool()
def calculate(op: str, a: float, b: float) -> float:
log_tool_call('calculate', {'op': op, 'a': a, 'b': b})
try:
result = a + b if op == 'add' else a - b
log_tool_call('calculate', {'op': op}, result=result)
return result
except Exception as e:
log_tool_call('calculate', {'op': op}, error=str(e))
raise
六、进阶主题
6.1 HTTP 服务器(支持多客户端)
Python + FastAPI
from mcp.server.fastmcp import FastMCP
from fastapi import FastAPI
import uvicorn
mcp = FastMCP("HTTP MCP Server")
app = FastAPI()
@mcp.tool()
def greet(name: str) -> str:
return f"Hello, {name}!"
app.mount("/mcp", mcp.get_asgi_app())
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
6.2 实战案例:系统监控服务器
from mcp.server.fastmcp import FastMCP
import psutil
import json
mcp = FastMCP("System Monitor")
@mcp.tool()
def get_cpu_info() -> str:
"""获取 CPU 使用率"""
cpu_percent = psutil.cpu_percent(interval=1)
cpu_count = psutil.cpu_count()
return f"CPU: {cpu_percent}% ({cpu_count} cores)"
@mcp.tool()
def get_memory_info() -> str:
"""获取内存信息"""
mem = psutil.virtual_memory()
return f"Memory: {mem.used/(1024**3):.2f}GB / {mem.total/(1024**3):.2f}GB ({mem.percent}%)"
@mcp.resource("system://status")
def get_status() -> str:
"""系统状态"""
return json.dumps({
'cpu': psutil.cpu_percent(),
'memory': psutil.virtual_memory().percent,
'disk': psutil.disk_usage('/').percent
})
if __name__ == "__main__":
mcp.run(transport="stdio")
七、最佳实践
7.1 安全考虑
import os
from pathlib import Path
def validate_path(file_path: str, allowed_dirs: list) -> bool:
"""验证文件路径安全"""
normalized = os.path.abspath(file_path)
# 防止路径遍历
if '..' in file_path:
return False
# 检查是否在允许目录中
return any(
normalized.startswith(os.path.abspath(d))
for d in allowed_dirs
)
@mcp.tool()
def read_file(path: str) -> str:
"""安全读取文件"""
ALLOWED = ["/Users/username/Documents"]
if not validate_path(path, ALLOWED):
return "Error: Access denied"
if os.path.getsize(path) > 10 * 1024 * 1024:
return "Error: File too large (max 10MB)"
try:
with open(path, 'r') as f:
return f.read()
except Exception:
return "Error: Failed to read file"
7.2 性能优化
from functools import lru_cache
import asyncio
# 1. 使用缓存
@lru_cache(maxsize=128)
def expensive_calc(data: str) -> str:
# 耗时计算
return process(data)
# 2. 异步批量处理
@mcp.tool()
async def batch_process(files: list) -> str:
async def process_one(f):
return {"file": f, "status": "done"}
results = await asyncio.gather(
*[process_one(f) for f in files]
)
return json.dumps(results)
# 3. 超时控制
from contextlib import contextmanager
import signal
@contextmanager
def timeout(seconds):
def handler(signum, frame):
raise TimeoutError()
signal.signal(signal.SIGALRM, handler)
signal.alarm(seconds)
try:
yield
finally:
signal.alarm(0)
@mcp.tool()
def long_task(data: str) -> str:
try:
with timeout(30):
return process(data)
except TimeoutError:
return "Error: Timeout"
7.3 测试策略
import pytest
@pytest.fixture
def mcp_server():
mcp = FastMCP("Test")
@mcp.tool()
def add(a: int, b: int) -> int:
return a + b
return mcp
@pytest.mark.asyncio
async def test_add_tool(mcp_server):
result = await mcp_server.call_tool("add", {"a": 2, "b": 3})
assert result == 5
八、生态系统
8.1 官方资源
- 文档:https://docs.claude.com/en/docs/mcp
- 规范:https://modelcontextprotocol.io
- Python SDK:https://github.com/modelcontextprotocol/python-sdk
- Go SDK:https://github.com/modelcontextprotocol/go-sdk
8.2 社区资源
第三方 SDK:
- mark3labs/mcp-go
- metoro-io/mcp-golang
- FastMCP (Python 高级框架)
支持 MCP 的应用:
- Claude Desktop
- Cursor IDE
- Zed Editor
- Codeium
- Sourcegraph Cody
8.3 官方服务器示例
- filesystem:文件系统访问
- github:GitHub 集成
- google-drive:Google Drive
- slack:Slack 集成
- postgres:PostgreSQL 数据库
- puppeteer:浏览器自动化
九、常见问题
Q1: MCP 和 RAG 有什么关系?
MCP 提供标准化的数据访问接口,RAG 利用这些接口检索知识。MCP 是基础设施,RAG 是应用模式。
Q2: 一个服务器能同时服务多个客户端吗?
stdio:不能(1对1)
HTTP/SSE:可以(1对多)
Q3: MCP 适合生产环境吗?
是的,但需要:
- 完善错误处理
- 添加监控日志
- 安全加固
- 性能测试
Q4: 如何调试连接问题?
- 查看日志文件
- 手动测试服务器
- 使用
mcp dev
工具- 检查 JSON-RPC 格式
- 验证配置路径
十、总结
核心要点
- MCP 是什么:AI 的 USB-C 接口,标准化的连接协议
- 三大能力:Resources(数据)、Tools(操作)、Prompts(模板)
- vs Function Calling:标准化、可重用、生态系统
- 实现要点:注意 stdio 日志规则,重视安全和错误处理
开始你的 MCP 之旅
- 从简单开始:实现 Hello World
- 解决实际问题:构建针对性服务器
- 贡献社区:开源你的实现
- 持续学习:关注规范更新
参考资源
希望这篇指南能帮助你全面掌握 MCP!🚀