MCP协议工程实践:AI工具集成的USB-C
AI 导读
MCP协议工程实践:AI工具集成的USB-C 作者:Maurice | 灵阙学院 目录 诞生背景:为什么需要统一的工具协议 核心架构:三层模型与通信机制 三大原语:Resources、Tools、Prompts Python实现:用FastMCP快速构建服务端 TypeScript实现:官方SDK建服务端 生产级最佳实践 主流MCP Server生态全景 Claude...
MCP协议工程实践:AI工具集成的USB-C
作者:Maurice | 灵阙学院
目录
- 诞生背景:为什么需要统一的工具协议
- 核心架构:三层模型与通信机制
- 三大原语:Resources、Tools、Prompts
- Python实现:用FastMCP快速构建服务端
- TypeScript实现:官方SDK建服务端
- 生产级最佳实践
- 主流MCP Server生态全景
- Claude Code与Cursor中的配置实战
- MCP vs Function Calling:协议层 vs 调用层
- 未来展望:Agent-to-Agent与MCP Marketplace
1. 诞生背景
每个AI都在造自己的轮子
2023年到2024年初,AI集成领域有一个令人沮丧的现象:每家AI公司都在以自己的方式定义"如何让模型调用外部工具"。OpenAI有Function Calling,Anthropic有Tool Use,Gemini有Code Interpreter——规范不同、Schema格式不同、错误处理方式不同。
开发者每接入一个新的AI,就要重写一套适配层。一个读取数据库的工具,可能需要为Claude、GPT-4、Gemini分别写三份胶水代码。这不是工程,这是苦役。
传统方式(碎片化):
OpenAI App ──→ [自定义适配层A] ──→ 数据库工具
Claude App ──→ [自定义适配层B] ──→ 数据库工具
Gemini App ──→ [自定义适配层C] ──→ 数据库工具
3个AI = 3套代码 = 3倍维护成本
Anthropic的答案:一个统一协议
2024年11月,Anthropic发布了 Model Context Protocol(MCP),并将其开源。设计理念只有一句话:
"MCP is like USB-C for AI applications."
就像USB-C统一了充电、数据传输、视频输出,MCP要统一AI模型与外部工具/数据源的通信方式。一个工具实现一次,所有支持MCP的AI都能调用。
MCP方式(标准化):
OpenAI App ─┐
Claude App ─┼─→ [MCP协议层] ──→ MCP Server(数据库工具)
Gemini App ─┘
N个AI = 1套代码 = 1倍维护成本
截至2026年初,MCP已成为AI工具集成的事实标准:超过600个MCP Server在官方生态中注册,GitHub官方仓库获得66,000+星,OpenAI、Google、Microsoft等主流AI平台均宣布支持。
2. 核心架构
2.1 三层模型:Host / Client / Server
MCP采用严格的三层架构,每一层职责清晰、不可越级:
┌──────────────────────────────────────────────────────────────┐
│ HOST 层 │
│ (Claude Desktop / Cursor / 你的AI应用) │
│ │
│ ┌─────────────────────────────────────┐ │
│ │ MCP CLIENT │ │
│ │ · 管理与Server的1:1连接 │ │
│ │ · 发送请求、接收响应 │ │
│ │ · 处理能力协商(capabilities) │ │
│ └───────────────┬─────────────────────┘ │
└───────────────────┼──────────────────────────────────────────┘
│ MCP Protocol (JSON-RPC 2.0)
│
┌───────────────┼───────────────┐
│ │ │
▼ ▼ ▼
┌────────┐ ┌────────────┐ ┌──────────────┐
│ MCP │ │ MCP │ │ MCP │
│ Server │ │ Server │ │ Server │
│ (文件) │ │ (数据库) │ │ (GitHub) │
└────────┘ └────────────┘ └──────────────┘
| 层级 | 英文名 | 职责 | 典型代表 |
|---|---|---|---|
| Host | Host | 用户交互界面,承载AI模型,管理多个Client | Claude Desktop, Cursor, VS Code |
| 客户端 | MCP Client | 协议翻译,与单个Server保持1:1连接 | Host内置,不需单独部署 |
| 服务端 | MCP Server | 暴露具体能力(工具/数据/模板) | filesystem, github, postgres |
关键设计原则:一个Client只连接一个Server(1:1)。Host可以同时持有多个Client,从而聚合多个Server的能力。这个设计保证了隔离性——filesystem server的权限不会污染github server。
2.2 传输层:stdio / SSE / Streamable HTTP
MCP底层通信使用 JSON-RPC 2.0 格式。传输层目前有三种方式:
stdio(标准输入输出)
最简单的传输方式。Host进程启动Server子进程,通过stdin/stdout传递JSON消息。适合本地工具集成。
Host进程 ──stdin──▶ Server子进程
Host进程 ◀─stdout── Server子进程
优点:零网络配置,天然进程隔离,延迟极低。 适用场景:本地开发工具、Claude Desktop、Cursor等桌面应用。
HTTP + SSE(已废弃,向后兼容)
早期远程传输方案。客户端用HTTP POST发送请求,服务端用Server-Sent Events推送响应。问题是需要维护两个端点,复杂度高,已在2025年规范中废弃,但为向后兼容仍支持。
Streamable HTTP(当前推荐的远程传输)
2025年3月规范引入,取代HTTP+SSE成为标准远程传输方案。
Client ──HTTP POST /mcp──▶ Server
Client ◀──Chunked Response── Server
(可选内嵌SSE流)
优点:单端点设计,支持无状态部署(AWS Lambda、Cloudflare Workers),支持渐进式响应流,兼容CDN与负载均衡。
传输方式选型一览:
| 传输方式 | 适用场景 | 状态 | 云原生友好度 |
|---|---|---|---|
| stdio | 本地工具 / 桌面应用 | 稳定 | 不适用 |
| HTTP+SSE | 旧版兼容 | 废弃(仍支持) | 中 |
| Streamable HTTP | 远程服务 / SaaS | 当前推荐 | 高 |
3. 三大原语
MCP定义了三种核心抽象——Resources(资源)、Tools(工具)、Prompts(提示模板)。理解这三个原语,就理解了MCP能做什么。
3.1 Resources(资源):给AI看的数据
Resources是只读的数据暴露机制。每个Resource有唯一URI,客户端可以订阅并在数据变化时收到通知。
资源URI示例:
file:///home/user/report.pdf # 本地文件
postgres://localhost/mydb/users # 数据库表
github://owner/repo/README.md # GitHub文件
memory://session/context # 会话上下文
Resources不是工具调用——它们是静态或准静态的上下文注入。AI模型读取Resource来"了解情况",而不是执行操作。
3.2 Tools(工具):AI能做的事
Tools是可调用的函数,是MCP中最核心的原语。AI模型决定何时调用哪个工具,MCP协议负责路由执行,Server负责实际执行并返回结果。
工具调用流程:
用户: "查询orders表里昨天的订单数量"
│
▼
AI模型分析意图
│
▼
调用工具: execute_sql
参数: { "query": "SELECT COUNT(*) FROM orders WHERE date = '2026-02-26'" }
│
▼
MCP Server执行SQL
│
▼
返回结果: { "count": 1428 }
│
▼
AI模型组织自然语言回答
每个Tool需要定义:名称、描述(供AI理解用途)、输入Schema(JSON Schema格式)、输出类型。
3.3 Prompts(提示模板):可复用的交互范式
Prompts是参数化的提示词模板,让用户(或AI)以标准化方式触发复杂工作流。与Resources和Tools不同,Prompts是面向"工作流编排"的设计。
# 示例:一个代码审查提示模板
@mcp.prompt()
def code_review(language: str, code: str, focus: str = "security") -> str:
return f"""
请对以下{language}代码进行{focus}方向的审查:
```{language}
{code}
```
请从以下维度分析:
1. {focus}风险点
2. 潜在的边界情况
3. 具体改进建议(附代码示例)
"""
三大原语对比:
| 原语 | 方向 | 谁发起 | 典型用途 |
|---|---|---|---|
| Resources | Server→Client(推) | 订阅/拉取 | 注入背景知识、实时数据 |
| Tools | Client→Server(拉) | AI模型决策 | 执行操作、查询、计算 |
| Prompts | Server→Client(推) | 用户选择 | 工作流模板、交互范式 |
4. Python实现
4.1 环境安装
pip install fastmcp
# 或使用官方SDK(FastMCP已并入官方SDK)
pip install mcp
4.2 完整MCP Server示例
下面是一个生产可用的MCP Server,集成了数据库查询工具和系统状态资源:
"""
production_mcp_server.py
一个生产级MCP Server示例,包含Tool、Resource和Prompt
"""
import asyncio
import json
from datetime import datetime
from typing import Any
import httpx
from mcp.server.fastmcp import FastMCP
from mcp.server.fastmcp import Context
# 初始化FastMCP,name是Server的标识名
mcp = FastMCP(
name="production-analytics-server",
description="企业数据分析MCP Server,提供订单查询、报表生成等能力",
)
# ─────────────────────────────────────────────
# TOOLS:可执行的函数
# ─────────────────────────────────────────────
@mcp.tool()
async def query_orders(
start_date: str,
end_date: str,
status: str = "all",
limit: int = 100,
) -> dict[str, Any]:
"""
查询指定日期范围内的订单数据。
Args:
start_date: 开始日期,格式YYYY-MM-DD
end_date: 结束日期,格式YYYY-MM-DD
status: 订单状态,可选 all/pending/completed/cancelled
limit: 最大返回条数,默认100,最大1000
Returns:
包含订单列表和统计摘要的字典
"""
# 输入验证(原子性保障)
if limit > 1000:
limit = 1000
if status not in ("all", "pending", "completed", "cancelled"):
return {
"error": f"无效的status值: {status}",
"valid_values": ["all", "pending", "completed", "cancelled"],
}
# 实际项目中替换为真实数据库查询
# result = await db.execute(
# "SELECT * FROM orders WHERE date BETWEEN $1 AND $2",
# start_date, end_date
# )
# 示例返回结构
return {
"total": 1428,
"filtered": 256,
"orders": [
{"id": "ORD-20260226-001", "amount": 1280.00, "status": "completed"},
{"id": "ORD-20260226-002", "amount": 350.50, "status": "pending"},
],
"summary": {
"total_amount": 328960.00,
"avg_amount": 1285.00,
"query_time_ms": 42,
},
"queried_at": datetime.now().isoformat(),
}
@mcp.tool()
async def generate_report(
report_type: str,
period: str,
format: str = "json",
ctx: Context = None,
) -> str:
"""
生成业务报表,支持长时间运行的异步任务。
Args:
report_type: 报表类型,可选 sales/inventory/finance
period: 统计周期,格式 YYYY-MM 或 YYYY-Q1
format: 输出格式,可选 json/csv/markdown
"""
valid_types = ("sales", "inventory", "finance")
if report_type not in valid_types:
return f"ERROR: 不支持的报表类型 '{report_type}',有效值: {valid_types}"
# 上报进度(利用MCP的进度通知机制)
if ctx:
await ctx.report_progress(0, 100, "开始生成报表...")
# 模拟耗时操作(实际替换为真实报表生成逻辑)
await asyncio.sleep(0.1)
if ctx:
await ctx.report_progress(50, 100, "数据聚合中...")
await asyncio.sleep(0.1)
if ctx:
await ctx.report_progress(100, 100, "报表生成完成")
report_data = {
"type": report_type,
"period": period,
"generated_at": datetime.now().isoformat(),
"data": {"revenue": 2850000, "orders": 1428, "avg_order_value": 1996},
}
if format == "csv":
return "type,period,revenue,orders\n" + \
f"{report_type},{period},2850000,1428"
elif format == "markdown":
return f"## {report_type}报表 ({period})\n\n" + \
f"- 营收: ¥2,850,000\n- 订单量: 1,428\n- 客单价: ¥1,996"
else:
return json.dumps(report_data, ensure_ascii=False, indent=2)
# ─────────────────────────────────────────────
# RESOURCES:只读数据暴露
# ─────────────────────────────────────────────
@mcp.resource("system://status")
async def system_status() -> str:
"""暴露系统实时健康状态"""
return json.dumps({
"status": "healthy",
"version": "2.1.0",
"uptime_hours": 720,
"db_connection": "ok",
"cache_hit_rate": "94.2%",
"timestamp": datetime.now().isoformat(),
}, ensure_ascii=False)
@mcp.resource("config://feature-flags")
async def feature_flags() -> str:
"""暴露当前功能开关配置"""
return json.dumps({
"new_dashboard": True,
"beta_reports": False,
"ai_suggestions": True,
"export_excel": True,
}, ensure_ascii=False)
# ─────────────────────────────────────────────
# PROMPTS:可复用的提示模板
# ─────────────────────────────────────────────
@mcp.prompt()
def analyze_sales_trend(period: str, focus_metric: str = "revenue") -> str:
"""销售趋势分析提示模板"""
return f"""
请分析{period}的销售趋势,重点关注{focus_metric}指标。
分析框架:
1. 同比/环比变化(附具体数字)
2. 关键驱动因素(正向/负向各2-3条)
3. 异常点识别(超出±20%均值的数据点)
4. 下一步行动建议(可执行,优先级排序)
请先调用 query_orders 工具获取原始数据,再进行分析。
"""
# ─────────────────────────────────────────────
# 启动入口
# ─────────────────────────────────────────────
if __name__ == "__main__":
# 本地开发:stdio传输
mcp.run()
# 生产部署:Streamable HTTP传输
# mcp.run(transport="streamable-http", host="0.0.0.0", port=8080)
4.3 测试与调试
# 方式1:MCP Inspector(官方调试工具,推荐)
npx @modelcontextprotocol/inspector python production_mcp_server.py
# 方式2:命令行直接调用
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | python production_mcp_server.py
# 方式3:FastMCP内置测试客户端
python -c "
import asyncio
from mcp.client.stdio import stdio_client
from mcp import ClientSession, StdioServerParameters
async def test():
params = StdioServerParameters(command='python', args=['production_mcp_server.py'])
async with stdio_client(params) as (r, w):
async with ClientSession(r, w) as session:
await session.initialize()
tools = await session.list_tools()
print([t.name for t in tools.tools])
asyncio.run(test())
"
5. TypeScript实现
5.1 安装依赖
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
5.2 完整TypeScript MCP Server示例
/**
* analytics-mcp-server.ts
* 企业级TypeScript MCP Server,集成工具与资源
*/
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { z } from "zod";
import express from "express";
// ─────────────────────────────────────────────
// Server初始化
// ─────────────────────────────────────────────
const server = new McpServer({
name: "analytics-mcp-server",
version: "1.0.0",
});
// ─────────────────────────────────────────────
// TOOLS定义(使用Zod做类型安全的Schema)
// ─────────────────────────────────────────────
server.tool(
"query_orders",
"查询指定日期范围内的订单数据,支持状态过滤",
{
start_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "格式必须为YYYY-MM-DD"),
end_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "格式必须为YYYY-MM-DD"),
status: z
.enum(["all", "pending", "completed", "cancelled"])
.default("all"),
limit: z.number().int().min(1).max(1000).default(100),
},
async ({ start_date, end_date, status, limit }) => {
try {
// 实际项目替换为真实数据库查询
const orders = await fetchOrdersFromDB({ start_date, end_date, status, limit });
return {
content: [
{
type: "text",
text: JSON.stringify({
total: orders.length,
orders,
queried_at: new Date().toISOString(),
}, null, 2),
},
],
};
} catch (error) {
// 结构化错误返回,不要抛出异常
return {
content: [
{
type: "text",
text: `ERROR: 查询失败 - ${error instanceof Error ? error.message : String(error)}`,
},
],
isError: true,
};
}
}
);
server.tool(
"execute_sql",
"执行只读SQL查询(仅支持SELECT语句)",
{
query: z.string().min(1).refine(
(q) => q.trim().toUpperCase().startsWith("SELECT"),
"安全限制:仅允许SELECT查询"
),
timeout_ms: z.number().int().min(100).max(30000).default(5000),
},
async ({ query, timeout_ms }) => {
// 防注入:白名单关键词检查
const forbidden = ["DROP", "DELETE", "UPDATE", "INSERT", "TRUNCATE", "ALTER"];
const upperQuery = query.toUpperCase();
const detected = forbidden.find((kw) => upperQuery.includes(kw));
if (detected) {
return {
content: [{ type: "text", text: `SECURITY ERROR: 检测到禁止操作 '${detected}'` }],
isError: true,
};
}
// 模拟执行
return {
content: [
{
type: "text",
text: JSON.stringify({
rows: [{ count: 1428, avg_amount: 1996.5 }],
execution_time_ms: 38,
query_executed: query,
}, null, 2),
},
],
};
}
);
// ─────────────────────────────────────────────
// RESOURCES定义
// ─────────────────────────────────────────────
server.resource(
"system-status",
"system://status",
{ mimeType: "application/json" },
async () => ({
contents: [
{
uri: "system://status",
mimeType: "application/json",
text: JSON.stringify({
status: "healthy",
version: "1.0.0",
db: "connected",
cache_hit_rate: "94.2%",
timestamp: new Date().toISOString(),
}, null, 2),
},
],
})
);
// ─────────────────────────────────────────────
// PROMPTS定义
// ─────────────────────────────────────────────
server.prompt(
"sales-analysis",
"销售数据分析提示模板",
{
period: z.string().describe("分析周期,如 2026-02 或 2026-Q1"),
focus: z.enum(["revenue", "volume", "margin"]).default("revenue"),
},
({ period, focus }) => ({
messages: [
{
role: "user",
content: {
type: "text",
text: `请分析${period}的销售趋势,重点关注${focus}指标。先调用query_orders工具获取数据,再给出分析结论和行动建议。`,
},
},
],
})
);
// ─────────────────────────────────────────────
// 传输层配置(二选一)
// ─────────────────────────────────────────────
// 本地stdio模式(Claude Desktop / Cursor本地使用)
async function runStdio() {
const transport = new StdioServerTransport();
await server.connect(transport);
}
// HTTP模式(生产部署,支持Streamable HTTP)
async function runHttp(port = 8080) {
const app = express();
app.use(express.json());
// 健康检查端点
app.get("/health", (_req, res) => {
res.json({ status: "ok", server: "analytics-mcp-server" });
});
// MCP端点(Streamable HTTP)
app.post("/mcp", async (req, res) => {
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined, // 无状态模式,适合Lambda/Serverless
});
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
});
app.listen(port, () => {
console.log(`MCP Server running on http://0.0.0.0:${port}/mcp`);
});
}
// 根据环境变量选择传输方式
if (process.env.MCP_TRANSPORT === "http") {
runHttp(Number(process.env.PORT) || 8080);
} else {
runStdio();
}
// 类型占位(实际项目替换为真实查询)
async function fetchOrdersFromDB(params: {
start_date: string;
end_date: string;
status: string;
limit: number;
}) {
return [
{ id: "ORD-001", amount: 1280, status: "completed", date: params.start_date },
{ id: "ORD-002", amount: 350, status: params.status === "all" ? "pending" : params.status },
].slice(0, params.limit);
}
6. 生产级最佳实践
6.1 工具设计原则
原则一:原子性(Atomicity)
每个Tool只做一件事,不要设计"多合一"工具。
# 错误示范:一个工具承担太多职责
@mcp.tool()
def manage_order(action: str, order_id: str, new_status: str = None, note: str = None):
if action == "query":
...
elif action == "update":
...
elif action == "cancel":
...
# 正确示范:职责分离
@mcp.tool()
def get_order(order_id: str) -> dict: ...
@mcp.tool()
def update_order_status(order_id: str, new_status: str) -> dict: ...
@mcp.tool()
def cancel_order(order_id: str, reason: str) -> dict: ...
原则二:幂等性(Idempotency)
查询类Tool必须幂等。写入类Tool应在描述中明确标注"非幂等",并在可能时提供去重机制(如接受 idempotency_key 参数)。
@mcp.tool()
async def create_order(
items: list[dict],
idempotency_key: str, # 客户端生成的唯一键,防止重复创建
) -> dict:
"""
创建新订单。WARNING: 此操作非幂等,请勿重复调用相同参数。
提供 idempotency_key 可防止因网络重试导致的重复创建。
"""
...
原则三:结构化错误返回
永远不要让异常穿透到MCP协议层。在Tool内部捕获所有错误,返回结构化的错误信息。
@mcp.tool()
async def query_database(sql: str) -> dict:
try:
result = await db.execute(sql)
return {"success": True, "data": result, "row_count": len(result)}
except DatabaseConnectionError as e:
return {
"success": False,
"error_type": "CONNECTION_ERROR",
"error_message": str(e),
"retry_after_seconds": 30,
}
except QuerySyntaxError as e:
return {
"success": False,
"error_type": "SYNTAX_ERROR",
"error_message": str(e),
"hint": "请检查SQL语法",
}
except Exception as e:
# 生产环境不要暴露内部错误细节
logger.exception("Unexpected database error", extra={"sql": sql})
return {
"success": False,
"error_type": "INTERNAL_ERROR",
"error_message": "数据库操作失败,请联系管理员",
"request_id": generate_request_id(),
}
6.2 安全:OAuth 2.1 + 输入验证
2025年3月,MCP规范正式引入OAuth 2.1作为标准认证框架。关键要点:
MCP认证架构(OAuth 2.1):
用户 ──→ Host(如Claude Desktop)
│
│ 1. 发现资源 (RFC 9728: Protected Resource Metadata)
▼
MCP Server: 返回 WWW-Authenticate 头
│ resource_metadata URL指向PRM文档
│
│ 2. 从PRM获取授权服务器地址
▼
Authorization Server(独立的IdP)
│
│ 3. OAuth 2.1 + PKCE流程
▼
颁发访问令牌(scope精确受限)
│
│ 4. 携带令牌调用MCP Server
▼
MCP Server验证令牌,执行工具调用
关键安全规则:
# 规则1:MCP Server永远不能把收到的token透传给上游API
# 这会造成"confused deputy"漏洞
# 错误示范
async def call_upstream_api(user_token: str):
return await httpx.get(
"https://upstream.api/data",
headers={"Authorization": f"Bearer {user_token}"} # 严禁!
)
# 正确示范:MCP Server用自己的Service Account调用上游
async def call_upstream_api():
service_token = await get_service_account_token() # 独立的服务账号
return await httpx.get(
"https://upstream.api/data",
headers={"Authorization": f"Bearer {service_token}"}
)
输入验证:白名单优于黑名单:
import re
from pathlib import Path
ALLOWED_PATH_PREFIX = Path("/var/mcp-data")
@mcp.tool()
async def read_file(file_path: str) -> str:
"""安全的文件读取工具"""
# 路径规范化,防止路径遍历攻击
try:
resolved = Path(file_path).resolve()
except Exception:
return "ERROR: 无效路径"
# 白名单检查:只允许访问指定目录
if not str(resolved).startswith(str(ALLOWED_PATH_PREFIX)):
return f"ERROR: 禁止访问 '{file_path}',只允许访问 {ALLOWED_PATH_PREFIX}"
# 文件扩展名白名单
allowed_extensions = {".txt", ".json", ".csv", ".md", ".log"}
if resolved.suffix not in allowed_extensions:
return f"ERROR: 不支持的文件类型 '{resolved.suffix}'"
if not resolved.exists():
return f"ERROR: 文件不存在"
return resolved.read_text(encoding="utf-8")
6.3 性能:连接池、超时、重试
import asyncio
import httpx
from contextlib import asynccontextmanager
# 全局连接池(避免每次工具调用都建立新连接)
_http_client: httpx.AsyncClient | None = None
@asynccontextmanager
async def lifespan(mcp: FastMCP):
"""Server生命周期管理:启动时初始化,关闭时清理"""
global _http_client
_http_client = httpx.AsyncClient(
timeout=httpx.Timeout(connect=5.0, read=30.0, write=10.0, pool=5.0),
limits=httpx.Limits(max_connections=50, max_keepalive_connections=20),
headers={"User-Agent": "MCP-AnalyticsServer/1.0"},
)
yield
await _http_client.aclose()
mcp = FastMCP("analytics-server", lifespan=lifespan)
# 重试装饰器(指数退避)
async def with_retry(coro_fn, max_retries: int = 3, base_delay: float = 1.0):
last_error = None
for attempt in range(max_retries):
try:
return await coro_fn()
except (httpx.TimeoutException, httpx.ConnectError) as e:
last_error = e
if attempt < max_retries - 1:
delay = base_delay * (2 ** attempt)
await asyncio.sleep(delay)
raise last_error
@mcp.tool()
async def fetch_external_data(url: str) -> dict:
"""带重试的外部API调用"""
async def do_request():
response = await _http_client.get(url)
response.raise_for_status()
return response.json()
try:
return await with_retry(do_request, max_retries=3)
except Exception as e:
return {"success": False, "error": str(e)}
6.4 可观测性:结构化日志 + Metrics
import logging
import time
import json
from functools import wraps
# 结构化日志配置
logging.basicConfig(
level=logging.INFO,
format='{"time":"%(asctime)s","level":"%(levelname)s","logger":"%(name)s","message":%(message)s}',
)
logger = logging.getLogger("mcp.analytics")
def instrument_tool(tool_name: str):
"""工具调用埋点装饰器"""
def decorator(fn):
@wraps(fn)
async def wrapper(*args, **kwargs):
start_time = time.monotonic()
request_id = kwargs.pop("_request_id", "unknown")
logger.info(json.dumps({
"event": "tool_call_start",
"tool": tool_name,
"request_id": request_id,
"args_keys": list(kwargs.keys()),
}))
try:
result = await fn(*args, **kwargs)
duration_ms = (time.monotonic() - start_time) * 1000
logger.info(json.dumps({
"event": "tool_call_success",
"tool": tool_name,
"request_id": request_id,
"duration_ms": round(duration_ms, 2),
}))
# 推送到Prometheus/Datadog等Metrics系统
# metrics.histogram("mcp.tool.duration", duration_ms, tags=[f"tool:{tool_name}"])
# metrics.increment("mcp.tool.success", tags=[f"tool:{tool_name}"])
return result
except Exception as e:
duration_ms = (time.monotonic() - start_time) * 1000
logger.error(json.dumps({
"event": "tool_call_error",
"tool": tool_name,
"request_id": request_id,
"duration_ms": round(duration_ms, 2),
"error_type": type(e).__name__,
"error": str(e),
}))
# metrics.increment("mcp.tool.error", tags=[f"tool:{tool_name}", f"error:{type(e).__name__}"])
raise
return wrapper
return decorator
7. 主流MCP Server生态
截至2026年初,官方与社区已发布600+个MCP Server。以下是经过生产验证的核心服务器:
官方参考实现(Anthropic维护)
| Server名称 | 功能 | 安装命令 |
|---|---|---|
| filesystem | 安全的本地文件读写,可配置访问路径白名单 | npx @modelcontextprotocol/server-filesystem |
| github | 读取/搜索/操作GitHub仓库、PR、Issue | npx @modelcontextprotocol/server-github |
| postgres | PostgreSQL查询、Schema检查、数据探索 | npx @modelcontextprotocol/server-postgres |
| sqlite | SQLite数据库的完整操作 | npx @modelcontextprotocol/server-sqlite |
| brave-search | Brave Search隐私搜索API集成 | npx @modelcontextprotocol/server-brave-search |
| fetch | 受控的HTTP请求工具 | npx @modelcontextprotocol/server-fetch |
| memory | 基于知识图谱的跨会话记忆持久化 | npx @modelcontextprotocol/server-memory |
| puppeteer | 无头浏览器自动化 | npx @modelcontextprotocol/server-puppeteer |
| slack | Slack消息发送与频道管理 | npx @modelcontextprotocol/server-slack |
社区高质量Server
| Server名称 | 功能 | 维护方 |
|---|---|---|
| supabase | Supabase数据库+认证全栈集成 | Supabase官方 |
| notion | Notion页面、数据库读写 | Notion社区 |
| playwright | 跨浏览器自动化测试 | 社区 |
| aws-kb | AWS Bedrock知识库检索 | AWS |
| cloudflare | Cloudflare Workers/KV/D1操作 | Cloudflare官方 |
| desktop-commander | 桌面自动化(截图/点击/键盘) | 社区 |
| mcp-qdrant | Qdrant向量数据库集成 | Qdrant |
Server选型建议
需求场景 推荐Server
─────────────────────────────────────────────────────
本地代码库操作 filesystem + github
数据库探索与查询 postgres / sqlite
实时网络搜索 brave-search / fetch
跨会话记忆持久化 memory
网页自动化/爬虫 puppeteer / playwright
团队协作通知 slack / notion
企业身份与权限 auth0 / okta(社区)
向量检索/RAG mcp-qdrant / pgvector
8. 配置实战
8.1 Claude Desktop配置
配置文件路径:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/yourname/projects",
"/Users/yourname/documents"
]
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxxxxxxxxxxxxxxxxxxx"
}
},
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": {
"POSTGRES_CONNECTION_STRING": "postgresql://user:password@localhost:5432/mydb"
}
},
"brave-search": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-brave-search"],
"env": {
"BRAVE_API_KEY": "BSAxxx..."
}
},
"custom-analytics": {
"command": "python",
"args": ["/path/to/production_mcp_server.py"],
"env": {
"DATABASE_URL": "postgresql://...",
"LOG_LEVEL": "INFO"
}
}
}
}
8.2 Claude Code(Claude CLI)配置
Claude Code通过 .claude/mcp.json 或全局 ~/.claude/mcp.json 配置:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "."],
"description": "当前项目目录的文件操作"
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}"
}
}
}
}
也可在命令行动态指定:
# 启动Claude Code时挂载MCP Server
claude --mcp-server "filesystem:npx -y @modelcontextprotocol/server-filesystem /path"
# 查看当前已连接的MCP Server
claude mcp list
# 添加新Server(持久化到配置文件)
claude mcp add github -- npx -y @modelcontextprotocol/server-github
8.3 Cursor配置
Cursor使用 .cursor/mcp.json(项目级)或全局设置:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "${workspaceFolder}"]
},
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": {
"POSTGRES_CONNECTION_STRING": "postgresql://localhost/mydb"
}
}
}
}
8.4 远程MCP Server(Streamable HTTP模式)
当MCP Server部署为独立HTTP服务时,客户端配置改为URL:
{
"mcpServers": {
"remote-analytics": {
"url": "https://mcp.yourcompany.com/analytics/mcp",
"headers": {
"Authorization": "Bearer ${MCP_API_TOKEN}"
}
}
}
}
9. MCP vs Function Calling
这是工程师最常问的问题。答案是:它们不在同一个层次,不是竞争关系。
核心差异
| 维度 | Function Calling(FC) | MCP |
|---|---|---|
| 本质 | 单次API调用的工具声明格式 | 工具集成的通信协议 |
| 绑定性 | 与特定AI提供商绑定(OpenAI格式、Anthropic格式各不同) | 跨AI模型、跨提供商的统一标准 |
| 工具生命周期 | 每次请求都需要在payload中重新声明所有工具 | 工具在Server中持久注册,动态发现 |
| 状态管理 | 无状态(每次调用独立) | 有状态连接(支持订阅、进度推送) |
| 安全边界 | 工具代码和AI应用运行在同一进程/服务 | 工具代码在独立进程/服务中隔离运行 |
| 复用性 | 工具定义与应用代码耦合,难以复用 | 一个MCP Server可被N个AI应用共享 |
| 部署单元 | 应用代码的一部分 | 独立部署的服务 |
| 生态 | 每家AI有自己的格式 | 600+标准化Server,即插即用 |
架构对比图
Function Calling架构(应用内耦合):
┌──────────────────────────────────────┐
│ AI应用进程 │
│ │
│ AI Model API ──→ FC声明(内联) │
│ │ │
│ └──→ 工具函数A(本地代码) │
│ └──→ 工具函数B(本地代码) │
│ └──→ 工具函数C(本地代码) │
└──────────────────────────────────────┘
问题:换个AI就要重写工具声明格式;工具代码与AI应用紧耦合
MCP架构(协议解耦):
┌─────────────────┐ MCP Protocol ┌──────────────────┐
│ AI应用A │ ────────────────────▶│ MCP Server │
│ (Host+Client) │ │ (独立进程/服务) │
└─────────────────┘ │ │
┌─────────────────┐ MCP Protocol │ · Tool A │
│ AI应用B │ ────────────────────▶│ · Tool B │
│ (Host+Client) │ │ · Resource X │
└─────────────────┘ │ · Prompt Y │
┌─────────────────┐ MCP Protocol │ │
│ AI应用C │ ────────────────────▶│ │
│ (Host+Client) │ └──────────────────┘
└─────────────────┘
优势:N个AI共用1个Server;切换AI无需改工具代码
选型建议
- 快速原型、单AI应用、一次性脚本 → Function Calling更轻便
- 企业级工具复用、多AI接入、独立部署、安全隔离 → MCP是正确架构
- MCP在内部实现上会用到Function Calling机制 → 两者互补,不互斥
10. 未来展望
10.1 Agent-to-Agent通信
2025年底,MCP规范新增了对异步任务(Tasks primitive)的支持:Server可以创建长时间运行的任务,返回任务句柄,支持进度通知,支持任务结果轮询。
这意味着 一个AI Agent可以通过MCP协议调用另一个AI Agent,形成Agent网络:
未来的Agent编排架构:
用户请求
│
▼
Orchestrator Agent(主协调者)
│
├──MCP──▶ Research Agent(专门做网络调研)
│ │
│ └──MCP──▶ brave-search server
│
├──MCP──▶ Code Agent(专门写代码)
│ │
│ └──MCP──▶ filesystem server
│ github server
│
└──MCP──▶ Review Agent(专门做审查)
│
└──MCP──▶ postgres server(读审计日志)
Google的A2A(Agent-to-Agent)协议与MCP是互补关系:MCP负责Agent与工具的连接,A2A负责Agent与Agent之间的高层协作协商。
10.2 MCP Marketplace
Microsoft Azure Marketplace已开始收录MCP Server,允许企业通过订阅方式接入经过安全审查的MCP Server。预计2026年内,主流云平台都会建立MCP Server市场:
MCP Marketplace生态演进:
2024: Anthropic开源MCP规范,参考实现10个Server
2025: 社区生态爆发,600+Server,主流AI平台采用
2026: 企业级MCP市场,认证Server(安全/合规/SLA保障)
2027+: W3C标准化,Agent Protocol生态成熟
10.3 工程师的准备清单
对于技术负责人和AI产品经理,现在就应该开始的行动:
近期(0-3个月):
- 在Claude Desktop/Cursor中配置几个常用MCP Server,感受工程体验
- 用FastMCP为团队内部工具(数据库、日志系统)写第一个MCP Server
- 评估现有的Function Calling工具是否值得迁移到MCP
中期(3-12个月):
- 建立公司内部的MCP Server注册中心(类似内部npm registry)
- 实施MCP的安全框架(OAuth 2.1 + 权限边界 + 审计日志)
- 考虑将MCP Server纳入CI/CD流程(自动化测试、版本发布)
长期(1-2年):
- 参与MCP生态建设,向官方贡献行业特定Server
- 探索Multi-Agent架构,让专业Agent通过MCP协作
- 跟踪W3C Agent Protocol标准化进程
总结
MCP不是一个工具库,而是一个工程规范。它解决的根本问题是:如何在AI模型与外部世界之间建立一个标准化的、可复用的、安全的通信接口。
就像USB-C统一了硬件世界的物理接口,MCP正在统一AI世界的软件接口。这个统一带来的收益是指数级的:每个按MCP规范实现的工具,可以为所有支持MCP的AI模型所用,无需任何改造。
对于产品经理:MCP让你可以把"AI能力集成"从定制开发变成标准采购。 对于技术负责人:MCP让你可以把工具能力作为独立服务管理,而不是散落在各个AI应用中的耦合代码。 对于创业者:MCP生态是一个开放的机会,第一批高质量的行业专用MCP Server将获得巨大的先发优势。
行动起来,现在就是时机。
参考资料
- MCP官方规范 2025-11-25
- FastMCP官方文档
- MCP TypeScript SDK
- MCP Python SDK
- 官方MCP Server参考实现
- MCP OAuth 2.1认证规范
- Streamable HTTP传输机制详解
- MCP vs Function Calling深度对比
- 2026年企业级MCP采用展望
- MCP与A2A协议对比
Maurice | maurice_wen@proton.me