MCP 从原理到实战:构建 LLM 可控工具世界

基于最新 MCP 规范(Schema Version:2025-06-18)

随着大语言模型(LLM)成为智能系统核心引擎,模型"能理解"远比"能调用"来得更快。如何让模型可靠地调用工具、管理任务、处理资源,逐渐成为构建 Agent 系统的基础挑战。Model Context Protocol(MCP)应运而生,它提供了一套清晰、通用的调用契约,使 LLM 能够安全、高效、语义一致地与服务接口交互。Model Context Protocol(MCP) 由 Anthropic 于 2024 年底提出,随后被 OpenAI、Microsoft、Google 等巨头采纳,MCP 已成为连接 LLM 与真实世界能力的桥梁。它是 Function Calling 的演进,也是 Agent 系统迈向实用的重要支撑。

(图像来自:github.com/open-interp...

1 什么是 MCP?

Model Context Protocol(MCP) 是一种专为 LLM 打造的通用接口协议,基于 JSON-RPC 2.0 构建,目标是在不绑定厂商的前提下,让任何模型都可以发现、调用外部功能(tools)和数据资源(resources),统一接入各种工具链。

它的核心设计理念包括:

  • 🌐 标准化:不再每接一个 API 就要重写代码,MCP 统一了调用格式。
  • 🧩 模块化:每个 MCP Server 声明自己的能力和数据资源,供模型动态发现。
  • 🔐 安全性:支持权限控制、调用跟踪、进度上报、取消请求等功能。
  • 🤖 通用模型适配:Claude、ChatGPT、Gemini 等模型都可接入。

1.1 MCP 的三大核心组成

sequenceDiagram title MCP 协议下 LLM 工具调用与响应流程 participant User participant MCPClient as MCP-Client participant LLM participant MCPServer as MCP-Server User ->> MCPClient: 发送请求 MCPClient ->> LLM: 传递用户查询及工具描述 [1,2](@ref) LLM -->> MCPClient: 返回需调用的工具指令 MCPClient ->> MCPServer: 执行工具调用(HTTP/SSE/stdio)[1](@ref) MCPServer -->> MCPClient: 返回执行结果 MCPClient ->> LLM: 传递工具执行结果 [2](@ref) LLM -->> MCPClient: 生成自然语言响应 MCPClient -->> User: 返回最终响应

1. MCP Server ------ 能力的提供者

MCP Server 是一个"对外注册能力和数据"的服务节点。它通过 JSON Schema 格式声明:

  • 提供哪些 tools
  • 可访问哪些 resources(如数据库、文件系统)
  • 支持哪些 prompts(用于生成指令或上下文)

2. MCP Client ------ 模型端的接入适配器

MCP Client 运行在 LLM 所在的上下文中,负责:

  • 自动发现 MCP Server(支持本地、远程、注册中心)
  • 查询其功能(通过 list_tools()
  • 发起调用请求(call_tool()
  • 转换返回结构供模型理解(结构化 JSON 转自然语言)

OpenAI 的 Agents SDK 已经内置了 MCP Client,未来也可能集成到更多应用中,如 VSCode Copilot、Windows Copilot、浏览器助手等。

3. LLM Agent ------ 思考 + 执行的主体

Agent 是具备规划、推理、多步执行能力的 LLM 应用。例如:

  • 当用户问"帮我整理下 Downloads 目录里的 PDF 按时间排序",模型会:

    1. 使用 MCP Client 查询 MCP Server 中的文件工具;
    2. 调用 list_files(path="/Users/xx/Downloads")
    3. 过滤出 .pdf 文件,调用 get_metadata() 获取创建时间;
    4. 输出整理后的列表。

过去这些流程要写死在程序中,现在只需通过 MCP 发现 +调用即可。

sequenceDiagram title 多MCP智能体复杂任务编排流程 participant User as 用户 participant Agent as 智能体 participant MCP1 as 规划引擎 MCP Server participant MCP2 as 地址解析 MCP Server User ->> Agent: 提交任务(如:配送最优路径生成) Agent ->> Agent: 分解任务、规划调用流程 Agent ->> MCP2: 地址标准化(解析文本为结构化地址) MCP2 -->> Agent: 返回标准地址对象 Agent ->> MCP1: 调用规划引擎(输入结构化地址等参数) MCP1 -->> Agent: 返回初步规划方案 Note right of Agent: 智能体判断结果是否符合业务约束 Agent ->> Agent: 若不满足,则调整约束参数 Agent ->> MCP1: 重新调用规划引擎 alt 需要再次解析新地址 MCP1 ->> MCP2: 触发新一轮地址解析 MCP2 -->> MCP1: 返回新地址对象 end MCP1 -->> Agent: 返回多种备选规划方案 Agent -->> User: 交付最终多方案规划结果

1.2 Function Calling 与 MCP 有什么不同?

功能 Function Calling(OpenAI) MCP(开放协议)
接入方式 开发者自定义 schema JSON-RPC 标准化
适配对象 ChatGPT 系列 任意 LLM
功能列表 静态注册 动态发现(list_tools)
安全机制 需自行实现 支持取消 / 权限管理
工具执行方 通常是 OpenAI 服务器 用户本地或远程 MCP Server
生态整合 专属 API 开放注册中心(Microsoft、Claude、Gemini 等通用)

简而言之,Function Call 是 OpenAI 的"私有厨房",MCP 是全世界都可以使用的"厨房标准化接口"。虽然 MCP 带来了灵活开放的生态,但它也引入了新的攻击面,例如:

  • Tool Injection:恶意 MCP Server 提供诱导工具
  • Prompt Injection:在指令层干扰 LLM 行为
  • 能力误用:如获取敏感文件、操控本地进程等

因此,微软正在将 MCP 接入 Windows 系统安全沙盒中(AI Foundry);OpenAI 和 Anthropic 也在构建 MCP 能力注册中心,并引入权限认证、可调用范围管理等机制。

项目 内容
协议名称 Model Context Protocol(MCP)
发布者 Anthropic,OpenAI,Microsoft 等联合
功能定位 统一 LLM 调用外部工具与数据源的接口协议
技术基础 JSON-RPC 2.0 + JSON Schema
关键组件 MCP Server、MCP Client、Agent
典型用途 文件管理、数据库操作、自动化助手、浏览器插件
替代方案 OpenAI Function Calling(功能较弱)
安全风险 Prompt Injection、恶意 Server、权限失控等

1.3 参考

2 MCP Server 接口设计示例

基于最新 MCP 规范(截至 2025‑06‑18)

  • JSON-RPC + Streamable HTTP 是目前标准
  • tools/listtools/call 是基础接口
    • tools/list 用于工具发现,tools/call 用于执行操作,是 MCP 必选标准
  • 状态查询与取消建议使用 Notifications 优化
    • 允许通过消息推送进度或取消命令,而非持续轮询
  • 授权机制强制外置 OAuth
    • MCP Server 作为资源服务器,不负责颁发令牌,而由独立 Authorization Server 提供 OAuth 2.1 认证,包括 RFC9728、RFC8707 标准项,客户端需传入 resource 参数,同时 Serv er 必须校验并拒绝不合格令牌。
  • Resources 功能更完善
    • 对于资源,服务器可以声明能力是否支持 subscribe 和 listChanged,客户端可选择订阅变化。这使资源接口更加动态、响应性更强

2.1 MCP Manifest(/manifest.well-known/mcp/manifest

MCP Manifest 是模型调用服务的"蓝图",它不仅描述可用工具的参数结构,也包含调用行为提示、版本信息、资源结构等关键元信息。建议每个 MCP 服务通过 /manifest.well-known/mcp/manifest 提供静态清单,以供模型在调用前理解其能力边界。

json 复制代码
{
  "schema_version": "2025-06-18",
  "name": "vrp-planner-mcp",
  "title": "VRP Planner",
  "description": "多约束多车辆智能调度规划引擎",
  "version": "1.2.0",
  "last_updated": "2025-07-14T03:00:00Z",
  "capabilities": {
    "callback": {
      "field": "callback_url",
      "methods": ["webhook"],
      "description": "支持异步任务完成后的 Webhook 通知"
    },
    "notifications": {
      "supported": true,
      "events": ["progress", "complete", "cancelled"]
    },
    "elicitation": {}
  },
  "oauth": {
    "resource_metadata": "https://auth.example.com/.well-known/oauth-authorization-server",
    "resource_indicators_supported": true
  },
  "tools": [
    {
      "name": "create_scenario",
      "title": "Create Scenario",
      "description": "创建配送场景,包括车辆和工单",
      "inputSchema": { "$ref": "#/components/schemas/Scenario" },
      "returns": {
        "type": "object",
        "properties": {
          "scenario_id": { "type": "string", "example": "scenario-123" }
        },
        "required": ["scenario_id"]
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      },
      "_meta": {
        "example": {
          "name": "北京城配",
          "planning_date": "2025-07-15",
          "agents": [{ "id": "veh-1", "capacity": 100 }],
          "tickets": [{ "id": "t-1", "location": "LOC_A", "demand": 20 }]
        }
      }
    },
    {
      "name": "solve_vrp",
      "title": "Solve VRP",
      "description": "提交 VRP 求解任务",
      "inputSchema": {
        "type": "object",
        "properties": {
          "scenario_id": { "type": "string", "example": "scenario-123" },
          "solve_time": { "type": "string", "default": "PT30S", "pattern": "^PT\\d+S$", "example": "PT60S" },
          "callback_url": { "type": "string", "format": "uri", "example": "https://example.com/webhook" }
        },
        "required": ["scenario_id"]
      },
      "returns": {
        "type": "object",
        "properties": {
          "job_id": { "type": "string", "example": "job-abc-123" },
          "submitted_at": { "type": "string", "format": "date-time", "example": "2025-07-14T03:10:00Z" }
        },
        "required": ["job_id", "submitted_at"]
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false,
        "callbackSupport": true
      }
    },
    {
      "name": "query_vrp_status",
      "title": "Query VRP Status",
      "description": "查询 VRP 求解进度与状态",
      "inputSchema": {
        "type": "object",
        "properties": {
          "job_id": { "type": "string", "example": "job-abc-123" }
        },
        "required": ["job_id"]
      },
      "returns": {
        "type": "object",
        "properties": {
          "job_id": { "type": "string", "example": "job-abc-123" },
          "status": { "type": "string", "enum": ["SOLVING", "FAILED", "COMPLETED"], "example": "SOLVING" },
          "progress": { "type": "integer", "minimum": 0, "maximum": 100, "example": 45 }
        },
        "required": ["job_id", "status", "progress"]
      },
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "cancel_vrp_job",
      "title": "Cancel VRP Job",
      "description": "取消正在进行的 VRP 求解任务",
      "inputSchema": {
        "type": "object",
        "properties": {
          "job_id": { "type": "string", "example": "job-abc-123" }
        },
        "required": ["job_id"]
      },
      "returns": {
        "type": "object",
        "properties": {
          "cancelled": { "type": "boolean", "example": true },
          "cancelled_at": { "type": "string", "format": "date-time", "example": "2025-07-14T03:12:00Z" }
        },
        "required": ["cancelled", "cancelled_at"]
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": true,
        "openWorldHint": false
      }
    }
  ],
  "components": {
    "schemas": {
      "Scenario": {
        "type": "object",
        "properties": {
          "name": { "type": "string", "example": "北京城配" },
          "planning_date": { "type": "string", "format": "date", "example": "2025-07-15" },
          "agents": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/Agent" }
          },
          "tickets": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/Ticket" }
          }
        },
        "required": ["name", "planning_date", "agents", "tickets"]
      },
      "Agent": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "example": "veh-1" },
          "capacity": { "type": "integer", "example": 100 }
        },
        "required": ["id", "capacity"]
      },
      "Ticket": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "example": "t-1" },
          "location": { "type": "string", "example": "LOC_A" },
          "demand": { "type": "integer", "example": 20 }
        },
        "required": ["id", "location", "demand"]
      }
    }
  }
}

清单说明

  • schema_version, version, last_updated 用于 manifest 版本控制与缓存策略,推荐采用语义化版本机制。
  • 每个 MCP 工具需要用 JSON Schema 描述其输入参数结构和约束,工具定义包括唯一名称、描述和 inputSchema(参数模式)等
    • 使用 type, properties, required 等字段明确限定数据结构和数据类型。支持嵌套对象时,子属性也要各自定义 Schema。
    • 对于枚举值或特定格式,可利用 JSON Schema 的 enum、pattern 等约束。例如,一个分析CSV的工具参数可定义一个枚举列表:"operations": { "type": "array", "items": { "enum": ["sum","average","count"] } }。这指导模型只能提供允许的操作名称。
    • 提供字段描述或示例。在 MCP 工具定义中可以加入对参数的自然语言描述,帮助模型理解各参数含义。Anthropic 的实践建议在工具描述中包含使用示例,演示模型应如何提供参数。这有助于模型正确构造复杂输入。
    • 如果工具有返回值的预期结构,也应在 manifest 或文档中加以说明。例如 manifest 可用 "returns" 字段描述返回的数据类型,如返回一个对象数组。标准的输出格式使模型对调用结果有正确预期(例如知道返回的是文本还是 JSON 对象)。
  • 数据结构通过 components/schemas 统一定义,以保持一致性与复用性。

capabilities

capabilities.callback

声明服务端支持客户端提供 callback_url,用于异步通知任务完成或失败。适用于长任务(如 solve_vrp),当任务完成时,服务端会向客户端的 callback_url 发 POST 通知,无需客户端主动查询状态。

典型结构

json 复制代码
"capabilities": {
  "callback": {
    "field": "callback_url",
    "methods": ["webhook"],
    "description": "支持异步任务完成后的 Webhook 通知"
  }
}

MCP 支持异步调用,推荐使用 Webhook 模式回调模型:

  • 指定 callback_url 字段;
  • 可对回调增加 HMAC 签名字段或 token 以保证安全;
  • MCP 协议统一推荐在 callback_url 中发送 job_id + status 等最小字段,但每个工具的回调请求体可以基于它的上下文进行扩展定义
    • 可以在 manifest 的每个 tool 下增加 _meta.callbackPayloadSchema 或 callback_payload 字段,来定义callback的请求体数据结构。
capabilities.notifications

声明服务端支持通过 Streamable HTTP + Notifications 向客户端推送事件(例如:进度更新、完成通知、取消确认),客户端实时接收,无需轮询。

典型结构

json 复制代码
"capabilities": {
  "notifications": {
    "supported": true,
    "events": ["progress", "complete", "cancelled"]
  }
}

工具注解(Tool Annotations)

**工具注解(Tool Annotations)**是对每个工具行为的元信息提示,帮助 Client 和 LLM 更安全、高效地使用这些工具:

  • 安全性提示:客户端在调用此类工具前可提示用户确认,避免误操作。
  • 重试安全:当网络中断或超时,Client 可知道是否应该重试该调用;

假设有一个工具 delete_file

json 复制代码
{
  "name": "delete_file",
  "annotations": {
    "readOnlyHint": false,
    "destructiveHint": true,
    "idempotentHint": true,
    "openWorldHint": false
  }
}
Hint 名称 类型 含义说明 使用场景示例
destructiveHint boolean 是否会对系统产生破坏性修改,如删除、修改、重命名、发起变更等操作。 删除任务、取消订单、修改配置等
idempotentHint boolean 幂等性提示:重复调用不会改变最终结果,可安全重试。 删除资源(已删也视为成功)、查询状态等
readOnlyHint boolean 是否为只读操作,即不对系统状态产生任何更改。 查询状态、获取详情、列表获取等
openWorldHint boolean 是否访问开放系统或外部世界 ,如互联网或跨系统调用,涉及安全边界时应标记为 true 访问外部 API、调用外部模型、发送通知等
callbackSupport boolean? (可选)是否支持异步回调机制(如 webhook),便于客户端获知操作完成。 异步求解任务、模型执行任务等

注:

  • 幂等工具可安全重试,非幂等工具需要谨慎,以免重复造成副作用,若一个工具是"创建用户"的工具,多次调用会产生多个用户账户,此行为非幂等 ,应设置 idempotentHint: false
  • 若涉及外部系统权限、调用、安全敏感时设为 openWorldHint: true
  • 所有 MCP 工具函数接口都应根据行为意图明确标注这些 Hint,有助于 UI 层、代理调度层、权限策略系统做出合适决策。

OAuth 授权配置

Manifest 中需包含 OAuth 授权相关元数据,如:

json 复制代码
"oauth": {
  "resource_metadata": "https://auth.example.com/.well-known/oauth-authorization-server",
  "resource_indicators_supported": true
}

授权流程

  • 客户端访问受保护 endpoint 时,服务返回 HTTP 401 并带有 WWW-Authenticate header,指向 OAuth 元数据 URL。
  • 客户端根据 RFC9728 和 RFC8414,通过 .well-known/oauth-authorization-server 获取授权服务器地址、token endpoint 等信息。
  • 通过预定义方式获取access token,客户端在后续请求中加入 Authorization: Bearer <token>。服务端验证 token 的 issuer、scope、有效期等。
  • 根据 RFC7591,客户端首次连接可自动注册获取 client_id/secret 。

具体OAuth配置推荐使用Keycloak。

2.2 MCP JSON‑RPC 接口

tools/list(获取工具清单)

请求

json 复制代码
{ "jsonrpc":"2.0","id":"1","method":"tools/list","params":null }

响应

json 复制代码
{ "jsonrpc":"2.0","id":"1","result": [/* manifest.tools 的内容 */] }

客户端可使用该接口动态发现工具,构建调用选项。

tools/call(调用工具执行)

请求样例

json 复制代码
{
  "jsonrpc":"2.0",
  "id":"2",
  "method":"tools/call",
  "params": {
    "name": "solve_vrp",
    "arguments": { "scenario_id":"uuid-abc","solve_time":"PT60S" }
  }
}

method 字段表明调用工具,服务端据此找到对应工具并执行。

响应(正常)

json 复制代码
{ "jsonrpc":"2.0","id":"2","result": { "job_id":"job-001","submitted_at":"2025-07-13T10:05:00Z" } }
错误处理结构

推荐将错误编号、类型描述和上下文信息统一返回

响应(错误)

json 复制代码
{
  "jsonrpc":"2.0",
  "id":"2",
  "error": {
    "code": 4002,
    "type": "MissingParameter",
    "message": "参数 scenario_id 缺失",
    "traceId": "abc-123"
  }
}
任务取消

请求

json 复制代码
{
  "jsonrpc": "2.0",
  "id": "3",
  "method": "tools/call",
  "params": { "name": "cancel_vrp_job", "arguments": { "job_id": "job-001" } }
}

同步响应

json 复制代码
{
  "jsonrpc": "2.0",
  "id": "3",
  "result": { "cancelled": true, "cancelled_at": "2025-07-13T10:07:00Z" }
}

随后服务端也会通过 notifications/cancelled 主动推送事件

Notifications(事件推送,取代持续轮询)

对于可能长时间运行的工具操作,应考虑提供状态查询和取消的机制:

  • 客户端建议在发起调用后,同时开启 SSE 监听,以实时接收进度/完成/取消等事件;
  • 事件推送采用 JSON‑RPC 通知消息,无需 id 字段;
  • 事件类型由 method 指定,如 notifications/progress

进度推送

json 复制代码
{
  "jsonrpc": "2.0",
  "method": "notifications/progress",
  "params": { "job_id": "job-001", "progress": 80 }
}

完成推送

json 复制代码
{
  "jsonrpc": "2.0",
  "method": "notifications/complete",
  "params": { "job_id": "job-001", "result": { /* 求解结果数据 */ } }
}

取消推送

json 复制代码
{
  "jsonrpc": "2.0",
  "method": "notifications/cancelled",
  "params": { "job_id": "job-001", "cancelled_at": "2025-07-13T10:07:00Z" }
}

推荐工作流

  1. 客户端 POST tools/call 启动作业,并开启 SSE 通道(或通过 Streamable HTTP 持续接收事件)。
  2. 服务器通过 notifications/progressnotifications/completenotifications/cancelled 等推送任务实时状态,客户端无需主动轮询 query_vrp_status,只在断线或异常时可手动查询。
  3. 取消任务,依然通过 tools/call + cancel_vrp_job 工具实现。

2.3 资源接口

根据 MCP 2025‑06‑18 最新规范,结合 Notifications 与 Streamable HTTP 推荐实践,对资源接口与设计要点进行了调整和补充。最终优化内容如下:

MCP 资源(Resource)代表服务可提供的结构化数据内容,包括文件、API 响应、数据库记录等,均以唯一 URI 或 URI 模板标识。Manifest 中可统一声明资源类型及其 JSON Schema。

资源类型与 URI 命名

  • 静态资源 :如 "uri": "file:///var/reports/plan-2025-06.pdf"
  • 动态资源/模板 :如 "uriTemplate": "scenario://{scenario_id}/result"
  • 复合/分页资源 :如 "uriTemplate": "jobs://vrp/status/{status}?page={page}&size={size}"

资源 URI 应规范命名,例如:

  • file:// 本地静态文件
  • scenario://{id}/result 动态数据视图
  • logs://vrp/{date} 时序日志

所有资源均应采用清晰的协议前缀(如 file://db://logs://),便于客户端识别来源和处理方式。若资源涉及敏感字段,manifest 中可使用 "x-sensitive-fields": ["user_id", "price"] 提示客户端脱敏或限制暴露。

资源列表接口

resources/list (获取资源清单)

请求:

json 复制代码
{ "jsonrpc": "2.0", "id": "5", "method": "resources/list", "params": null }

响应:

json 复制代码
{
  "jsonrpc": "2.0",
  "id": "5",
  "result": [
    {
      "uri": "file:///var/reports/plan-2025-06.pdf",
      "name": "6月调度执行报告",
      "mimeType": "application/pdf",
      "size": 2183344
    },
    {
      "uriTemplate": "logs://vrp/{date}",
      "name": "VRP 系统调度日志",
      "mimeType": "text/plain"
    }
    // ...更多资源
  ]
}

推荐每个资源项包含:uri/uriTemplatenamemimeTypesize(如可用)、description 等元数据。Manifest 可进一步通过 JSON Schema 规范资源结构。

资源读取接口

resources/read(读取资源内容)

请求:

json 复制代码
{
  "jsonrpc": "2.0",
  "id": "6",
  "method": "resources/read",
  "params": { "uri": "logs://vrp/2025-07-13" }
}

响应:

json 复制代码
{
  "jsonrpc": "2.0",
  "id": "6",
  "result": {
    "content": "...2025-07-13 的日志内容...",
    "mimeType": "text/plain"
  }
}

文本型内容用 content 字段返回,二进制内容(如图片、Excel等)建议以 base64 编码置于 blob 字段,并指明 mimeType。 复合资源可返回数组,如目录下多个文件。

2.4 调用生命周期

sequenceDiagram autonumber participant 智能体 Agent participant MCP服务端 participant SSE通道 participant WebHook接口(callback_url) %% 工具发现与调用 智能体 Agent->>MCP服务端: tools/list → 获取工具清单 MCP服务端-->>智能体 Agent: 返回 tools + inputSchema 智能体 Agent->>MCP服务端: tools/call → 提交 solve_vrp 调用(附带 callback_url) MCP服务端-->>智能体 Agent: 返回 job_id:"job-001",任务提交成功 %% 主流程:使用 SSE 实时获取进度 智能体 Agent->>SSE通道: 建立 SSE 通道,监听任务进度 SSE通道-->>智能体 Agent: notifications/progress → 任务进度 20% SSE通道-->>智能体 Agent: notifications/progress → 任务进度 80% SSE通道-->>智能体 Agent: notifications/complete → 任务完成结果 %% 可选分支 A:使用 Webhook 接收通知 MCP服务端-->>WebHook接口(callback_url): notifications/complete → POST 最终结果 %% 可选分支 B:Agent 主动轮询获取结果 智能体 Agent->>MCP服务端: tools/call → query_status 查询任务状态 MCP服务端-->>智能体 Agent: 返回任务完成状态 + 结果数据

2.5 实践建议

  • 核心设计原则
    • Manifest 即协议契约:所有工具、资源、Schema 结构必须在 Manifest 中显式声明,便于客户端理解和自动调用。
    • 结构化 JSON + 明确 Schema :每个工具的输入 inputSchema 与返回结果结构应清晰、可验证,支持前端校验与自动补全。
    • 保持接口幂等与无状态:所有调用遵循 JSON-RPC 标准,接口无状态、返回结构统一,便于扩展与重试。
    • 安全提示通过 annotations 明确 :标注 readOnlyHintdestructiveHint 等属性,帮助模型/用户识别风险操作。
  • 异步任务与事件推送
    • 支持 callback_url 回调 :Manifest 中声明 capabilities.callbacks,工具参数允许指定异步通知地址。
    • 推荐集成 SSE 通道 :支持如 notifications/progressnotifications/complete 等实时事件,取代轮询,提升体验。
  • 资源访问建议
    • 使用 URI 与 uriTemplate:静态/动态/分页资源统一建模,支持结构化读取。
    • 可结合事件推送资源变更 :如 resources/changed 事件,让客户端感知数据刷新。

2.6 参考

3 动态生成满足类型约束的输入数据

MCP 的一大优势在于模型能够根据 manifest 自动构造正确格式的工具输入。为实现这一点,必须确保类型约束明确上下文提示充分,使 LLM 在决策调用时能够"填空作答",动态生成合法的 JSON 参数。

3.1 Schema 驱动输入生成

当客户端(LLM 主机)获取 MCP 服务的 manifest 后,会解析出各工具的参数Schema。在对话过程中,如果模型决定使用某工具,它会按照 Schema 组织参数。

  • 例如,manifest 指定参数 maxResults 是整数且默认为10,模型就会遵守此约束提供一个数字(除非有特定需要改变默认值)。
  • OpenAI 新推出的函数调用机制正体现了这一思路------开发者为函数提供 JSON Schema,模型会产出匹配该Schema的 JSON。这种自动约束生成减少了出错概率。

3.2 复杂对象的生成

当工具参数本身是复杂结构(如 Scenario 场景对象,包含嵌套字段),需要模型一次性构造嵌套 JSON。为辅助模型:

  • 逐层描述 :在 Schema 中对每个子字段都提供类型和含义说明。例如 Scenario 对象也许有 context(字符串)和 steps(步骤数组)等字段,就应在Schema里详细定义这些子元素的类型和要求。模型会依据这些定义填充每个字段,保证完整性。
  • 提供示例:如前述,工具描述中加入示例调用最为直接。如果模型看到例子里的 Scenario JSON,它更容易模仿格式输出。
  • 利用参数描述 :MCP 协议允许在参数 Schema 之外,还可在工具manifest里为每个参数写人类可读的说明。例如 Pieces MCP 服务的一个工具参数 time_ranges 带有说明,指导模型提供带 fromtophrase 三个属性的数组。模型据此提取用户问题中的时间范围,生成正确结构的 JSON。例如用户问"我昨天在做什么?",模型会按照描述将"昨天"解析成相应的 UTC 起止时间并填入 JSON。这种提示式约束极大提高了复杂输入的正确率。

3.3 传统业务数据映射

企业的数据分散在多张表、多个系统里,如果不能方便地把这些数据整理成标准的 JSON 格式,LLM 智能体就无法顺利调用算法和工具。只有把数据结构统一好,智能体才能理解业务、自动执行任务,让企业的数据真正用起来、发挥出更大的价值。在这里提出一种五步对接方法,具体如下:

A 读取 MCP manifest 的 inputSchema

  • 目的: 明确工具所需的 JSON 结构与类型约束,做到数据标准一目了然。
  • 关键点: manifest 的 inputSchema 应作为"源真理"(single source of truth)驱动后续所有映射和校验环节。
  • 建议: 若 manifest 有变更,应自动刷新本地 Schema 缓存。

B 导出业务数据表的记录(JSON格式)

  • 目的: 从现有数据库、API 或中台系统直接提取所需业务事实。

  • 关键点:

    • 关注导出字段、命名、嵌套与原有业务语义。
    • 尽量与 inputSchema 做初步字段对齐,减少后续处理难度。
  • 建议: 推荐以"单记录单 JSON"或"批量 JSON 数组"两种模式导出。

C 设计提示词,引导 LLM 自动生成转换代码

  • 目的: 利用大模型"推断能力"自动补齐字段映射、重命名、结构变换等代码(如 Python)。

  • 关键点:

    • 提示词需清楚描述原数据格式、目标 Schema,以及需处理的特殊字段/缺省值/类型转换。
    • 结合导出的业务数据,采用 few-shot(少样本)方式举例输入/输出,提升 LLM 生成准确率。
  • 建议:

    • 先人工 review LLM 生成的脚本,保证数据安全与业务合规。
    • 若常用转换可沉淀为通用 mapping 函数或脚本模板。

D 测试用例驱动 Schema 校验与逻辑校验

  • 目的: 保证最终 JSON 输入既合规又符合业务逻辑,避免后续工具调用异常。
  • 关键点:
    • 用 JSON Schema 自动校验格式和必填字段。
    • 逻辑校验可自定义(如:ID 唯一性、数量大于零、时间戳合法等)。
  • 建议:
    • 每次映射后都做 schema 校验,并输出详细报错定位。
    • 可以持续收集失败样例,用于优化 mapping/prompt 设计。

E 脚本的集成或 Function 化,为 LLM/Agent 提供标准化业务数据

  • 目的: 把数据转换能力封装为可复用的服务/脚本/Function(如 n8n Function、微服务、Serverless 函数等),为上游 LLM/Agent 调用提供标准入口。
  • 关键点:
    • 应保证接口语义清晰、幂等、输出稳定。
    • 支持输入参数灵活指定,如按业务主键、批量处理等。
  • 建议:
    • 可结合 CI/CD,对转换脚本/Function 持续回归测试与自动化部署。
    • 若有权限要求,需同步集成权限/审计控制。

实践建议

最大限度利用 LLM 自动化能力,减少人工编码与维护负担。

  • 既适应 Schema 变化,也能动态适配不同业务系统和表结构。
  • 全流程测试与校验环节保障安全与正确性。
  • 最终形态为"标准化 Function/服务",高度可扩展与集成。

需要关注的风险

  • LLM 生成代码需严格测试和审计,避免不符合业务规则或隐含安全隐患。
  • Schema 变更需自动同步,否则会引入数据格式漂移问题。
  • 业务逻辑校验要分层设计,测试用例的维护非常重要。

3.4 验证与纠错

服务端在接收到模型生成的输入后,应对其进行验证,确保满足 Schema 约定。可以借助 JSON Schema 校验库或静态类型工具(如 Pydantic)进行检查,在发现缺失字段或类型不符时返回结构化错误,让模型明白哪里不合规。模型通常会依据错误信息重试,并修正输出以满足约束。因此,清晰的错误反馈也是动态生成正确输入的重要环节。

此外,模型可能会依赖上下文动态调整输入。例如某些工具manifest是动态的 ,只有满足一定条件才出现额外参数。模型需要先通过 tools/list 获取最新Schema,再生成输入。这要求客户端实现上每次对话开始或 manifest 有变动时都刷新模型对工具的认知。Anthropic Claude 等实现支持 notifications/tools/list_changed 来通知工具列表变化,模型据此更新调用策略。

总结来说,让模型动态地产生合规输入的关键在于:完善的模式定义充分的语义提示。通过 MCP manifest,将输入输出格式精确定义,并辅以描述和示例,LLM 就能在推理中自动拟合这些约束,构造出满足类型要求的 JSON 对象。这种能力使智能代理能够安全地调用复杂操作(如规划场景Scenario的创建等)而不需要硬编码每种接口格式,大大提高了系统的灵活性和健壮性。

3.5 参考

4 降低 LLM Token 消耗的策略

在集成 MCP 工具时,需要密切关注上下文 Token的消耗,因为每增加一个工具及其说明,都会占用模型的提示窗口,进而增加费用和延迟。

  • 按需加载工具 :尽可能避免在不需要时向模型提供大量工具信息。当注册了 MCP 服务后,客户端通常会在每次对话请求中附带所有工具描述给模型。这意味着即便工具未被使用,其名称、说明、参数列表都会占用提示tokens。因此应提供机制让用户或系统启用/停用某些 MCP 工具。例如,在 Cursor 等应用中,如果当前任务不需要 Pieces 的长时记忆检索,就暂时禁用该 MCP 服务,以免无谓地增加 token 开销。只在需要时启用工具,能直接减少上下文长度。

  • 精简工具清单 :控制提供给模型的工具数量。每多一个可选工具,模型推理时就要考虑更多选项并处理相应描述,消耗更多token。对于从现有 API 自动生成的大量 MCP 工具,应筛除不必要的部分,保留最有用的操作。例如,如果通过 OpenAPI 规范自动生成了几十个工具,可以只保留其中模型可能用到的几个关键操作。工具越少,模型决策越高效,且提示中的无关内容也减少。每个保留的工具都应该有清晰的使用场景说明,以便模型准确判断何时调用。

  • 过滤无关数据 :当工具调用会返回大量数据时,服务端应对返回内容做处理,减少传给模型的冗余信息。例如,一个工具可能封装第三方API,返回很大的 JSON,其中只有部分字段对模型任务有用。此时 MCP 服务可对响应进行裁剪或汇总,只返回相关字段或更小的摘要数据给模型。实践案例表明,对API响应JSON过滤无关部分,可使提示tokens减少90%以上。同理,对于资源读取,如果文件过大,可以只返回片段或增加查询参数限制大小。总之,让进入模型上下文的数据尽量精简,既降低成本又提升模型处理效率。

  • 缓存和复用:利用缓存策略避免重复消费 tokens。对于重复出现的提示或响应,可以实现:

    • 提示缓存(Prompt Caching):如果相同的请求上下文已经向模型询问过,并得到结果,可缓存该模型回答,下次遇到类似请求直接复用结果而非再次调用模型。
    • 响应缓存:类似地,对于相同参数的工具调用结果,可以缓存起来,下次模型再调用同样的 MCP 工具时直接返回缓存内容,避免再次耗费模型 token。
    • 语义缓存:对提问或工具请求做语义哈希,如果新请求在语义上与之前类似,则复用以前结果。

    需要注意,缓存应与实时性需求权衡,并保持缓存内容不过期失效。同时,随着增加工具或API上下文,有时即使缓存也可能增加一定提示开销,因此缓存策略需结合实际流量和模型成本综合考虑。

  • 监控与优化 :部署LLM 可观测性工具监控每次调用的 token 使用情况,以发现瓶颈。观察哪些工具描述占用大量上下文、哪些响应文本冗长,从而有针对性地优化。例如,如果发现某一类工具的描述过长且很少被用,可以尝试简化描述或拆分工具集。在大规模代理应用中,引入 AI 网关可以帮助监控和限流,通过集中路由和缓存来降低总体token消耗。定期审计模型对话日志,筛查无效的长回答或重复对话,也有助于识别削减token的机会。

  • 优化上下文窗口利用 :MCP 的初衷之一就是更高效地使用模型上下文,以减少无效token。开发者应充分利用这一优势:把大量背景知识从提示中拿掉,改由工具按需提供。例如,与其在每次对话中都填充大段说明,不如将说明作为资源或提示模板,在需要时模型通过调用获取。这种延迟提供信息 的策略确保模型只在必要时才"看到"该信息,从而节省了平时的token占用。同样,如果需要模型多步推理,可考虑让模型调用自身的采样功能(MCP sampling特性)而非一次性输出所有步骤,也可以一定程度降低每步的提示长度。

综合以上策略,核心在于减少模型上下文中无效或次要的信息。每一次Agent-LLM交互都会消耗token并产生成本和时延,所以应努力让每个token都"物有所值"。通过精简工具和数据、聪明地调度何时提供何种信息,以及技术手段如缓存和监控,开发者可以将LLM集成的开销降到最低。正如经验所示,一个适度提供精确信息的MCP集成,能以更低的token成本获得更高质量的结果。

4.1 参考

5 接口重构

将传统微服务功能暴露为 MCP 工具,有时需要调整接口设计,以符合 MCP 的统一规范和 JSON Schema 要求。这种适配如果逐个手工改造,工作量和风险都很高。例如,一个老系统可能有众多 REST API,各自返回格式不同,也缺少机器可读的契约说明。那么要把它们纳入 MCP,就需为每个API编写 Schema、提供描述,并统一通过 manifest 公布------相当于对接口进行重构和数据格式转换。这被视为MCP落地的一大挑战之一。

5.1 接口重构是否必要

好消息是,未必需要对每个微服务API大动干戈。业界已经出现多种MCP 适配层和自动化工具,帮助将现有接口快速包装为 MCP 工具,尽量减少人工重构:

  • API 网关适配: 如华为云提出的方案,在微服务网关层进行一次性的协议转换。网关拦截内部服务的 OpenAPI/Swagger 描述或服务注册信息,自动生成 MCP 所需的manifest和工具模式。这样企业无需修改内部各微服务,只需在网关输出一个统一的 MCP Server 接口。该网关会承担把 MCP 请求转换为内部 REST 请求的工作,并把内部响应转回 MCP 格式。这一"一次适配,全局受益"的方法显著降低了改造成本。

  • 契约导出与描述生成: 除了纯手工编写 manifest,现在有工具能自动提取服务的接口契约并生成 Schema 描述。例如,一些框架可扫描代码或注解生成 JSON Schema,可以利用 AI 自动生成工具描述的尝试------根据接口定义和实现代码,预填描述文本,减轻人工编写负担。这种人机协同方式确保描述准确一致,同时减少了人工成本。

  • 开源适配库: 社区已有若干 MCP 工具库支持从现有应用直接导出工具。例如 FastMCP 提供了 from_fastapi()from_openapi() 等方法,可将已有 FastAPI 应用或 OpenAPI 文档直接转换成 MCP Server,实现自动包装。OpenAI 的 Agents SDK 也支持直接用 Python 函数定义工具,并自动根据函数签名生成 JSON Schema------这对于新开发的服务非常便利,开发者写好函数,Schema 和 manifest 便可由框架生成。还有像 LangChain 等代理框架也在集成 MCP 客户端/服务端,以简化适配过程。

因此,一般不建议为适配 MCP 而彻底推倒重写现有接口。相反,可以增设一层 MCP 接口或使用工具将它们包装起来。在保持原有微服务不变的情况下,通过 MCP Server 将其功能暴露给 LLM。这避免了高昂的系统重构代价和潜在风险。

Quarkus 提供的 MCP Server 扩展

  • Quarkus 提供了 quarkus-mcp-server-stdioquarkus-mcp-server-sse 扩展,支持标准的 stdioHTTP/SSE 传输协议,还引入了对 Streamable HTTP 的支持,是 Java 生态中使用最便捷的 MCP Server SDK。
  • 使用方式简单:通过 CDI 注解 @Tool, @Resource, @Prompt 宣告 server capabilities,同时 Quarkus 自动构建工具清单与 JSON-RPC 接口,实现声明式注册和运行时处理。

安全认证支持

  • Quarkus MCP Server 扩展支持通过 Quarkus OIDC(如 Keycloak、GitHub OAuth2)为 MCP Server 端点和工具调用启用安全认证。
  • 使用 @Authenticated 注解标示工具方法,仅允许认证用户访问。
  • 客户端(如 MCP Inspector 或 curl)可以通过 Bearer Token 调用工具。
  • Quarkus MCP Client 也支持将用户的权限令牌传递给 MCP Server 实现安全调用 。

支持多种通信形式

  • 支持 stdio 子进程模式、HTTP/SSE(现代流式 HTTP via Streamable HTTP)两种方式,适应控制台或云原生场景.
  • Quarkus 1.2.0+ 为首个支持 Streamable HTTP 的 Java SDK,大幅提升实时通信能力。

快速上手示例

xml 复制代码
<dependency>
  <groupId>io.quarkiverse.mcp</groupId>
  <artifactId>quarkus-mcp-server-sse</artifactId>
  <version>1.2.0</version>
</dependency>
java 复制代码
@ApplicationScoped
public class ServerFeatures {
  @Tool(description = "Convert text to lowercase")
  String toLower(@ToolArg(description="Text") String input) {
    return input.toLowerCase();
  }

  @Resource(uri="file:///data/config.json")
  BlobResourceContents config() { /* load file */ }
}

启动 Quarkus 应用后,它会自动:

  • 生成 manifest 清单并暴露 /mcp(Streamable HTTP)或 /mcp/sse(SSE)端点
  • MCP 客户端(如 Claude、LangChain4j、MCP Inspector)可用 JSON‑RPC 协议自动识别工具、调用执行。

5.2 何时需要重构

然而,在某些情况下适度的接口重构是值得的:

  • 现有接口设计对模型并不友好(比如需要多次往返才能完成一项任务,或请求/响应格式非常复杂),那么优化接口以更贴近"工具"概念可能提高 Agent 调用效率。
    • 例如,将多个细粒度API整合为一个更高阶的 MCP 工具,模型即可一次调用完成整个操作,而不必连环调用多个子步骤。
  • 这种重构可以看作针对 LLM 用例的接口升级,使之语义化和原子化,更易被模型正确使用。
  • 如果旧接口缺乏安全控制,也可借机在 MCP 层引入权限校验和输入验证,从而增强整体安全。

理想情况下,通过 MCP,我们希望实现工具接口统一与现有系统解耦 。完全重构虽非必须,但一定程度的契约梳理不可避免。建议的路径是:

  • 先利用自动化手段快速生成初版 manifest 和 Schema
  • 然后由开发者调整优化(如删除无用端点、补充示例、细化描述)。 这样既避免了全手工重构,又保证了生成的 MCP 接口高质量且贴合业务需求。

正如 MCP 标准所强调的,清单化的工具接口使不同语言、平台的系统都能无缝衔接,大幅减少"N对M"集成的负担。通过聪明地利用现有资源并适度演进接口设计,我们无需大规模重写代码,就能让智能规划引擎畅通无阻地调用各种微服务,实现AI时代的微服务生态升级。

5.3 参考

6 结语

MCP 协议为构建"模型可调用世界"奠定了基础,它规范了工具、资源、任务执行与异步反馈等关键组件,是实现 Agent-to-System 协同的核心协议之一。但 MCP 聚焦于"操作层"的接口设计,而在真正面向用户的场景中,还需与可视化交互、用户意图理解、流程指引等机制结合。

因此,我们正在探索另一项关键协议 ------ AG‑UI 协议,它旨在为 Agent 提供画布式、组件化的 UI 指令与数据绑定机制,使 LLM 不仅能调用工具,还能动态驱动 UI,让"人机共创"成为现实。

下一篇文章将全面介绍 AG‑UI 协议设计与实现,敬请期待。

相关推荐
Captaincc4 小时前
𝐂𝐮𝐫𝐬𝐨𝐫 𝐌𝐂𝐏 攻击现象:一句话,就能让你的私有数据库裸奔
mcp
AI大模型6 小时前
AI大模型智能体开发实战|基于 Dify + MCP 的理财助手
程序员·llm·mcp
围巾哥萧尘10 小时前
「掌握Trae IDE」 Trae + Git Agent + EdgeoneMCP,20 分钟用 Trae 完成网页开发和部署🧣
mcp
这里有鱼汤11 小时前
🚀逆天神器来了!MCP Chrome:超越Playwright,让AI接管你的浏览器,我当场震撼!
github·mcp
未来影子21 小时前
Spring Ai Alibaba Gateway 实现存量应用转 MCP 工具
gateway·springai·mcp
LeeAt1 天前
手把手教你构建自己的MCP服务器并把它连接到你的Cursor
javascript·cursor·mcp
harykali1 天前
Datawhale AI 夏令营:Task2从MCP入门到MCP Sever设计
算法·mcp
西柚配咖啡1 天前
aichat-core简化 LLM 与 MCP 集成的前端核心库(TypeScript)
mcp
喜欢吃豆1 天前
深入企业内部的MCP知识(四):FastMCP装饰器与类方法:正确结合面向对象与MCP组件的实践指南
人工智能·python·大模型·mcp