写点什么

最新 MCP 规范解读,看这篇就够了!

  • 2025-11-12
    北京
  • 本文字数:19849 字

    阅读完需:约 65 分钟

一、MCP 是什么? 为什么需要它?


想象一下,你正在开发一个 AI 编程助手,它需要:


  • 读取和修改项目文件

  • 查询数据库 Schema

  • 搜索代码仓库

  • 执行 Git 操作


传统做法是为每个数据源写一套专用代码,不同团队重复造轮子。Model Context Protocol(MCP) 就是为了解决这个问题而生的开放标准协议。


通俗理解: MCP 就像是「AI 应用的 USB 接口标准」。就像 USB 让不同设备都能接入电脑一样,MCP 让不同的数据源和工具都能以统一方式接入 AI 应用。


实际案例: 在 Claude Desktop 中,你可以配置多个官方 MCP 服务器:


  • Filesystem 服务器: 安全地读写本地文件,有权限控制

  • SQLite 服务器: 查询和分析 SQLite 数据库,自动生成 SQL

  • GitHub 服务器: 搜索仓库、创建 Issue、管理 PR


你的 AI 应用只需实现一个 MCP 客户端,就能连接所有服务器,无需为每个服务器写专用代码。

二、架构设计: 三个角色的分工

MCP 采用宿主-客户端-服务器三层架构,就像一家公司的组织结构:


宿主(Host) = 总经理


  • 管理所有客户端

  • 控制安全策略和权限

  • 负责 AI 模型的调用


客户端(Client) = 部门经理


  • 客户端负责连接服务器

  • 负责双方的沟通协调

  • 转发消息和通知


服务器(Server) = 业务专员


  • 提供具体功能(资源、工具、提示模板)

  • 可以是本地程序或远程服务

  • 不知道其他服务器的存在

三、协议约定:统一规范与个性化扩展

每个 MCP 服务器提供的工具、资源都不一样,但它们都遵循相同的 MCP 协议规范。

3.1 协议的分层设计

MCP 采用 基础协议 + 功能扩展 的设计,就像 HTTP 协议一样:


核心层(所有实现必须支持) :


  • JSON-RPC 2.0 消息格式

  • 初始化握手流程(initialize/initialized)

  • 基本错误处理


功能层(按需选择) :


  • Resources、Prompts、Tools(服务器端)

  • Roots、Sampling、Elicitation(客户端)


这样设计的好处:


统一的基础协议 → 保证互操作性     +灵活的功能选择 → 满足不同场景需求既标准化又可扩展
复制代码

3.2 协议约定的过程

步骤 1: 基础协议是固定的


所有 MCP 服务器和客户端都遵循相同的 JSON-RPC 2.0 格式:


// 请求格式(固定){  "jsonrpc": "2.0",      // 必须是2.0  "id": 1,                // 唯一标识  "method": "方法名",     // 要调用的方法  "params": {...}         // 参数对象}
// 响应格式(固定){ "jsonrpc": "2.0", "id": 1, // 对应请求的ID "result": {...} // 成功结果 // 或 "error": {...} // 错误信息}
复制代码


步骤 2: 能力在初始化时协商


// 客户端发起初始化{  "method": "initialize",  "params": {    "protocolVersion": "2024-11-05",    "capabilities": {      "sampling": {},       // 我支持LLM采样      "roots": {}           // 我支持根目录    },    "clientInfo": {"name": "MyClient", "version": "1.0"}  }}
// 服务器响应{ "result": { "protocolVersion": "2024-11-05", "capabilities": { "tools": {}, // 我提供工具 "resources": {} // 我提供资源 }, "serverInfo": {"name": "SQLiteServer", "version": "2.0"} }}
复制代码


协商完成后,双方都知道对方支持什么功能,只使用交集部分


步骤 3: 方法名称是标准化的


MCP 规范定义了标准方法名:



步骤 4: 具体内容是个性化的


虽然方法名固定,但每个服务器返回的具体数据不同:


// SQLite服务器的工具{  "tools": [    {"name": "query", "description": "执行SQL查询"},    {"name": "list_tables", "description": "列出所有表"}  ]}
// Filesystem服务器的工具{ "tools": [ {"name": "read_file", "description": "读取文件"}, {"name": "write_file", "description": "写入文件"}, {"name": "search_files", "description": "搜索文件"} ]}
复制代码

3.3 协议发现机制

客户端如何知道服务器有哪些工具?


第一步:列举


客户端 → 服务器: {"method": "tools/list"}服务器 → 客户端: {  "tools": [    {      "name": "query",      "description": "执行SQL查询",      "inputSchema": {           // JSON Schema定义输入格式        "type": "object",        "properties": {          "sql": {"type": "string"}        }      }    }  ]}
复制代码


第二步:调用


客户端 → 服务器: {  "method": "tools/call",  "params": {    "name": "query",           // 使用第一步获得的工具名    "arguments": {"sql": "SELECT * FROM users"}  }}
复制代码


关键点:通过 JSON Schema,客户端知道如何正确调用工具,无需硬编码。

四、协议基础:如何通信?

MCP 基于 JSON-RPC 2.0 构建,这是一个成熟的远程过程调用协议。理解这一层对掌握 MCP 至关重要。

4.1 JSON-RPC 2.0 基础

消息类型


MCP 中有三种基本消息类型。


1. 请求(Request) - 期待响应


{  "jsonrpc": "2.0",           // 协议版本,必须是"2.0"  "id": 1,                     // 请求唯一标识(字符串或数字)  "method": "tools/list",     // 要调用的方法名  "params": {                  // 可选的参数对象    "cursor": "page2"  }}
复制代码


2. 响应(Response) - 对请求的回复


// 成功响应{  "jsonrpc": "2.0",  "id": 1,                     // 必须与请求的id相同  "result": {                  // 成功结果    "tools": [      {"name": "query", "description": "执行查询"}    ]  }}
// 错误响应{ "jsonrpc": "2.0", "id": 1, "error": { // 错误对象 "code": -32602, // 错误码(整数) "message": "参数无效", // 错误描述 "data": { // 可选的额外信息 "field": "cursor", "reason": "格式错误" } }}
复制代码


3. 通知(Notification) - 单向消息,无需响应


{  "jsonrpc": "2.0",  "method": "notifications/resources/updated",  // 通知方法名  "params": {                                   // 通知参数    "uri": "file:///project/data.json"  }  // 注意:没有id字段}
复制代码


标准错误码


MCP 使用 JSON-RPC 2.0 的标准错误码:


4.2 能力协商详解

能力协商是 MCP 连接建立的第一步,决定了整个会话中可用的功能。


初始化流程详解


阶段 1: 客户端发起初始化


{  "jsonrpc": "2.0",  "id": 1,  "method": "initialize",  "params": {    "protocolVersion": "2024-11-05",  // 客户端支持的协议版本    "capabilities": {                  // 客户端能力声明      "roots": {                       // 支持根目录        "listChanged": true            // 支持根目录变更通知      },      "sampling": {},                  // 支持LLM采样      "elicitation": {},               // 支持用户询问      "experimental": {                // 实验性功能        "customFeature": {}            // 自定义功能      }    },    "clientInfo": {                    // 客户端信息      "name": "MyAIApp",               // 程序名(必填)      "version": "1.2.0",              // 版本号(必填)      "title": "我的AI应用"             // 显示名称(可选)    }  }}
复制代码


阶段 2: 服务器响应能力


{  "jsonrpc": "2.0",  "id": 1,  "result": {    "protocolVersion": "2024-11-05",  // 服务器选择的协议版本    "capabilities": {                  // 服务器能力声明      "resources": {                   // 支持资源        "subscribe": true,             // 支持资源订阅        "listChanged": true            // 支持资源列表变更通知      },      "tools": {                       // 支持工具        "listChanged": true      },      "prompts": {                     // 支持提示模板        "listChanged": false           // 不支持列表变更通知      },      "logging": {}                    // 支持日志输出    },    "serverInfo": {                    // 服务器信息      "name": "sqlite-mcp-server",      "version": "2.1.0",      "title": "SQLite MCP服务器"    },    "instructions": "此服务器提供SQLite数据库访问能力"  // 可选的使用说明  }}
复制代码


阶段 3: 客户端确认就绪


{  "jsonrpc": "2.0",  "method": "notifications/initialized"  // 无id,这是通知}
复制代码


协议版本协商规则


客户端请求版本: "2024-11-05"    服务器支持?    ↙        ↘  支持        不支持   ↓            ↓返回相同版本  返回服务器支持的最新版本   ↓            ↓协商成功    客户端检查是否支持              ↙        ↘           支持        不支持            ↓            ↓         协商成功     断开连接
复制代码


实际示例:


// 场景1: 版本匹配客户端: "protocolVersion": "2024-11-05"服务器: "protocolVersion": "2024-11-05"  ✅ 成功
// 场景2: 服务器版本更新客户端: "protocolVersion": "2024-06-01"服务器: "protocolVersion": "2024-11-05" → 客户端检查是否支持2024-11-05 → 如果不支持则断开
// 场景3: 客户端版本更新客户端: "protocolVersion": "2025-01-01"服务器: "protocolVersion": "2024-11-05" → 客户端检查是否支持2024-11-05 → 如果支持则降级使用
复制代码


能力交集计算


初始化后,双方只能使用共同支持的能力:


客户端能力: {roots, sampling, elicitation}服务器能力: {resources, tools, prompts}   可用功能集合   ├─ 客户端 → 服务器: resources, tools, prompts   └─ 服务器 → 客户端: roots, sampling, elicitation
复制代码


示例:


# 客户端代码示例if server_capabilities.get("tools"):    # 服务器支持工具,可以调用    tools = await session.list_tools()else:    # 服务器不支持工具,跳过    print("服务器不提供工具功能")
if client_capabilities.get("sampling"): # 客户端支持采样,服务器可以请求 # (服务器端会检查这个能力) pass
复制代码

4.3 连接生命周期深入

完整的消息时序图


客户端                                            服务器  │                                              │  │  1. initialize (请求)                         │  ├──────────────────────────────────────>│  │     {protocolVersion, capabilities}          │  │                                              │  │  2. initialize (响应)                         │  │<──────────────────────────────────────┤  │     {protocolVersion, capabilities}          │  │                                              │  │  3. initialized (通知)                        │   ├──────────────────────────────────────>│  │                                              │  │═══════════ 正常操作阶段 ════════════        │  │                                              │  │  4. tools/list (请求)                         │  ├──────────────────────────────────────>│  │                                              │  │  5. tools/list (响应)                         │  │<──────────────────────────────────────┤  │     {tools: [...]}                           │  │                                              │  │  6. tools/call (请求)                         │  ├──────────────────────────────────────>│  │     {name: "query", arguments: {...}}        │  │                                              │  │  7. notifications/progress (通知)             │  │<──────────────────────────────────────┤  │     {progress: 50, total: 100}               │  │                                              │  │  8. tools/call (响应)                         │  │<──────────────────────────────────────┤  │     {content: [...]}                         │  │                                              │  │  9. notifications/resources/updated          │  │<──────────────────────────────────────┤  │     {uri: "file://..."}                      │  │                                              │  │═══════════ 关闭阶段 ═══════════           │  │                                              │  │  10. 关闭stdin                               │  ├─────────────X                             │  │                                             │  │                                          服务器退出
复制代码


初始化前的限制


initialized通知发送前:


客户端只能发送:


  • initialize请求

  • ping请求(用于保活)

  • ❌ 其他任何请求


服务器只能发送:


  • initialize响应

  • ping请求

  • logging通知(日志)

  • ❌ 其他任何消息


违反限制的后果:


// 客户端在初始化前调用tools/list请求: {"method": "tools/list"}响应: {  "error": {    "code": -32600,    "message": "会话未初始化"  }}
复制代码


超时和重试机制


请求超时:


import asyncio
# 设置30秒超时try: result = await asyncio.wait_for( session.call_tool("slow_operation", {}), timeout=30.0 )except asyncio.TimeoutError: # 发送取消通知 await session.send_notification( "notifications/cancelled", {"requestId": "123", "reason": "超时"} )
复制代码


进度通知重置超时:


# 当收到进度通知时,可以重置超时计时器timeout = 30  # 基础超时max_timeout = 300  # 最大超时(5分钟)
while True: try: msg = await wait_for_message(timeout) if msg.method == "notifications/progress": # 收到进度,重置超时 timeout = 30 except TimeoutError: # 超时处理 break
复制代码

4.4 传输方式对比

stdio 传输详解


优点:


  • ✅ 简单直接,适合本地开发

  • ✅ 进程隔离,安全性好

  • ✅ 自动管理生命周期

  • ✅ 无需网络配置


缺点:


  • ❌ 只能本地使用

  • ❌ 不支持多客户端

  • ❌ 调试相对困难


消息格式:


消息1\n消息2\n消息3\n
复制代码


每个 JSON 对象占一行,以\n分隔。


HTTP 传输详解


架构:


┌─────────┐         HTTP POST         ┌─────────┐│         ├──────────────────────────>│         ││ 客户端  │  请求/通知/响应(JSON-RPC) │ 服务器  ││         │<──────────────────────────┤         │└─────────┘     HTTP 响应/SSE流       └─────────┘             (application/json 或              text/event-stream)
复制代码


发送消息(POST) :


POST /mcp HTTP/1.1Host: localhost:8080Content-Type: application/jsonAccept: application/json, text/event-streamMcp-Session-Id: abc123
{"jsonrpc":"2.0","id":1,"method":"tools/list"}
复制代码


立即响应(JSON) :


HTTP/1.1 200 OKContent-Type: application/json
{"jsonrpc":"2.0","id":1,"result":{"tools":[...]}}
复制代码


流式响应(SSE) :


HTTP/1.1 200 OKContent-Type: text/event-streamMcp-Session-Id: abc123
id: 1data: {"jsonrpc":"2.0","method":"notifications/progress","params":{"progress":25}}
id: 2 data: {"jsonrpc":"2.0","method":"notifications/progress","params":{"progress":50}}
id: 3data: {"jsonrpc":"2.0","id":1,"result":{"content":[...]}}
复制代码


接收服务器消息(GET) :


GET /mcp HTTP/1.1Host: localhost:8080Accept: text/event-streamMcp-Session-Id: abc123Last-Event-ID: 42
复制代码


会话管理:


# 服务器端设置会话ID@app.post("/mcp")async def handle_mcp(request):    if request.method == "initialize":        session_id = generate_session_id()        return Response(            content=json.dumps(result),            headers={"Mcp-Session-Id": session_id}        )
# 客户端后续请求携带会话ID@client.requestasync def send_request(method, params): headers = {} if self.session_id: headers["Mcp-Session-Id"] = self.session_id return await http.post( "/mcp", json={"jsonrpc": "2.0", "method": method, "params": params}, headers=headers )
复制代码


断线重连:


async def connect_sse(last_event_id=None):    headers = {"Accept": "text/event-stream"}    if last_event_id:        headers["Last-Event-ID"] = last_event_id        async with httpx.stream("GET", "/mcp", headers=headers) as stream:        async for line in stream.aiter_lines():            if line.startswith("id:"):                last_event_id = line[3:].strip()            elif line.startswith("data:"):                data = json.loads(line[5:])                yield data, last_event_id
复制代码

4.5 实际通信示例

让我们看一个完整的 SQLite 查询场景:


// 1. 列出工具客户端 → 服务器:{  "jsonrpc": "2.0",  "id": 1,  "method": "tools/list"}
服务器 → 客户端:{ "jsonrpc": "2.0", "id": 1, "result": { "tools": [ { "name": "query", "description": "执行SQL查询", "inputSchema": { "type": "object", "properties": { "sql": {"type": "string"} }, "required": ["sql"] } } ] }}
// 2. 调用查询工具客户端 → 服务器:{ "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "query", "arguments": { "sql": "SELECT COUNT(*) FROM users WHERE active = 1" }, "_meta": { "progressToken": "query-123" // 请求进度通知 } }}
// 3. 服务器发送进度(异步通知)服务器 → 客户端:{ "jsonrpc": "2.0", "method": "notifications/progress", "params": { "progressToken": "query-123", "progress": 50, "total": 100, "message": "正在扫描users表..." }}
// 4. 返回查询结果服务器 → 客户端:{ "jsonrpc": "2.0", "id": 2, "result": { "content": [ { "type": "text", "text": "查询结果: 1,234个活跃用户" } ], "isError": false }}
// 5. 如果查询出错服务器 → 客户端(错误情况):{ "jsonrpc": "2.0", "id": 2, "error": { "code": -32603, "message": "SQL语法错误", "data": { "sql": "SELECT COUNT(*) FROM users WHERE active = 1", "error": "near "WHERE": syntax error", "position": 35 } }}
复制代码


这就是 MCP 通信的完整过程!通过 JSON-RPC 2.0,客户端和服务器可以进行结构化、类型安全的通信。

五、服务器能力:三种核心功能

MCP 服务器可以提供三种功能。

5.1 Resources(资源):应用决定用什么

资源就是数据,比如文件内容、数据库记录、API 响应。


谁控制: 应用程序决定把哪些资源提供给 AI


如何使用:


// 列出所有可用资源{"method": "resources/list"}
// 读取某个资源{ "method": "resources/read", "params": {"uri": "file:///project/main.py"}}
复制代码


资源 URI 示例:


  • file:///project/src/main.py - 文件

  • db://schema/users - 数据库表结构

  • git://commits/main - Git 提交历史

  • https://api.example.com/data - Web API


订阅变更: 可以订阅资源,当它变化时自动收到通知。


实际案例: Filesystem 服务器暴露资源


{  "uri": "file:///Users/alice/project/src/main.py",  // Python源文件  "name": "main.py",                                  // 文件名  "mimeType": "text/x-python",                        // 文件类型  "text": "import os\ndef main()..."                  // 文件内容}
复制代码


客户端 AI 可以读取这个资源,理解代码结构后提供重构建议或生成测试。

5.2 Prompts(提示模板):用户选择用什么

什么是 Prompt?


Prompt 就像是「对话模板」或「快捷指令」,把常用的复杂指令预设好,用户一键调用。用生活中的例子类比,就像微信的「快捷回复」或 IDE 中的「代码片段(Snippet)」。


为什么需要 Prompt?


场景 1:没有 Prompt 时


用户每次都要输入:"请分析这个Git仓库最近一周的提交,统计:1. 总提交次数2. 每个作者的贡献3. 修改的主要文件4. 是否有破坏性变更请用表格格式输出"
复制代码


场景 2:有 Prompt 后


用户只需:1. 点击 "/analyze-commits" 命令2. 选择分支 "main"3. AI自动执行完整分析
复制代码


Prompt 的数据结构


定义一个 Prompt:


{  "name": "analyze_commits",              // Prompt的唯一标识  "title": "提交历史分析",                  // 用户界面显示的名称  "description": "分析Git提交并生成报告",    // 功能说明  "arguments": [                          // 需要的参数列表    {      "name": "branch",                   // 参数名      "description": "要分析的分支名",      // 参数说明      "required": true                    // 是否必填    },    {      "name": "since",                    // 时间范围      "description": "起始日期(如:7 days ago)",      "required": false                   // 可选参数    },    {      "name": "author",                   // 作者过滤      "description": "只看某个作者的提交",      "required": false    }  ]}
复制代码


实际使用示例


步骤 1: 列出所有可用的 Prompt


// 客户端请求{  "jsonrpc": "2.0",  "id": 1,  "method": "prompts/list"}
// 服务器响应{ "jsonrpc": "2.0", "id": 1, "result": { "prompts": [ { "name": "analyze_commits", "title": "📊 提交历史分析", "description": "分析指定分支的提交历史,生成统计报告", "arguments": [ {"name": "branch", "required": true}, {"name": "since", "required": false} ] }, { "name": "review_code", "title": "🔍 代码审查", "description": "对代码进行质量审查和改进建议", "arguments": [ {"name": "file_path", "required": true}, {"name": "focus", "required": false} ] }, { "name": "explain_error", "title": "🐛 错误诊断", "description": "解释错误信息并提供修复建议", "arguments": [ {"name": "error_message", "required": true} ] } ] }}
复制代码


步骤 2: 用户在界面上看到这些选项


━━━━━━━━━━━━━━━━━━━━━━━━━━━━  可用命令:    📊 /analyze-commits     分析指定分支的提交历史       🔍 /review-code       对代码进行质量审查       🐛 /explain-error     解释错误信息并修复━━━━━━━━━━━━━━━━━━━━━━━━━━━━
复制代码


步骤 3: 用户选择并填写参数


用户输入: /analyze-commits
系统弹窗:┌─────────────────────────┐│ 提交历史分析 │├─────────────────────────┤│ 分支名 *: [main ] ││ 时间范围: [7 days ago] ││ 作者: [ ] ││ ││ [取消] [确定] │└─────────────────────────┘
复制代码


步骤 4: 获取完整的 Prompt 内容


// 客户端请求{  "jsonrpc": "2.0",  "id": 2,  "method": "prompts/get",  "params": {    "name": "analyze_commits",       // 使用哪个模板    "arguments": {                    // 用户填写的参数      "branch": "main",      "since": "7 days ago"    }  }}
// 服务器响应 - 返回完整的对话消息{ "jsonrpc": "2.0", "id": 2, "result": { "description": "分析main分支最近7天的提交", "messages": [ // 发送给AI的完整对话 { "role": "user", // 用户角色 "content": { "type": "text", "text": "请分析main分支在过去7天的Git提交历史。\n\n需要统计:\n1. 总提交次数\n2. 每个作者的贡献次数和代码行数\n3. 主要修改的文件列表\n4. 是否包含breaking changes\n5. 提交消息的规范性\n\n请用表格格式输出结果,并在最后给出改进建议。" } } ] }}
复制代码


步骤 5: 客户端将消息发送给 AI


用户看到AI正在分析...
AI返回:━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━📊 main分支提交分析报告(最近7天)
总提交次数: 23
作者贡献统计:┌────────────┬────────┬──────────┐│ 作者 │ 提交数 │ 代码行数 │├────────────┼────────┼──────────┤│ Alice │ 12 │ +543/-89││ Bob │ 8 │ +234/-45││ Charlie │ 3 │ +123/-12│└────────────┴────────┴──────────┘
主要修改文件:- src/api/users.py (8次修改)- src/models/user.py (5次修改)- tests/test_user.py (4次修改)
Breaking Changes: 无
提交规范性: 良好 (91%符合Conventional Commits)
改进建议:1. 建议增加单元测试覆盖率2. 部分提交消息过于简短━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
复制代码


Prompt 的内容类型


Prompt 消息中可以包含多种内容:


1. 纯文本


{  "role": "user",  "content": {    "type": "text",    "text": "请审查这段代码..."  }}
复制代码


2. 嵌入图片


{  "role": "user",  "content": {    "type": "image",    "data": "base64-encoded-image-data",     // 图片数据    "mimeType": "image/png"                  // 图片类型  }}
复制代码


3. 嵌入资源(引用 MCP 资源)


{  "role": "user",  "content": {    "type": "resource",    "resource": {      "uri": "file:///project/src/user.py",  // 资源URI      "mimeType": "text/x-python",      "text": "class User:\n    def __init__..."  // 资源内容    }  }}
复制代码


4. 多轮对话


{  "messages": [    {      "role": "user",      "content": {"type": "text", "text": "我想优化这段代码"}    },    {      "role": "assistant",                    // AI的回复      "content": {"type": "text", "text": "请提供代码内容"}    },    {      "role": "user",      "content": {        "type": "resource",                   // 用户提供代码        "resource": {...}      }    }  ]}
复制代码


Prompt vs Tool vs Resource 对比


特性          Prompt              Tool                Resource━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━控制者        用户主动选择         AI自动决定           应用程序控制
触发方式 用户点击命令 AI判断需要调用 应用自动附加 /analyze 或用户选择 返回内容 对话消息 执行结果 数据内容 (给AI的指令) (函数返回值) (上下文信息) 典型用途 工作流模板 执行操作 提供背景信息 快捷指令 查询数据 文件内容 示例 代码审查模板 执行SQL查询 项目README 错误诊断向导 发送邮件 数据库Schema 用户感知 ✅ 明显 ❓ 可能不知道 ❓ 透明的 (用户点击) (AI决定) (自动加载)
复制代码


Prompt 是预设的对话模板,通过参数化实现灵活应用,提升用户体验,并能与 MCP 其他能力组合形成完整工作流。


代码实现示例


# 服务器端:注册Prompt@server.list_prompts()async def list_prompts():    return [        Prompt(            name="analyze_commits",            title="📊 提交历史分析",            description="分析Git提交历史并生成统计报告",            arguments=[                {"name": "branch", "description": "分支名", "required": True},                {"name": "since", "description": "时间范围", "required": False}            ]        )    ]
@server.get_prompt()async def get_prompt(name: str, arguments: dict): if name == "analyze_commits": branch = arguments["branch"] since = arguments.get("since", "7 days ago") # 构建完整的提示消息 prompt_text = f"""请分析{branch}分支在{since}的Git提交历史。
需要统计:1. 总提交次数2. 每个作者的贡献3. 主要修改的文件4. 是否有breaking changes
请用表格格式输出。 """ return { "messages": [ { "role": "user", "content": {"type": "text", "text": prompt_text} } ] }
# 客户端:使用Promptasync def use_prompt(session, prompt_name, arguments): # 获取Prompt内容 prompt = await session.get_prompt( name=prompt_name, arguments=arguments ) # 将消息发送给AI for message in prompt.messages: ai_response = await send_to_ai(message) print(ai_response)
复制代码

5.3 Tools(工具):AI 自己决定用什么

Tool 就是可执行的函数,比如查询数据库、调用 API、写文件。


谁控制:AI 模型根据对话内容自己决定调用哪个工具


如何使用:


// 列出可用工具{"method": "tools/list"}
// AI调用工具{ "method": "tools/call", "params": { "name": "get_weather", "arguments": {"city": "北京"} }}
复制代码


返回结果:


{  "content": [{    "type": "text",    "text": "北京天气:晴,温度22°C"  }],  "isError": false}
复制代码

5.4 其他功能

补全


MCP 提供标准化的参数自动补全功能,支持为提示和资源 URI 提供上下文相关的建议,实现类似 IDE 的交互体验。服务器通过声明completions能力,支持对ref/promptref/resource两种引用类型的补全,每次最多返回 100 个按相关性排序的建议值,并可通过completion/complete请求获取补全结果。


日志


MCP 提供结构化日志消息传递机制,允许服务器向客户端发送包含严重性级别、可选记录器名称和任意 JSON 可序列化数据的日志通知。服务器需声明logging能力,支持遵循 RFC 5424 标准的日志级别(从 debug 到 emergency),客户端可通过logging/setLevel请求配置最低日志级别,服务器通过notifications/message通知发送日志消息。


分页


MCP 支持对可能返回大量结果集的列表操作进行分页处理,使用基于不透明游标的分页模型而非数字页码。服务器在响应中包含当前页结果和可选的nextCursor字段(表示更多结果存在),客户端可通过在请求中包含游标继续分页。支持分页的操作包括resources/listresources/templates/listprompts/listtools/list,客户端必须将游标视为不透明令牌。

六、客户端能力:反向请求

客户端不仅接收服务器的数据,也可以提供能力给服务器使用:

6.1 Sampling(采样):服务器请求客户端调用 AI

场景: 服务器在处理任务时,需要 AI 帮忙分析中间结果。


如何使用:


{  "method": "sampling/createMessage",  "params": {    "messages": [{      "role": "user",      "content": {"type": "text", "text": "这个数据正常吗?"}    }],    "modelPreferences": {      "hints": [{"name": "claude-3-sonnet"}],  // 建议用的模型      "intelligencePriority": 0.8,             // 要求智能程度      "speedPriority": 0.5                     // 速度要求    }  }}
复制代码


实际案例:Filesystem 服务器在搜索大量文件时,请求 AI 判断哪些文件最相关。

6.2 Roots(目录):告诉服务器工作范围

场景: 让服务器知道可以访问哪些目录。


如何使用:


{  "method": "roots/list"}
复制代码


返回:


{  "roots": [{    "uri": "file:///home/user/project",    "name": "我的项目"  }]}
复制代码


服务器知道只能在这个目录里操作,保护其他文件安全。

6.3 Elicitation(引导):服务器向用户询问信息

场景: 服务器需要用户提供额外信息才能继续。


如何使用:


{  "method": "elicitation/create",  "params": {    "message": "请提供您的GitHub用户名",    "requestedSchema": {      "type": "object",      "properties": {        "username": {"type": "string"}      }    }  }}
复制代码


用户响应:


{  "action": "accept",  // 或"decline"拒绝, "cancel"取消  "content": {    "username": "octocat"  }}
复制代码


实际案例: Git 服务器需要知道提交信息格式,弹窗问用户:"请选择提交规范:Conventional Commits/Angular/Custom?"

七、完整实战:从零构建天气查询 MCP

下面让我们从头到尾构建一个完整的 MCP 系统,包含服务器和客户端。

7.1 需求分析

目标: 构建一个天气查询 MCP 服务器,提供:


  • 资源: 城市列表

  • 工具: 查询天气、获取预报

  • 提示: 天气分析模板

7.2 服务器实现(Python)

第一步: 安装 MCP SDK


pip install mcp
复制代码


第二步: 创建服务器 (weather_server.py)


from mcp.server import Serverfrom mcp.types import Resource, Tool, Prompt, TextContentimport mcp.server.stdioimport httpx
# 创建MCP服务器实例server = Server("weather-server")
# 1. 定义资源:支持的城市列表@server.list_resources()async def list_resources(): """返回可用的资源列表""" return [ Resource( uri="weather://cities", name="支持的城市列表", mimeType="application/json", description="查询天气支持的所有城市" ) ]
@server.read_resource()async def read_resource(uri: str): """读取具体资源内容""" if uri == "weather://cities": cities = ["北京", "上海", "广州", "深圳", "杭州"] return { "contents": [{ "uri": uri, "mimeType": "application/json", "text": str(cities) }] }
# 2. 定义工具:天气查询@server.list_tools()async def list_tools(): """返回可用的工具列表""" return [ Tool( name="get_current_weather", description="获取指定城市的当前天气", inputSchema={ "type": "object", "properties": { "city": { "type": "string", "description": "城市名称,如'北京'" } }, "required": ["city"] } ), Tool( name="get_forecast", description="获取未来3天天气预报", inputSchema={ "type": "object", "properties": { "city": {"type": "string", "description": "城市名称"}, "days": {"type": "number", "description": "预报天数(1-3)", "default": 3} }, "required": ["city"] } ) ]
@server.call_tool()async def call_tool(name: str, arguments: dict): """执行工具调用""" if name == "get_current_weather": city = arguments["city"] # 实际项目中这里调用真实的天气API # 示例:使用模拟数据 weather_data = { "city": city, "temperature": 22, "condition": "晴", "humidity": 45 } return { "content": [{ "type": "text", "text": f"{city}当前天气:\n温度: {weather_data['temperature']}°C\n天气: {weather_data['condition']}\n湿度: {weather_data['humidity']}%" }] } elif name == "get_forecast": city = arguments["city"] days = arguments.get("days", 3) # 模拟预报数据 forecast = f"{city}未来{days}天预报:\n第1天: 晴,20-25°C\n第2天: 多云,18-23°C\n第3天: 小雨,16-20°C" return { "content": [{"type": "text", "text": forecast}] }
# 3. 定义提示模板:天气分析@server.list_prompts()async def list_prompts(): """返回可用的提示模板""" return [ Prompt( name="analyze_weather", description="分析天气趋势并给出建议", arguments=[ {"name": "city", "description": "城市名称", "required": True} ] ) ]
@server.get_prompt()async def get_prompt(name: str, arguments: dict): """获取提示模板内容""" if name == "analyze_weather": city = arguments["city"] return { "messages": [ { "role": "user", "content": { "type": "text", "text": f"请分析{city}的天气情况,并给出出行建议。包括:\n1. 温度是否适宜\n2. 是否需要带伞\n3. 穿衣建议" } } ] }
# 启动服务器if __name__ == "__main__": # 使用stdio传输(本地) mcp.server.stdio.run_stdio_server(server)
复制代码

7.3 配置服务器(Claude Desktop)

创建配置文件 ~/Library/Application Support/Claude/claude_desktop_config.json:


{  "mcpServers": {    "weather": {      "command": "python",      "args": ["/path/to/weather_server.py"]    }  }}
复制代码

7.4 客户端实现(Python)

如果要自己实现客户端:


from mcp import ClientSessionfrom mcp.client.stdio import stdio_clientimport asyncio
async def main(): # 连接到服务器 async with stdio_client( command="python", args=["/path/to/weather_server.py"] ) as (read, write): async with ClientSession(read, write) as session: # 1. 初始化连接 await session.initialize() print("✅ 连接成功!") # 2. 列出可用资源 resources = await session.list_resources() print(f"\n📁 可用资源: {len(resources.resources)}") for r in resources.resources: print(f" - {r.name}: {r.uri}") # 3. 读取城市列表资源 cities_resource = await session.read_resource( uri="weather://cities" ) print(f"\n🌍 城市列表: {cities_resource.contents[0].text}") # 4. 列出可用工具 tools = await session.list_tools() print(f"\n🔧 可用工具: {len(tools.tools)}") for t in tools.tools: print(f" - {t.name}: {t.description}") # 5. 调用工具查询天气 result = await session.call_tool( name="get_current_weather", arguments={"city": "北京"} ) print(f"\n🌤️ 查询结果:\n{result.content[0].text}") # 6. 获取预报 forecast = await session.call_tool( name="get_forecast", arguments={"city": "上海", "days": 3} ) print(f"\n📅 天气预报:\n{forecast.content[0].text}") # 7. 列出提示模板 prompts = await session.list_prompts() print(f"\n💡 提示模板: {len(prompts.prompts)}") for p in prompts.prompts: print(f" - {p.name}: {p.description}") # 8. 获取提示内容 prompt = await session.get_prompt( name="analyze_weather", arguments={"city": "广州"} ) print(f"\n📝 生成的提示:\n{prompt.messages[0]['content']['text']}")
if __name__ == "__main__": asyncio.run(main())
复制代码

7.5 运行效果

$ python weather_client.py
✅ 连接成功!
📁 可用资源: 1 - 支持的城市列表: weather://cities
🌍 城市列表: ['北京', '上海', '广州', '深圳', '杭州']
🔧 可用工具: 2 - get_current_weather: 获取指定城市的当前天气 - get_forecast: 获取未来3天天气预报
🌤️ 查询结果:北京当前天气:温度: 22°C天气: 晴湿度: 45%
📅 天气预报:上海未来3天预报:第1天: 晴,20-25°C第2天: 多云,18-23°C第3天: 小雨,16-20°C
💡 提示模板: 1 - analyze_weather: 分析天气趋势并给出建议
📝 生成的提示:请分析广州的天气情况,并给出出行建议。包括:1. 温度是否适宜2. 是否需要带伞3. 穿衣建议
复制代码

八、其他部分:MCP 基础协议的另一半

8.1 授权(Authorization)

MCP 授权规范定义了基于 HTTP 传输的安全授权机制,使 MCP 客户端能够代表资源所有者向受限制的 MCP 服务器发出请求。该规范基于 OAuth 2.1 及相关标准,实现了授权服务器发现、动态客户端注册和访问令牌管理。例如,客户端通过resource参数明确指定目标 MCP 服务器(如https://mcp.example.com),服务器则验证令牌是否专门为其颁发,确保令牌不会被误用于其他服务,从而防止"令牌传递"安全漏洞。

8.2 取消(Cancellation)

MCP 取消机制允许通过通知消息中止正在进行的请求,任何一方都可以发送notifications/cancelled通知来终止先前发出的请求。例如,当用户取消长时间运行的操作时,客户端可以发送包含请求 ID 和可选原因的取消通知,接收方应停止处理、释放资源且不发送响应。该机制考虑了网络延迟导致的竞态条件,允许接收方在请求已完成或无法取消时忽略通知,同时建议双方记录取消原因以便调试。


{  "method": "notifications/cancelled",  "params": {    "requestId": "123",    "reason": "用户取消"  }}
复制代码

8.3 Ping 机制

MCP 提供了可选的 ping 机制,允许任何一方验证对方是否仍然响应且连接存活。该机制通过简单的请求/响应模式实现,例如客户端发送{"jsonrpc":"2.0","id":"123","method":"ping"},服务器必须立即响应{"jsonrpc":"2.0","id":"123","result":{}}。如果在合理超时时间内未收到响应,发送方可以将连接视为陈旧并终止连接或尝试重新连接。实现应定期发送 ping 以检测连接健康状况,但应避免过度 ping 以减少网络开销。

8.4 进度跟踪(Progress)

MCP 支持通过通知消息对长时间运行的操作进行可选的进度跟踪。请求方可以在请求元数据中包含唯一的progressToken(如字符串"task123")来接收进度更新,接收方则可以发送包含进度值、可选总值和消息的notifications/progress通知。例如,文件上传操作可以发送{"progress":50,"total":100,"message":"正在上传文件..."}来指示完成百分比。进度值必须随每个通知递增,双方应实现速率限制以防止消息泛滥,并在操作完成后停止发送进度通知。


{  "method": "notifications/progress",  "params": {    "progressToken": "task123",    "progress": 50,      // 当前进度    "total": 100,        // 总量    "message": "正在上传文件..."  }}
复制代码

九、安全实践:必须重视

9.1 核心原则

1. 用户同意优先


  • 所有数据访问必须经用户明确同意

  • 所有工具调用前必须让用户确认


2. 数据隐私保护


  • 服务器只能看到必要的信息

  • 完整对话历史保留在宿主,不发给服务器


3. 工具安全


  • 工具代表代码执行,必须谨慎

  • 显示工具要做什么,让用户批准


4. 输入验证


  • 服务器必须验证所有输入

  • 客户端必须验证工具返回的结果

9.2 实际建议

服务器开发者:


  • 验证所有输入参数

  • 实现访问控制和速率限制

  • 记录操作日志供审计


客户端开发者:


  • 显示清晰的权限请求界面

  • 在调用工具前展示参数

  • 实现工具调用超时机制

十、MCP 生态:谁开发客户端?

关键认知: 在 MCP 生态中,客户端通常不是由下游开发者开发的,而是内置在 AI 应用平台中


  开发者开发MCP服务器  配置到AI平台(Claude/Cursor等)  AI平台内置的MCP客户端自动连接
复制代码


对于软件开发者来说,在 MCP 生态中的位置如下。


角色定位:┌─────────────────────────────────────────┐│ AI平台开发者(Anthropic, Cursor等)       ││ ────────────────────────────────        ││ 职责:                                   ││  ✅ 开发MCP客户端SDK                    ││  ✅ 集成到自己的AI应用中                ││  ✅ 提供配置界面                        ││  ✅ 管理MCP服务器生命周期               ││  ✅ 处理AI与MCP的交互逻辑               │└─────────────────────────────────────────┘                 ↓ 提供平台┌─────────────────────────────────────────┐│ MCP服务器开发者(你、我、社区)           ││ ────────────────────────────────        ││ 职责:                                   ││  ✅ 开发MCP服务器                       ││  ✅ 实现Resources/Tools/Prompts        ││  ✅ 编写使用文档                        ││  ✅ 发布到npm/PyPI                      ││  ❌ 不需要开发客户端                    ││  ❌ 不需要关心AI如何调用                │└─────────────────────────────────────────┘                 ↓ 使用服务┌─────────────────────────────────────────┐│ 最终用户(开发者、分析师等)               ││ ────────────────────────────────        ││ 职责:                                   ││  ✅ 安装需要的MCP服务器                 ││  ✅ 配置到AI平台                        ││  ✅ 使用AI完成任务                      ││  ❌ 不需要写代码                        │└─────────────────────────────────────────┘
复制代码

后记

MCP 让 AI 应用开发变得更简单、更安全、更强大。它不是银弹,但为构建可靠的 AI 系统提供了坚实基础。本文全部内容基于提示编写,欢迎交流讨论!

参考文献

  1. MCP 官方规范: https://modelcontextprotocol.io/specification/2025-06-18

  2. JSON-RPC 2.0: https://www.jsonrpc.org/

发布于: 3 小时前阅读数: 12
用户头像

拥抱技术,与开发者携手创造未来! 2018-11-20 加入

我们将持续为人工智能、大数据、云计算、物联网等相关领域的开发者,提供技术干货、行业技术内容、技术落地实践等文章内容。京东云开发者社区官方网站【https://developer.jdcloud.com/】,欢迎大家来玩

评论

发布
暂无评论
最新MCP规范解读,看这篇就够了!_京东科技开发者_InfoQ写作社区