Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 53 additions & 9 deletions llmdoc/app-runtime/mcp-interop.mdx
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
---
description: MCP 上游互操作指南:modern/legacy era 协商与缓存键、入站 /~mcp 无状态适配、互操作安全边界与四层验证
description: MCP 固定入口与按需发现调用、CLI 能力对等、目标 schema 与结果保真、modern/legacy 上游互操作及入站客户端验证
kind: guide
relations:
requires:
- protocol/htbp-contract.mdx
related:
- device/durable-mailbox.mdx
- cli/argument-contract.mdx
code:
paths:
- packages/plugins/src/feishu/feishuMcp.ts
- packages/app/src/providers/mcp.ts
- packages/app/src/mcpServer.ts
- packages/app/src/mcpInvoke.ts
- packages/app/src/jsonSchemaValidator.ts
- packages/app/src/routes/mcp.ts
- packages/app/test/mcp.integration.test.ts
- packages/app/test/mcpArguments.test.ts
- scripts/verify-mcp.ts
---

# MCP 上游互操作
# MCP 入口与上游互操作

MCP 是外部协议兼容面。项目内部历史状态可删,但现代/旧版 MCP 服务端的真实差异必须保留。

Expand All @@ -23,17 +32,47 @@ MCP 是外部协议兼容面。项目内部历史状态可删,但现代/旧版
- 不复用上游 session,不存在 `mcpsession` 或 `forceFresh` 控制面。
- 入站 `/~mcp` 是无状态适配器,不依赖 `Mcp-Session-Id`。

### 入站 device delivery 投影
## 入站固定入口与 CLI 对等

入站 `/~mcp` 不为 Mailbox 暴露第二个 enqueue tool。对 metadata `delivery:'both'` 的设备工具,input schema
增加 `~delivery` enum `realtime|mailbox|fallback`;mailbox-only 工具只投影 `mailbox|fallback`;realtime-only
工具不增加该字段。MCP call 携带的 `~delivery` 原样进入同一个普通 command POST,复用 app 的授权、能力
检查与 dispatch certainty,而不是在 MCP adapter 里再实现 fallback。
`/~mcp` 只暴露网关固定能力,业务工具按完整 HTBP command path 发现和调用。`tools/list` 不访问上游、
不遍历可见工具树,也不因挂载数量或路径长度增长;有 search capability 才广告 `tb_search`。固定入口
仍逐请求认证,窄 SK 不靠缩减入口名表达权限,而在发现与调用目标时执行同一可见性和 scope 检查。

| MCP 入口 | 对应 CLI | 契约 |
|---|---|---|
| `tb_search` | `tb search` | 复用全局搜索选项,默认 compact,选中后按需取 schema |
| `tb_help` | `tb help` | 浏览节点或完整命令,取得参数、权限、副作用与 delivery metadata |
| `tb_list_nodes` | `tb ls` / `tb tree` | 按路径与有界深度浏览,不要求 search capability |
| `tb_call` | `tb call` | `{path,args,delivery?,ttlSeconds?,idempotencyKey?}`,args 缺省空对象 |
| `tb_device_operations` | `tb device op` | 顶层 `action: list|get|cancel`,其余参数按 action 校验 |

发现返回的 `cmds[].path` 是调用身份,直接放进 `tb_call.path`;已知路径与契约时可直接调用。路径不是 URL
或 shell 命令。工具、Context、Skill 和可调用的 `system/*` 管理命令复用既有 handler 与权威授权,MCP 不
新增权限旁路。对等范围是网关工具使用能力;本机文件读写、daemon、安装与 localhost 配对属于 CLI 宿主
职责,不要求远程 MCP 模拟。

调用只解析目标 owner:本地先按 Registry 最长前缀解析,remote 只读取选定命令的 help;需要上游清单时
只读取目标 provider,不能为一次调用枚举其他节点。仅编译、校验选定命令的 schema;目标 schema 畸形时
fail closed,但无关工具的畸形 schema 不能阻塞入口发现或合法调用。设备未声明 `cmds` 时仍允许与 CLI
一致的已知 realtime 路径;显式空清单表示没有命令,不得编造 schema 或绕过 allowlist。

MCP 上游调用复用 provider 执行路径,保留原生内容块、`structuredContent` 与 `isError`;不能先经普通
HTTP JSON 响应再重建而丢失图片或业务错误。其他命令继续复用普通 HTBP 执行入口与错误语义。通用调用
和 operation 管理可包含写入或取消,入口 annotation 不能统一标为只读;目标 effect/confirm 从 help 读取,
权限由服务端判定。

### Device delivery 与 operation 闭环

`tb_call` 的 `delivery` 转成普通 command POST 的 `~delivery`,TTL 与幂等键分别进入既有 query/header;
后两者只允许显式 `mailbox|fallback`。参数控制层不修改业务 schema,也不投影第二套设备工具或 enqueue
入口。app 权威复核 command delivery 能力并执行 dispatch certainty,MCP adapter 不自行实现 fallback。

显式 delivery 的成功结果保持可判别:HTTP 200/realtime 投影为
`{delivery:'realtime',result}`,HTTP 202/mailbox 投影为 `{delivery:'mailbox',operation}`。`fallback` 只在
gateway 明确判断 `not_dispatched` 时入队;设备业务错误属于 completed,unknown 也不入队。这样一次 MCP
工具调用即可请求 fallback,同时避免 Agent 在 MCP 层 call 失败后再猜测是否 enqueue。
返回的 operation 通过 `tb_device_operations` 查询、分页或取消,复用 owner/admin 授权。设备身份必须使用
原始 `deviceId`;claimed cancel 只是协作式请求,result_unknown 或可能执行过的 expired 都不证明重试安全。

## 互操作边界

Expand All @@ -59,10 +98,15 @@ gateway 明确判断 `not_dispatched` 时入队;设备业务错误属于 compl
## 验证层次

1. provider 单测:era 探测、TTL、能力约束、错误映射。
2. app/server 集成:`~help`、单工具披露、envelope 调用、缓存命中。
3. consumer E2E:用真实 MCP client 连接 `/~mcp`,验证初始化、发现与调用。
2. app/server 集成:`~help`、单工具披露、完整命令路径与裸 arguments 调用、缓存命中。
3. consumer E2E:用真实 MCP client 连接 `/~mcp`,验证 modern/legacy、固定入口,以及 help 返回路径直接
进入 tb_call 的闭环;窄 SK 仍看到固定入口,但发现隐藏越权目标、已知越权路径调用被拒。
4. 真实上游仅在明确提供 URL/凭据时运行 `pnpm verify:mcp`。

排查连接先区分入站 `/~mcp` 与出站 MCP provider。CLI 经 HTBP 调用上游成功不能替代入站消费者验证;
官方 SDK 成功也不能证明尚未复现的用户客户端链路兼容。验收还须证明工具目录规模有界、入口发现不访问
上游、无关上游故障与 schema 错误被隔离,以及设备 delivery/operation 与原生 MCP 结果语义完整。

注意:不要为了单个失常服务端加入无边界 fallback。先确认它是协议时代差异还是上游 bug,再把兼容限制在 provider 层并加回归测试。

JSON Schema runtime validation 使用 MCP SDK 官方 AJV adapter。不同上游 schema 即使复用同一 $id 也必须隔离 compiler,避免缓存把工具 A 的 schema 误当成工具 B;方言按 schema 声明分派,不能以删除旧校验器为由跳过校验。验证 adapter 只在服务端 app,neutral SDK 不引用 AJV/Node 实现。
Expand Down
4 changes: 4 additions & 0 deletions llmdoc/cli/argument-contract.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,10 @@ CLI 在 workspace 内 typecheck、Vitest 与 tsup 三个解析面都必须覆盖
| 直接 API / HTBP | 权限、状态码、响应形状与错误码 |
| `tb` CLI | 参数可达、帮助文本、stdout/stderr、退出码 |
| Dashboard | 字段来源、校验、敏感值处理与成功后刷新 |
| MCP | 固定入口按需发现、完整路径调用、目标授权、结果与 device operation 语义 |

CLI、Dashboard、MCP 的对等以网关工具使用能力为边界。远程 MCP 不要求模拟 CLI 的本机文件、daemon、
安装和 localhost 配对;固定入口与验收约束见 [MCP 入口](../app-runtime/mcp-interop.mdx)。

Store 的当前对等面是 `system/store`、上述 `tb store` 命令与 Dashboard `/manage/store`。Device SDK 的
`call.uploadObject` 是设备数据面,不要求 CLI 模拟 call capability,但最终仍消费同一 create/relay/direct
Expand Down
6 changes: 4 additions & 2 deletions llmdoc/device/durable-mailbox.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ code:
- packages/app/src/routes/deviceMailbox.ts
- packages/app/src/routes/invoke.ts
- packages/app/src/mcpServer.ts
- packages/app/src/mcpInvoke.ts
- packages/app/src/routes/mcp.ts
- packages/server/src/deviceHub.ts
- packages/server/src/deviceRouter.ts
Expand Down Expand Up @@ -60,12 +61,13 @@ mailbox-capable command 仍须可发现并允许入队。
设备 command metadata 的 `delivery` 描述能力:缺省 `realtime`,`mailbox` 只支持 durable 入队,`both`
同时支持 realtime 与 Mailbox。请求 body 的保留 `~delivery` 则是每次调用的 policy:缺省 `realtime`,
可选 `realtime|mailbox|fallback`。两层不能混同;metadata 随既有 hello command metadata 进入 Help
JSON/DSL/Markdownstructured profile 与 MCP 投影,不增加 frame 类型
JSON/DSL/Markdownstructured profile;MCP 通过 `tb_help` 按需披露,不增加 frame 类型或逐工具 schema 投影

| 面 | 入口 | 责任 |
|---|---|---|
| caller invoke | `POST /<完整 command path>` | arguments 顶层可带 `~delivery`;TTL 在 query,幂等键在 header |
| caller 管理 | `POST /~device/operations/get|list|cancel` | owner/admin 查询、分页与取消 |
| MCP caller | `tb_call` + `tb_device_operations` | 同一 delivery、TTL、幂等键与 operation 管理语义,见 [MCP 入口](../app-runtime/mcp-interop.mdx) |
| device 数据 | `POST /~device/mailbox/claim|renew|complete` | Bearer 鉴权、lease 与四种设备终态 |
| SDK device | `createDeviceMailboxProcessor` | 宿主显式调用 `pullOnce`/`drain`;不自启 timer |

Expand All @@ -81,7 +83,7 @@ fallback 的判断必须消费 `DeviceCallDisposition` 这类 dispatch certainty

每条 operation 的 `targetPath` 是含命令叶子的完整 command identity;设备内部执行 `path` 则保持相对
mountPath 且含命令叶子。Registry 的系统写入 `deviceId` 是数据面原始身份,不能从规范化 mount path
反推。Dashboard/CLI 的便利校验都不替代服务端对 device-backed command 与 `delivery` 的权威复核。
反推。Dashboard/CLI/MCP 的便利校验都不替代服务端对 device-backed command 与 `delivery` 的权威复核。

## Authority、存储与加密

Expand Down
12 changes: 6 additions & 6 deletions llmdoc/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"validatedRevision": "fca74657f159c6a41b0a58abceb44243f025af5d"
},
"app-runtime/mcp-interop.mdx": {
"validatedRevision": "fca74657f159c6a41b0a58abceb44243f025af5d"
"validatedRevision": "5663a7eb6df73c18085dec8f01053523f73beeb6"
},
"architecture.mdx": {
"validatedRevision": "fca74657f159c6a41b0a58abceb44243f025af5d"
Expand All @@ -18,7 +18,7 @@
"validatedRevision": "fca74657f159c6a41b0a58abceb44243f025af5d"
},
"cli/argument-contract.mdx": {
"validatedRevision": "fca74657f159c6a41b0a58abceb44243f025af5d"
"validatedRevision": "5663a7eb6df73c18085dec8f01053523f73beeb6"
},
"dashboard/canvas-architecture.mdx": {
"validatedRevision": "fca74657f159c6a41b0a58abceb44243f025af5d"
Expand All @@ -42,13 +42,13 @@
"validatedRevision": "fca74657f159c6a41b0a58abceb44243f025af5d"
},
"protocol/htbp-contract.mdx": {
"validatedRevision": "fca74657f159c6a41b0a58abceb44243f025af5d"
"validatedRevision": "5663a7eb6df73c18085dec8f01053523f73beeb6"
},
"protocol/security-boundaries.mdx": {
"validatedRevision": "fca74657f159c6a41b0a58abceb44243f025af5d"
"validatedRevision": "5663a7eb6df73c18085dec8f01053523f73beeb6"
},
"release/npm-publishing.mdx": {
"validatedRevision": "29c1feacd57348d449f93e9b3596e1d4fdd29e8f"
"validatedRevision": "5663a7eb6df73c18085dec8f01053523f73beeb6"
},
"search/search-index.mdx": {
"validatedRevision": "fca74657f159c6a41b0a58abceb44243f025af5d"
Expand All @@ -57,7 +57,7 @@
"validatedRevision": "fca74657f159c6a41b0a58abceb44243f025af5d"
},
"device/durable-mailbox.mdx": {
"validatedRevision": "fca74657f159c6a41b0a58abceb44243f025af5d"
"validatedRevision": "5663a7eb6df73c18085dec8f01053523f73beeb6"
},
"hosts-deploy/managed-configuration.mdx": {
"validatedRevision": "fca74657f159c6a41b0a58abceb44243f025af5d"
Expand Down
2 changes: 1 addition & 1 deletion llmdoc/protocol/htbp-contract.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ builtin、Context 与 Skillhub 的命令使用 `HtbpCommandRegistry` 注册一
mailbox 或 certainty-gated fallback,没有独立 `~enqueue` 路由。
- `POST /~device/operations/get|list|cancel`:调用方的 operation 管理面;`POST /~device/mailbox/claim|renew|complete` 是设备 Bearer 数据面。
- `PUT /~store/uploads/<uploadId>` 与 `GET /~store/refs|shares/<token>` 是 Store 的 bearer 数据面,不是 Registry 节点;精确授权见[Store](#store)。
- `ALL /~mcp`:把当前身份可见工具投影为 MCP server。`GET /healthz`:公开健康信息,不证明认证数据面可用。
- `ALL /~mcp`:提供固定发现、帮助、按路径调用与 device operation 入口,不全量投影业务工具;目标仍按当前身份授权,详见 [MCP 入口](../app-runtime/mcp-interop.mdx)。`GET /healthz`:公开健康信息,不证明认证数据面可用。
- 错误统一 `{code,message,retryable}`;主要 code:not_found、permission_denied、invalid_argument、conflict、unavailable、rate_limited、internal。
- 未知 Help DSL 行与未知可选 capability 应忽略(协议演进能力);安全字段和未知写入参数不得静默忽略。

Expand Down
8 changes: 4 additions & 4 deletions packages/app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tool-bridge/app",
"version": "0.21.0",
"version": "0.22.0",
"description": "Host-neutral HTBP application: assemble the tool-bridge tree on any runtime by injecting state, object, secret, device-channel and search adapters",
"type": "module",
"license": "MIT",
Expand Down Expand Up @@ -38,16 +38,16 @@
"@modelcontextprotocol/client": "catalog:",
"@modelcontextprotocol/core": "catalog:",
"@modelcontextprotocol/server": "catalog:",
"hono": "catalog:"
"hono": "catalog:",
"zod": "catalog:"
},
"devDependencies": {
"@modelcontextprotocol/sdk": "catalog:",
"@tool-bridge/core": "workspace:*",
"@tool-bridge/plugins": "workspace:*",
"tsup": "catalog:",
"typescript": "catalog:",
"vitest": "catalog:",
"zod": "catalog:"
"vitest": "catalog:"
},
"repository": {
"type": "git",
Expand Down
2 changes: 0 additions & 2 deletions packages/app/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@ export {
handleMcpRequest,
type McpBridgeTool,
type McpToolBridge,
mcpToolIdentity,
mcpToolName,
} from './mcpServer'
// --- mcp 上游的托管 OAuth 授权码流程 ---
export {
Expand Down
Loading
Loading