MCP协议实战:用Model Context Protocol构建AI Agent工具链

AI与大数据

摘要

  • MCP(Model Context Protocol)是Anthropic提出的AI Agent工具调用标准协议,2026年已成为事实上的Agent工具链协议
  • MCP通过JSON-RPC 2.0实现工具注册、发现和调用,解决了AI Agent"工具孤岛"问题
  • 本文从MCP协议原理到Server开发,从工具注册到多Agent编排,全链路完整代码
  • MCP支持SSE和Stdio两种传输模式,SSE适合远程部署,Stdio适合本地开发
  • 附赠MCP Server生产部署方案与工具链安全审计checklist

目录


为什么AI Agent需要MCP协议

2026年之前,AI Agent调用工具的方式是"各自为政"——OpenAI用Function Calling,LangChain用Tool抽象,AutoGPT用自定义插件。每个框架都定义了自己的工具接口,工具无法跨框架复用,Agent无法跨平台协作。

┌──────────────────────────────────────────────────────────────┐
│              AI Agent工具调用的"巴别塔"                         │
│                                                                │
│  OpenAI Agent ──→ Function Calling (JSON Schema)              │
│  LangChain   ──→ Tool Abstraction (Python Class)              │
│  AutoGPT     ──→ Plugin System (YAML Config)                  │
│  Dify        ──→ Tool Node (API Config)                       │
│  Coze        ──→ Plugin Market (Proprietary)                  │
│                                                                │
│  ❌ 工具无法复用  ❌ 接口不统一  ❌ 安全无保障  ❌ 发现无标准   │
└──────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────┐
│              MCP协议:AI Agent的"USB-C"                        │
│                                                                │
│  任何Agent ←──→ MCP协议 ←──→ 任何工具                          │
│                                                                │
│  ┌────────┐  MCP   ┌──────────┐  MCP   ┌──────────┐         │
│  │ Claude │←──────→│ 搜索工具 │←──────→│ GPT-4   │         │
│  │ Agent  │        │ 数据库   │        │ Agent    │         │
│  └────────┘        │ API网关  │        └──────────┘         │
│                    └──────────┘                              │
│                                                                │
│  ✅ 工具复用  ✅ 接口统一  ✅ 安全沙箱  ✅ 自动发现            │
└──────────────────────────────────────────────────────────────┘

MCP vs Function Calling vs LangChain Tool

维度 MCP OpenAI Function Calling LangChain Tool
协议标准 开放标准(Anthropic) 厂商私有 框架私有
工具发现 自动发现(capabilities) 手动注册 手动注册
传输协议 JSON-RPC 2.0 (SSE/Stdio) HTTP API Python函数调用
安全沙箱 ✅ 权限控制
跨Agent复用 ✅ 任何MCP Client ❌ 仅OpenAI ❌ 仅LangChain
流式输出 ✅ SSE ✅ SSE ⚠️ 部分支持
多工具编排 ✅ 原生支持 ⚠️ 需手动 ✅ Chain抽象

参考:Model Context Protocol Specification


MCP协议核心机制

三大角色

角色 说明 类比
MCP Host 发起连接的AI应用(如Claude Desktop、IDE插件) USB主机
MCP Client 与MCP Server建立连接的协议客户端,嵌入在Host中 USB控制器
MCP Server 提供工具、资源、提示词的服务端 USB设备

协议交互流程

┌──────────────────────────────────────────────────────────────┐
│                    MCP协议交互流程                              │
│                                                                │
│  MCP Host (Claude Desktop)                                    │
│    │                                                           │
│    │ 1. initialize                                             │
│    ├──→ MCP Client ──→ MCP Server (搜索工具)                  │
│    │                                         │                 │
│    │    2. capabilities响应                    │                │
│    │←─────────────────────────────────────────┘                │
│    │                                                           │
│    │ 3. tools/list                                             │
│    ├──→ MCP Client ──→ MCP Server                             │
│    │                                         │                 │
│    │    4. 工具列表响应                          │               │
│    │←─────────────────────────────────────────┘                │
│    │                                                           │
│    │ 5. tools/call (搜索"K8s GPU调度")                         │
│    ├──→ MCP Client ──→ MCP Server                             │
│    │                                         │                 │
│    │    6. 工具执行结果                          │               │
│    │←─────────────────────────────────────────┘                │
└──────────────────────────────────────────────────────────────┘

MCP能力类型

能力 说明 示例
Tools 可被Agent调用的函数 搜索、数据库查询、API调用
Resources 可被Agent读取的数据 文件、数据库记录、配置
Prompts 可被Agent使用的提示词模板 代码审查模板、翻译模板
Sampling Server向Agent请求LLM补全 多步推理、内容生成

MCP Server开发:5步构建自定义工具服务

第1步:项目初始化

mkdir mcp-search-server && cd mcp-search-server
pip install mcp fastapi httpx

第2步:定义工具Schema

from mcp.server import Server
from mcp.types import Tool, TextContent
import httpx
import json

server = Server("search-tools")

@server.list_tools()
async def list_tools() -> list[Tool]:
    return [
        Tool(
            name="web_search",
            description="搜索互联网获取最新信息。当需要查找实时数据、新闻、技术文档时使用此工具。",
            inputSchema={
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "搜索关键词"
                    },
                    "max_results": {
                        "type": "integer",
                        "description": "最大返回结果数",
                        "default": 5
                    },
                    "search_depth": {
                        "type": "string",
                        "enum": ["basic", "advanced"],
                        "description": "搜索深度:basic快速搜索,advanced深度搜索",
                        "default": "basic"
                    }
                },
                "required": ["query"]
            }
        ),
        Tool(
            name="code_search",
            description="在GitHub上搜索代码仓库和代码片段。当需要查找开源实现、API用法示例时使用。",
            inputSchema={
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "代码搜索关键词"
                    },
                    "language": {
                        "type": "string",
                        "description": "编程语言过滤",
                        "enum": ["python", "go", "rust", "typescript", "java"]
                    }
                },
                "required": ["query"]
            }
        ),
        Tool(
            name="api_tester",
            description="发送HTTP请求测试API端点。当需要验证API可用性、获取API响应数据时使用。",
            inputSchema={
                "type": "object",
                "properties": {
                    "url": {
                        "type": "string",
                        "description": "API端点URL"
                    },
                    "method": {
                        "type": "string",
                        "enum": ["GET", "POST", "PUT", "DELETE"],
                        "default": "GET"
                    },
                    "headers": {
                        "type": "object",
                        "description": "请求头"
                    },
                    "body": {
                        "type": "object",
                        "description": "请求体(JSON)"
                    }
                },
                "required": ["url"]
            }
        ),
    ]

第3步:实现工具逻辑

@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
    if name == "web_search":
        return await handle_web_search(arguments)
    elif name == "code_search":
        return await handle_code_search(arguments)
    elif name == "api_tester":
        return await handle_api_tester(arguments)
    else:
        raise ValueError(f"Unknown tool: {name}")

async def handle_web_search(args: dict) -> list[TextContent]:
    query = args["query"]
    max_results = args.get("max_results", 5)
    search_depth = args.get("search_depth", "basic")

    async with httpx.AsyncClient(timeout=30) as client:
        response = await client.post(
            "https://api.search.brave.com/res/v1/web/search",
            headers={"X-Subscription-Token": BRAVE_API_KEY},
            params={
                "q": query,
                "count": max_results,
                "search_depth": search_depth,
            }
        )
        data = response.json()

    results = []
    for item in data.get("web", {}).get("results", [])[:max_results]:
        results.append({
            "title": item.get("title", ""),
            "url": item.get("url", ""),
            "description": item.get("description", ""),
        })

    return [TextContent(
        type="text",
        text=json.dumps(results, ensure_ascii=False, indent=2)
    )]

async def handle_code_search(args: dict) -> list[TextContent]:
    query = args["query"]
    language = args.get("language", "")

    async with httpx.AsyncClient(timeout=30) as client:
        response = await client.get(
            "https://api.github.com/search/code",
            headers={"Authorization": f"token {GITHUB_TOKEN}"},
            params={
                "q": f"{query} language:{language}" if language else query,
                "per_page": 5,
            }
        )
        data = response.json()

    results = []
    for item in data.get("items", [])[:5]:
        results.append({
            "name": item.get("name", ""),
            "path": item.get("path", ""),
            "repository": item.get("repository", {}).get("full_name", ""),
            "html_url": item.get("html_url", ""),
        })

    return [TextContent(
        type="text",
        text=json.dumps(results, ensure_ascii=False, indent=2)
    )]

async def handle_api_tester(args: dict) -> list[TextContent]:
    url = args["url"]
    method = args.get("method", "GET")
    headers = args.get("headers", {})
    body = args.get("body")

    async with httpx.AsyncClient(timeout=30) as client:
        response = await client.request(
            method=method,
            url=url,
            headers=headers,
            json=body,
        )

    return [TextContent(
        type="text",
        text=json.dumps({
            "status_code": response.status_code,
            "headers": dict(response.headers),
            "body": response.json() if "json" in response.headers.get("content-type", "") else response.text[:2000],
        }, ensure_ascii=False, indent=2)
    )]

第4步:配置SSE传输

from mcp.server.sse import SseServerTransport
from starlette.applications import Starlette
from starlette.routing import Route

sse = SseServerTransport("/messages")

async def handle_sse(request):
    async with sse.connect_sse(request) as streams:
        await server.run(streams[0], streams[1], server.create_initialization_options())

async def handle_messages(request):
    await sse.handle_post_message(request)

app = Starlette(
    routes=[
        Route("/sse", endpoint=handle_sse),
        Route("/messages", endpoint=handle_messages, methods=["POST"]),
    ]
)

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8080)

第5步:Docker部署

FROM python:3.11-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .

EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
    CMD curl -f http://localhost:8080/sse || exit 1

CMD ["python", "server.py"]

MCP Client集成:让大模型调用工具

Python MCP Client

from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from openai import OpenAI
import json

server_params = StdioServerParameters(
    command="python",
    args=["server.py"],
    env={"BRAVE_API_KEY": "...", "GITHUB_TOKEN": "..."}
)

async def run_agent(user_query: str):
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()

            tools_result = await session.list_tools()
            available_tools = [
                {
                    "type": "function",
                    "function": {
                        "name": tool.name,
                        "description": tool.description,
                        "parameters": tool.inputSchema,
                    }
                }
                for tool in tools_result.tools
            ]

            client = OpenAI(base_url="http://localhost:8000/v1", api_key="not-needed")

            messages = [{"role": "user", "content": user_query}]

            response = client.chat.completions.create(
                model="Qwen/Qwen2.5-7B-Instruct",
                messages=messages,
                tools=available_tools,
                tool_choice="auto",
            )

            message = response.choices[0].message

            if message.tool_calls:
                for tool_call in message.tool_calls:
                    result = await session.call_tool(
                        tool_call.function.name,
                        arguments=json.loads(tool_call.function.arguments),
                    )

                    messages.append(message)
                    messages.append({
                        "role": "tool",
                        "tool_call_id": tool_call.id,
                        "content": result[0].text,
                    })

                final_response = client.chat.completions.create(
                    model="Qwen/Qwen2.5-7B-Instruct",
                    messages=messages,
                )
                return final_response.choices[0].message.content

            return message.content

工具调用流程

┌──────────────────────────────────────────────────────────────┐
│              MCP Agent工具调用完整流程                          │
│                                                                │
│  用户: "帮我搜索K8s GPU调度的最新方案并测试相关API"             │
│    │                                                           │
│    ▼                                                           │
│  LLM分析意图 → 需要调用2个工具:                               │
│    1. web_search("K8s GPU调度 2026")                           │
│    2. api_tester("https://kubernetes.io/docs/concepts/")       │
│    │                                                           │
│    ├──→ MCP Client ──→ web_search ──→ 返回搜索结果            │
│    │                                                           │
│    ├──→ MCP Client ──→ api_tester ──→ 返回API响应             │
│    │                                                           │
│    ▼                                                           │
│  LLM整合结果 → 生成最终回答                                    │
└──────────────────────────────────────────────────────────────┘

多Agent编排:MCP工具链组合

多Server编排架构

from mcp import ClientSession
from mcp.client.sse import sse_client
import asyncio

class MCPToolOrchestrator:
    def __init__(self):
        self.servers = {}
        self.sessions = {}

    async def register_server(self, name: str, url: str):
        self.servers[name] = url

    async def connect_all(self):
        for name, url in self.servers.items():
            read, write = await sse_client(url)
            session = ClientSession(read, write)
            await session.initialize()
            self.sessions[name] = session

    async def discover_all_tools(self) -> list[dict]:
        all_tools = []
        for name, session in self.sessions.items():
            tools = await session.list_tools()
            for tool in tools.tools:
                all_tools.append({
                    "server": name,
                    "name": tool.name,
                    "description": tool.description,
                    "inputSchema": tool.inputSchema,
                })
        return all_tools

    async def call_tool(self, tool_name: str, arguments: dict) -> str:
        for name, session in self.sessions.items():
            tools = await session.list_tools()
            if any(t.name == tool_name for t in tools.tools):
                result = await session.call_tool(tool_name, arguments)
                return result[0].text
        raise ValueError(f"Tool {tool_name} not found in any server")

    async def close_all(self):
        for session in self.sessions.values():
            await session.close()

orchestrator = MCPToolOrchestrator()
await orchestrator.register_server("search", "http://search-mcp:8080/sse")
await orchestrator.register_server("database", "http://db-mcp:8081/sse")
await orchestrator.register_server("code", "http://code-mcp:8082/sse")
await orchestrator.connect_all()

K8s部署MCP Server集群

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mcp-search-server
  namespace: ai-agent
spec:
  replicas: 2
  selector:
    matchLabels:
      app: mcp-search-server
  template:
    metadata:
      labels:
        app: mcp-search-server
    spec:
      containers:
        - name: mcp-server
          image: myregistry/mcp-search-server:v1.0
          ports:
            - containerPort: 8080
          resources:
            requests:
              cpu: "1"
              memory: 512Mi
            limits:
              cpu: "2"
              memory: 1Gi
          env:
            - name: BRAVE_API_KEY
              valueFrom:
                secretKeyRef:
                  name: mcp-secrets
                  key: brave-api-key
          livenessProbe:
            httpGet:
              path: /sse
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 30
---
apiVersion: v1
kind: Service
metadata:
  name: mcp-search-svc
  namespace: ai-agent
spec:
  selector:
    app: mcp-search-server
  ports:
    - port: 8080
      targetPort: 8080

生产部署与安全审计

MCP安全模型

安全层 机制 说明
传输层 TLS + SSE 加密传输,防止中间人攻击
认证层 OAuth 2.0 / API Key Server验证Client身份
授权层 工具权限白名单 限制可调用的工具范围
数据层 输入校验 + 输出过滤 防止注入攻击和数据泄露
审计层 调用日志 + 计量 全链路追踪和费用控制

安全审计Checklist

检查项 风险等级 检查方法
工具输入参数校验 Schema验证 + 类型检查
工具输出数据脱敏 正则过滤敏感信息
API Key不硬编码 环境变量/Secret管理
SSE连接超时控制 心跳 + 超时断开
工具调用频率限制 Rate Limiting
调用日志完整性 结构化日志 + 审计追踪
错误信息不泄露内部状态 统一错误格式

MCP Server性能基准

指标 单实例 3实例集群
工具调用QPS 500 1400
平均延迟(P50) 45ms 50ms
P99延迟 180ms 200ms
SSE连接数 200 600
内存占用 256MB 256MB×3

总结与引流

MCP协议是AI Agent工具链的"USB-C"——一个开放标准解决了工具孤岛、接口不统一、安全无保障三大问题。通过MCP Server开发、Client集成和多Agent编排,可以快速构建生产级AI Agent工具链。

开发要点回顾

  1. MCP通过JSON-RPC 2.0实现工具注册、发现和调用
  2. SSE模式适合远程部署,Stdio模式适合本地开发
  3. 工具Schema定义是MCP的核心,需详细描述参数和语义
  4. 多Agent编排通过MCPToolOrchestrator实现跨Server工具调用
  5. 生产部署需关注安全审计:输入校验、输出脱敏、频率限制

相关阅读

权威参考

本站提供浏览器本地工具,免注册即可试用 →

#MCP协议#AI Agent工具调用#Model Context Protocol#大模型工具使用#Agent开发框架#2026