/** * `McpServerSpec.toolFaces` —— MCP 工具的**面覆盖层**(core 7.8.0 [ref] §1.1/§2.7;声明面 * settings-schema 1.9.0)。一条规则、三条腿共用(请求腿 `task-mcp.ts normalizeServer` / center 腿 * `config-center/skills-mcp.ts resolveMcpServers` / 本地 config.d 腿 `config-provider.ts` 只做承载), * 与 {@link import("./mcp-source-label.js").readMcpSourceLabel} 同一个「判据单点、三腿一致」的形。 * * ## 分工(为什么本层**不**复述 core 的那张表) * 覆盖层的**语义**校验属 core 的摄入侧,而且只有它做得了:`pathTarget.param` 是不是这只工具**物化后 * schema** 的顶层属性、审批卡要的键在不在、`ruleFace` 指的是不是标量键 —— 三件都要看远端 server 真发 * 回来的 schema,本层手里根本没有。core 的处置也已经定死:不合 ⇒ **这只工具在这条腿上不挂**,并通告 * 一次 `config.tool_face_invalid`(亲读 `dist/core/runner/tool-face-overlay.js` 的 `return undefined` 臂 —— * 该文件 d.ts 头注里那句「the tool mounts BARE」是**过期**的,以实现为准)。 * * 本层因此只做 core 的校验器**假定成立**的那一件事:**JSON 结构合不合 TS 型**。理由是硬的 —— core 的 * `toolFaceProblem` 是按 TS 型写的,`renderHints.summaryParams` 是个字符串时它会 `.find is not a function` * 地把 prepare 整条炸掉,`ruleFace.params` 是个数字时 `for...of` 直接抛。原样透传一份任意 JSON 不是 * 「解析透明」,是把类型错误变成运行期崩溃。 * * 闭词(`family` / `pathTarget.access` / `renderHints.approvalCard`)判据**取自 core 的导出常量** * (`TOOL_FAMILIES` / `TOOL_PATH_ACCESSES` / `TOOL_APPROVAL_CARDS`),本仓一个词都不枚举 —— 与 * `MCP_DELIVERY_VERDICTS` 的消费姿势同款:词表单一属主在 core,抄一遍就是抄了一张会漂的表。 * * ## 坏形的方向:**整条 server 不进**(fail-closed) * 🔴 这一条被 codex 对抗复审 r1 的 [high] **翻正过一次**,把翻正的理由留在这里,因为它是本模块最容易 * 想错的一格:初版按 `contentOrigin` 的先例判「丢声明、留 server + 留痕」,而那条先例管的是**放宽**键 * (丢它 ⇒ 回落到过度标记 = fail-closed 的那一侧)。`toolFaces` 是**收窄**声明:它声明的 `pathTarget` * 正是敏感路径策略、写围栏、编辑跟踪读的那一格 —— 丢掉它,那只工具就**无面挂载**,运维声明的保护 * 静默失效。codex 实测(同一只 MCP 写工具、同一个 `/work/.env` 目标,仅把 `renderHints.summaryParams` * 从数组写成字符串):`createSensitivePathPolicy` 从 deny 变成 allow。一条 warn 补偿不了这个方向。 * * 所以判据回到本仓对**收窄键**已经成文的那一条(`task-mcp.ts` 的 `allowTools`,[ref] / [ref] codex F1): * **一个键在场就必须合法,否则这条 server 整条不进**。三问逐条: * ① 谁需要 —— 在部署侧给 MCP 工具声明写围栏 / 审批卡 / 显示名的运维; * ② 谁受伤 —— 覆盖层写错的那台 server:它**整条**不挂(请求腿进 `dropped` 并点名上报,center 腿跳过 * 该条、兄弟 server 照挂)。代价是真的(少一套工具),但它**响亮且立刻**;反方向的代价是一条 * **静默失效的安全声明**,而后者按 [ref] 不许存在于安全轴上; * ③ 拿什么补偿 —— 报出来的名字本身:请求腿 `mcp_injection_dropped{dropped:[name]}`、center 腿 * `sema_registry_mcp_tool_faces_invalid{server,problem,note}` 一条点名 warn(`problem` 里带工具名与哪一段不合;运维照着改那一行即可)。 * 每只工具的面是**整只**判(core 明写 "applied whole or not at all");而**任一只**判不出形 ⇒ 整条 server * 不进 —— 「留下好的那几只」同样是让坏的那一只无面挂载,与整张丢没有区别。 */ import { type McpServerSpec } from "@sema-agent/core"; type ToolFaces = NonNullable; /** * 一条 server 条目的 `toolFaces` 判形结果: * · `{ ok: true, faces }` —— 合形(`faces` 缺席 = 这台 server 没有覆盖层,或声明了一个**空**覆盖层: * 两者对 core 同义,都不铸键); * · `{ ok: false, problem }` —— 判不出形,**整条 server 不进**(方向与理由见模块头注),`problem` 是 * 给运维的一句点名(只带工具名与哪一段不合,不带面的值)。 */ export type McpToolFacesRead = { ok: true; faces?: ToolFaces; } | { ok: false; problem: string; }; /** * 从任一腿的原始条目里读出 `toolFaces` —— **三条腿共用的唯一判据点**。 * 缺席 ⇒ 合形无覆盖层;整只不是对象 / **任一只**工具的面判不出形 ⇒ `ok:false`(调用方整条 server 不收)。 */ export declare function readMcpToolFaces(v: unknown): McpToolFacesRead; export {}; //# sourceMappingURL=mcp-tool-faces.d.ts.map