返回 AIPA 笔记
AIPA Day 100

工具网关 I — 工具注册表

工具网关 I — 工具注册表

2026-09-22
tool-gatewaymcptool-registry

日期: 2026-09-22 阶段: Phase 4 - 自建 Agent 平台×求职冲刺 标签: #tool-gateway #mcp #tool-registry

核心问题

Day 99 把平台五件套的边界画出来了,工具网关(注册+鉴权+审计)是其中最核心、也是我决定完整自建的一件。今天动手第一层:工具注册表——把「任意内部 API / 函数」变成「LLM 能发现、能调用的 MCP 工具」的契约层。

三个要回答的问题:

  1. 一个函数要变成 agent 工具,需要声明什么? 直觉只会想到「名字 + 入参」。但 MCP 规范(2025-06-18)的 Tool 对象远不止这两个字段——outputSchematitleannotations 都有用途,少一个就会在生产里踩坑。
  2. 怎么从 P2 的 toolRegistry.ts 升级? P2 那版是「进程内 MCP 协议形状」的教学装置,只支持 inputSchema。平台层要支持多 agent 复用、工具发现、输出校验——缺口在哪?
  3. AgentCore Gateway 是怎么做的? 它把「注册」抽象成 target(Lambda/OpenAPI/Smithy/MCP server),还内置「语义工具选择」。对照它,能看清我的注册表该补哪些维度。

关键内容

A. MCP Tool 对象:一个函数变成 agent 工具要声明什么

WebFetch MCP 官方 Tools 规范(2025-06-18),Tool 对象的完整字段(不是只有 name+inputSchema):

字段必填用途P2 toolRegistry 是否有
name唯一标识,LLM 用它做决策(名字烂 = 选错工具)
title人类可读显示名(UI 展示用,与 name 解耦)❌ 缺
descriptionLLM 看 description 判断「这个工具适不适合当前任务」
inputSchemaJSON Schema,声明入参形状,调用前校验
outputSchemaJSON Schema,声明输出形状,让 client/LLM 能结构化解析❌ 缺
annotations行为标注(如 readOnlyHint/destructiveHint),不可信除非来自可信 server❌ 缺

反直觉洞察①(最该补的不是入参 schema,而是 outputSchema):直觉是「把入参声明清楚、校验住,工具就安全了」。但 MCP 规范特别强调 outputSchema 的价值——「Servers MUST provide structured results that conform to this schema; Clients SHOULD validate structured results against this schema」。对 AML 这种合规场景,工具的输出比输入更需要被校验:一个「查制裁名单」工具若返回了形状不对的结果(少了 matchScore 字段、sanctioned 字段类型错),下游 SAR 草稿就会接地到错误数据,而这种错误在「输入校验」里完全看不见。输出 schema 是 grounded 的前置防线。 P2 装置只有 inputSchema,平台层必须补 outputSchema

MCP 工具结果还分两种内容(这也是 P2 缺的):

  • unstructured:放在 content 数组(text/image/audio/resource_link/embedded resource);
  • structured:放在 structuredContent(JSON 对象,须符合 outputSchema)。规范要求「a tool that returns structured content SHOULD also return the serialized JSON in a TextContent block」(向后兼容)。

对 AML,所有工具结果都该走 structuredContent(机器可读、可校验、可接地),同时冗余一份 text——这正好对应 day74 审计 trace 要求「单 span 也能自证」的纪律。

再说 annotations:MCP 给工具加 readOnlyHint/destructiveHint/idempotentHint 等行为标注,让 client 在调用前就知道「这个工具会不会改状态、改了能不能撤」。但规范有一句要命的警告——「clients MUST consider tool annotations to be untrusted unless they come from trusted servers」。标注是 hint 不是保证:一个被投毒的工具可以把「冻结账户」标成 readOnlyHint: true 来骗过下游放行。这意味着 annotations 只能用作 UI 提示和排序,绝不能拿来做权限决策——权限必须由 day101 的鉴权层独立判定,不信任工具自报。这条边界划错,就是 MCPTox 工具投毒(day52,攻击成功率最高 72%)的入口。

B. 从 P2 toolRegistry.ts 升级到平台注册表:差距清单

P2 的 McpToolRegistrysrc/agent/mcp/toolRegistry.ts)已经做对了很多:register/list/call、JSON-Schema 子集校验、JSON-RPC 信封、重名抛错、stateless tools/list 可缓存。平台层要补的是**「多工具规模化」和「输出可信」**两类能力:

P2 装置(进程内协议形状)          平台注册表(多 agent 复用)
─────────────────────────────────────────────────────────────
McpToolSpec {                     PlatformToolSpec {
  name                              name
  description                       title          ← 新增:UI 显示名
  inputSchema                       description
}                                   inputSchema
                                    outputSchema   ← 新增:输出校验(A 节)
                                    annotations    ← 新增:readOnly/destructive 行为标注
                                    tags[]         ← 新增:分类,喂语义工具选择(C 节)
                                    ownerAgent     ← 新增:哪个 agent 注册的(多 agent 治理)
                                  }
register(spec, handler)          register(spec, handler)  // 校验扩展:也校验 outputSchema 是 object
call(name, args) → unknown       call(name, args) → ToolResult  // 结果带 structuredContent + 输出校验
                                 search(query, topK)            ← 新增:语义工具发现(C 节)

注册 / 发现的核心算法(伪代码,扩展 P2 的 register/call):

register(spec, handler):
  assert spec.name matches /^[a-zA-Z][\w.-]*$/        # 沿用 P2 命名约束
  assert not tools.has(spec.name)                     # 沿用 P2 重名抛错
  assert spec.inputSchema.type == 'object'            # 沿用 P2
  if spec.outputSchema: assert spec.outputSchema.type == 'object'   # ★新增
  tools.set(spec.name, { spec, handler })

call(name, args) -> ToolResult:
  t = tools.get(name) or throw TOOL_NOT_FOUND
  errs = validate(t.spec.inputSchema, args)
  if errs: throw INVALID_PARAMS(errs)                 # 沿用 P2 输入校验
  raw = t.handler(args)
  if t.spec.outputSchema:                             # ★新增:输出校验
    outErrs = validate(t.spec.outputSchema, raw)
    if outErrs: return { isError: true, content: [text(outErrs)] }   # 不接地到坏数据
  return { structuredContent: raw,                    # ★新增:结构化结果
           content: [text(JSON.stringify(raw))],      # 冗余 text(MCP 向后兼容)
           isError: false }

关键:错误走两个通道(MCP 明确区分,P2 已部分实现)——协议错(未知工具/入参非法)走 JSON-RPC error(code -32601/-32602);工具执行错(业务失败/输出 schema 不符)走结果里的 isError: true不要把业务失败也抛成协议错,否则 LLM 收不到「工具跑了但结果是空名单」这类可恢复信息。

C. AgentCore Gateway 的 target 抽象与语义工具选择

WebFetch AgentCore Gateway 文档(2026),它把「注册」抽象成 target:「a target defines the APIs, Lambda functions, or other MCP servers that a gateway will provide as tools」,支持 OpenAPI / Smithy / Lambda / MCP server 四类输入。这给我的注册表两个启发:

  1. 注册的来源应该是「声明式」的:AgentCore 不要你手写 handler,而是喂一份 OpenAPI spec,它自动生成工具。我的项目没有这么多外部 API,但 AML 工具(查制裁名单、查 PEP、算风险分、检索证据)应从一份集中的 OpenAPI-like 声明生成,而非散落在各处手写——这样 inputSchema/outputSchema 有单一事实源。

  2. 语义工具选择:AgentCore 原话「Semantic Tool Selection enables agents to search across available tools... allowing agents to leverage thousands of tools while minimizing prompt size and reducing latency」。

反直觉洞察②(工具一多,最大的成本不是执行,而是「把所有工具塞进 prompt」):直觉是「注册表只要能 list 全部工具就行」。但当工具数到几十上百,把全部工具的 name+description+schema 塞进每次 LLM 请求的 system prompt,会线性吃 token、涨延迟、还稀释 LLM 的注意力(工具越多选得越差)。AgentCore 的解法是 search(query, topK)——按任务上下文先语义检索出最相关的 K 个工具,只把这 K 个塞进 prompt。这就是为什么平台注册表必须有 search 而不只是 listlist 是给治理看的,search 是给 LLM 用的。 我的项目工具数不多(个位数),但要在注册表里预留 search(query, topK) 接口并用 P2 已有的 token-overlap(semanticCache.ts 的 Jaccard)做廉价语义近似,把这个边界讲清楚。

各方案对照:

维度P2 toolRegistry平台注册表(本日)AgentCore Gateway
注册来源手写 spec+handler集中声明 + handlerOpenAPI/Smithy/Lambda/MCP server target
输入校验✅ JSON-Schema 子集✅ 沿用
输出校验✅ outputSchema
工具发现list(全量)list + search(topK)语义工具选择(thousands of tools)
多 agent单注册表ownerAgent 标注多 gateway endpoint
鉴权❌(day101 补)❌(day101 补)ingress + egress OAuth

设计要点/决策表

要点决策理由
Tool 字段name/title/description/inputSchema/outputSchema/annotations/tags/ownerAgent对齐 MCP 2025-06-18 完整 Tool 对象,补 P2 缺的输出维度
输出校验outputSchema 不符 → isError:true,不接地坏数据合规场景输出比输入更需校验(grounded 前置)
结果形状structuredContent 主 + content text 冗余MCP 向后兼容 + 单结果自证(呼应 day74 审计)
错误双通道协议错走 JSON-RPC error,业务错走 isError业务失败是 LLM 可恢复信息,不能抛成协议错
发现list(治理)+ search(topK)(喂 LLM)工具多时全量塞 prompt 吃 token、降选择质量
注册来源集中声明(OpenAPI-like),单一事实源对照 AgentCore target,避免 schema 散落

对本项目的落地

  • 新建 src/agent/platform/gateway/toolRegistry.ts(平台层,区别于 P2 的 src/agent/mcp/toolRegistry.ts):导出 PlatformToolSpec(A/B 节字段)、PlatformToolRegistryregister/list/call/search)。复用而非复制 P2 的 validate()(从 mcp/toolRegistry import JSON-Schema 校验器),在其上加 outputSchema 校验与 ToolResult 结构化结果。P2 文件保留不动(day99 已声明平台层是其演进、不删旧)。
  • search(query, topK) 实现:复用 P2 gateway/semanticCache.tsjaccard() token-overlap,对 query 与每个工具的 name + description + tags 算相似度,取 topK。头注诚实标注「token-overlap 是 embedding 语义检索的粗近似,生产接真实 embedding + 向量库」——与 semanticCache.ts 同纪律。
  • AML 工具集中声明:把 AML Copilot 用的工具(sanctionsScreen/pepLookup/riskScore/evidenceRetrieve)写进 src/agent/platform/gateway/amlTools.ts,每个带完整 inputSchema + outputSchemaevidenceRetrieve 的 outputSchema 强制 { chunks: [{id, text, source}] },让 day65 证据检索的结果可被 verifyAnchors(day72 grounded)直接校验。
  • 诚实标注search 的语义检索是 Jaccard 近似;annotations(readOnly/destructive)在 v1 只声明、不据其做权限决策(权限是 day101 鉴权层的事);注册表本身仍是进程内、无网络 transport、无 OAuth——那是 day101-102 的内容。

参考资料

  1. MCP 官方规范 — Server / Tools(2025-06-18):Tool 对象字段 name/title/description/inputSchema/outputSchema/annotations;tools/list(cursor 分页 + listChanged);tools/call(content + structuredContent + isError);错误双通道(协议错 JSON-RPC vs 工具执行错 isError);「Clients SHOULD validate structured results against outputSchema」 (2025-06)
  2. AWS — Amazon Bedrock AgentCore Gateway(官方文档):target = APIs/Lambda/Smithy/MCP server;「build, deploy, discover, and connect to tools at scale」;Semantic Tool Selection「leverage thousands of tools while minimizing prompt size and reducing latency」;ingress + egress auth (2026)
  3. AWS Blog — Introducing Amazon Bedrock AgentCore Gateway:OpenAPI/Smithy/Lambda 输入类型;1-click 集成 Salesforce/Slack/Jira;Translation/Composition/Secure Credential Exchange (2026)
  4. Obot AI — MCP Tool Discovery: How It Works:tool discovery 在 AI Gateway 层做,dynamic/identity-scoped/policy-aware/auditable;版本与稳定性元数据标注 (2026)
  5. 本仓库 src/agent/mcp/toolRegistry.ts(P2 进程内 MCP 协议形状,将被平台层复用 validate)、src/agent/gateway/semanticCache.ts(jaccard token-overlap,喂 search)、docs/aipa/day99-platform-component-boundaries.md(五件套边界)、docs/aipa/day72-sar-llm-draft.md(verifyAnchors grounded,消费 outputSchema) (2026-06)

SOTA 检查 (2026-06-11)

  • MCP Tool 对象(含 outputSchema/structuredContent)是 2026-06 工具契约的事实标准:MCP 2025-06-18 规范稳定,2026-07-28 终版进一步定 stateless core(P2 头注已记)。outputSchema + structuredContent 在 2025-06 引入后已被广泛采纳,是「工具输出可校验」的主线,本笔记据此补 P2 缺口。
  • 「语义工具选择」是工具规模化的 2026 共识解:AgentCore Gateway、各 MCP registry(Anthropic/GitHub/Microsoft 共维的公共目录)都把「按任务语义检索 topK 工具」作为标配,正因为「全量工具塞 prompt」在工具数上百时不可持续(反直觉洞察②)。本项目工具数少,但预留 search 接口对齐这一边界。
  • MCP Registry 生态在 2026 升温modelcontextprotocol/registry(社区驱动)+ Anthropic/GitHub/PulseMCP/Microsoft 共维公共元数据目录,印证 day99「注册表是平台必需件」的判断;本项目单 agent 暂不建组织级 registry,只在注册表里标 ownerAgent 预留多 agent 维度。
  • 过时认知警示:「工具只要声明 name + inputSchema 就够」过时——缺 outputSchema 会让结构化结果无法校验,合规场景下是 grounded 漏洞;「注册表只要能 list 就行」过时——工具规模化需 search
  • 待跟踪:MCP 2026-07-28 终版对 Tools 扩展(Tasks extension 把长任务挪出核心)落地后,长耗时工具(如全量证据检索)是否要从 tools/call 改走 tasks/*;AgentCore Gateway 的 OpenAPI→工具自动生成是否开放可复刻的 spec 格式,以对齐我的 amlTools.ts 集中声明。