工具网关 I — 工具注册表
工具网关 I — 工具注册表
日期: 2026-09-22 阶段: Phase 4 - 自建 Agent 平台×求职冲刺 标签: #tool-gateway #mcp #tool-registry
核心问题
Day 99 把平台五件套的边界画出来了,工具网关(注册+鉴权+审计)是其中最核心、也是我决定完整自建的一件。今天动手第一层:工具注册表——把「任意内部 API / 函数」变成「LLM 能发现、能调用的 MCP 工具」的契约层。
三个要回答的问题:
- 一个函数要变成 agent 工具,需要声明什么? 直觉只会想到「名字 + 入参」。但 MCP 规范(2025-06-18)的 Tool 对象远不止这两个字段——
outputSchema、title、annotations都有用途,少一个就会在生产里踩坑。 - 怎么从 P2 的
toolRegistry.ts升级? P2 那版是「进程内 MCP 协议形状」的教学装置,只支持inputSchema。平台层要支持多 agent 复用、工具发现、输出校验——缺口在哪? - 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 解耦) | ❌ 缺 |
description | ✅ | LLM 看 description 判断「这个工具适不适合当前任务」 | ✅ |
inputSchema | ✅ | JSON Schema,声明入参形状,调用前校验 | ✅ |
outputSchema | ✗ | JSON 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 的 McpToolRegistry(src/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 四类输入。这给我的注册表两个启发:
-
注册的来源应该是「声明式」的:AgentCore 不要你手写 handler,而是喂一份 OpenAPI spec,它自动生成工具。我的项目没有这么多外部 API,但 AML 工具(查制裁名单、查 PEP、算风险分、检索证据)应从一份集中的 OpenAPI-like 声明生成,而非散落在各处手写——这样
inputSchema/outputSchema有单一事实源。 -
语义工具选择: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而不只是list:list是给治理看的,search是给 LLM 用的。 我的项目工具数不多(个位数),但要在注册表里预留search(query, topK)接口并用 P2 已有的 token-overlap(semanticCache.ts的 Jaccard)做廉价语义近似,把这个边界讲清楚。
各方案对照:
| 维度 | P2 toolRegistry | 平台注册表(本日) | AgentCore Gateway |
|---|---|---|---|
| 注册来源 | 手写 spec+handler | 集中声明 + handler | OpenAPI/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 节字段)、PlatformToolRegistry(register/list/call/search)。复用而非复制 P2 的validate()(从mcp/toolRegistryimport JSON-Schema 校验器),在其上加outputSchema校验与ToolResult结构化结果。P2 文件保留不动(day99 已声明平台层是其演进、不删旧)。 search(query, topK)实现:复用 P2gateway/semanticCache.ts的jaccard()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+outputSchema。evidenceRetrieve的 outputSchema 强制{ chunks: [{id, text, source}] },让 day65 证据检索的结果可被 verifyAnchors(day72 grounded)直接校验。 - 诚实标注:
search的语义检索是 Jaccard 近似;annotations(readOnly/destructive)在 v1 只声明、不据其做权限决策(权限是 day101 鉴权层的事);注册表本身仍是进程内、无网络 transport、无 OAuth——那是 day101-102 的内容。
参考资料
- 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) - 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)
- AWS Blog — Introducing Amazon Bedrock AgentCore Gateway:OpenAPI/Smithy/Lambda 输入类型;1-click 集成 Salesforce/Slack/Jira;Translation/Composition/Secure Credential Exchange (2026)
- Obot AI — MCP Tool Discovery: How It Works:tool discovery 在 AI Gateway 层做,dynamic/identity-scoped/policy-aware/auditable;版本与稳定性元数据标注 (2026)
- 本仓库
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集中声明。