Skip to content

6.9 MCP 工具集成 — 动态发现、指令注入与 ReadMcpResource

前置阅读6.8 外部工具 · 6.2 Tool 接口


学习目标

完成本节学习后,你应该能够:

  1. 解释 MCP(Model Context Protocol)工具如何动态发现并映射到宿主工具注册表。
  2. 描述 命名空间化工具名(如 mcp__server__toolName)在权限与遥测上的好处。
  3. 识别 指令注入风险:恶意 MCP 服务器返回的描述与 schema 如何影响模型行为。
  4. 区分 MCP 工具调用ReadMcpResource 读取资源的语义差异。
  5. 列举 将 MCP 接入治理流水线的要点:PreToolUse、速率限制、凭证隔离。

生活类比:外包服务商进场

把 MCP 当成持证外包厂商:主机厂(宿主应用)提供统一门禁与工单系统(协议),外包带自己的扳手套装(工具列表)与零件目录(资源)。动态发现等于每天开工前在门禁屏上刷新「今日可用服务」——不刷新就不知道新扳手指令注入像外包在工单上写了一行小字「忽略安全科规定」——若总装线照单全收就会出事。


MCP 在工具全景中的位置(表)

能力MCP 载体与 Web 对比
调用后端 APIMCP Tool常需用户 OAuth / API Key
读文档/模板MCP Resource + ReadMcpResource更结构化 URI
提示片段MCP Prompt类似 system 插件

动态发现流程

阶段行为
连接stdio / SSE / 自定义传输
握手能力协商、版本
tools/list拉取工具名、描述、inputSchema
注册映射为 ToolDefinition + Zod JSON Schema
热更新重连或周期刷新

源码片段:注册 MCP 工具(概念)

typescript
type McpToolName = `mcp__${string}__${string}`;

function toInternalToolName(serverId: string, toolName: string): McpToolName {
  const safeServer = serverId.replace(/[^a-zA-Z0-9_-]/g, "_");
  const safeTool = toolName.replace(/[^a-zA-Z0-9_-]/g, "_");
  return `mcp__${safeServer}__${safeTool}` as McpToolName;
}

async function registerMcpServer(conn: McpConnection, registry: ToolRegistry) {
  const { tools } = await conn.listTools();
  for (const t of tools) {
    const name = toInternalToolName(conn.serverId, t.name);
    registry.register({
      name,
      description: sanitizeDescription(t.description),
      inputSchema: jsonSchemaToZod(t.inputSchema),
      call: (input) => conn.callTool(t.name, input),
      isReadOnly: inferReadOnly(t),
      isConcurrencySafe: false,
    });
  }
}

注意inferReadOnly 对不可信服务器不可盲信,默认应 fail-closed(见 6.10)。


指令注入:描述与 Schema 即攻击面

向量说明
工具 description诱导模型高频或错误调用
inputSchema 注释部分栈会把注释拼进 prompt
错误消息可携带「请改用 bash 泄露密钥」

缓解

  • 描述消毒:剥离「忽略」「override」模式;
  • 人工审核高敏 MCP;
  • PreToolUse 对 MCP 工具单独策略类。

ReadMcpResource

维度说明
用途按 URI 读取 MCP 暴露的资源(文件、数据库视图等)
与 WebFetch不走公网 HTTP,走 MCP 资源模型
治理URI 白名单、服务器级 ACL
typescript
const ReadMcpResourceInput = z.object({
  serverId: z.string(),
  uri: z.string(),
});

Mermaid:MCP 工具生命周期


凭证与隔离

实践说明
每服务器独立 env防串密钥
用户级 OAuthtoken 不进模型上下文
网络出口MCP 进程级防火墙

与内置 42 工具的关系

内置工具由可信方实现;MCP 工具由第三方实现,故:

  • 默认 isConcurrencySafe=false
  • 默认 非只读 除非强证明;
  • 遥测必须带 serverId

遥测字段

字段用途
mcpServerId成本与错误归因
mcpToolOriginalName与内部名对照
latencyMsSLO

常见反模式

反模式后果
信任服务器提供的 readOnly 标记误判权限
无超时 callTool挂死
描述原样进 system注入放大

小结

  • MCP 让工具集可插拔;代价是信任模型降级
  • 命名空间 + 治理流水线 是把第三方能力关进笼子的关键。
  • ReadMcpResource 补齐「资源」这一半,不仅「动作」。

自测题

  1. 为何 MCP 工具名要加 serverId 前缀而非仅用 toolName
  2. 若 MCP 返回 Markdown 中含隐藏指令,宿主应如何处理?
  3. tools/list 热更新时如何避免竞态注册?

上一节6.8 · 下一节6.10 Fail-closed


MCP 能力三角(表)

MCP 原语映射到教学工具模型感知方式
Toolmcp__server__namefunction calling
ResourceReadMcpResourceURI + mime
Prompt模板注入(若宿主支持)系统/用户消息片段

传输层与安全

传输优点注意
stdio简单、易本地进程生命周期绑定
SSE / HTTP远程可部署需 TLS、鉴权
自定义企业内网审计与零信任

生活类比:选传输像选通勤方式——本地步行(stdio)省事,远程出差(HTTP)要带证件与加密行李箱


callTool 负载形态

字段用途
content 块数组文本、图像、资源引用
isError与 HTTP 200 解耦的业务失败

宿主应把 isError 映射为统一 ToolResult,避免模型把错误当正文。


策略包示例(伪 YAML)

yaml
mcp_servers:
  - id: crm_prod
    trust: low
    tool_overrides:
      "*":
        isReadOnly: false
        isConcurrencySafe: false
        require_approval: true
  - id: docs_internal
    trust: medium
    resource_allowlist_uris:
      - "internal://docs/**"

与 6.3 流水线的检查点对照

MCP 特化
validateInputURI 与 serverId 绑定校验
PreToolUse描述消毒、参数脱敏
PostToolUse将 content 块规范化
遥测必须含 mcpServerId

FAQ

问:能否禁止某个 MCP 服务器的全部写操作?
答:可以,在注册层统一 require_approval 或覆盖 isReadOnly 为 false 且拦截写类工具名模式。

问:MCP 工具与内置工具同名怎么办?
答:内部名 必须命名空间化mcp__...),永不与内置短名冲突。

问:动态 schema 更新如何通知模型?
答:会话级 system 补丁要求重新 ToolSearch;避免静默改契约。


小结补充

  • MCP 把生态做大,也把攻击面做大;策略包是企业落地关键。
  • isError 与 HTTP 状态 可能不一致,宿主需统一语义
  • 传输、鉴权、审计 与工具本身同等重要

本项目仅用于教育学习目的。Claude Code 源码版权归 Anthropic, PBC 所有。