MCP协议工程实践:AI工具集成的USB-C

作者:Maurice | 灵阙学院


目录

  1. 诞生背景:为什么需要统一的工具协议
  2. 核心架构:三层模型与通信机制
  3. 三大原语:Resources、Tools、Prompts
  4. Python实现:用FastMCP快速构建服务端
  5. TypeScript实现:官方SDK建服务端
  6. 生产级最佳实践
  7. 主流MCP Server生态全景
  8. Claude Code与Cursor中的配置实战
  9. MCP vs Function Calling:协议层 vs 调用层
  10. 未来展望: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将获得巨大的先发优势。

行动起来,现在就是时机。


参考资料


Maurice | maurice_wen@proton.me