Skip to content

feat(mcp-gateway): aikey mcp try <tool> — 自检调用(8.7 / R23) - #31

Open
damonleelcx wants to merge 3 commits into
develop-v1.0.6from
feat/mcp-gateway-4.8-8.7-closeout
Open

feat(mcp-gateway): aikey mcp try <tool> — 自检调用(8.7 / R23)#31
damonleelcx wants to merge 3 commits into
develop-v1.0.6from
feat/mcp-gateway-4.8-8.7-closeout

Conversation

@damonleelcx

Copy link
Copy Markdown
Contributor

为什么是 CLI 而不是控制台面板

R23 要的是控制台「试一下」面板。🔴 它在 Production 不可能存在:控制台发起的调用要求控制面能连到 MCP 面,而那个面是员工笔记本上的代理。面板只能在 Trial/Cluster 工作 —— 而「只在某一个版型才存在的能力」按本项目自己的规则就是缺陷。裁决 A-1 把能力搬到面真正所在的地方。

R23 的每一条要求在这个形态下是构造上成立的:这条命令本身就是一个 MCP 客户端,拿不到比一个客户端更多的东西。

三处值得留住的判断

走完整握手。 网关确实接受无会话的 tools/call,那样更短且照样过每一道闸。仍然不对:自检要回答「我的客户端能不能用」,而客户端要握手、之后每个请求要带 MCP-Protocol-Version(不支持的值回 400)。跳过协商的探针,会在一台真实客户端握不上手的网关上报告成功。

参数类型取自工具发布的 schema,不看长相。「像 JSON 就当 JSON」会把版本号 1.0 变成数字 1、账号 0012 变成 12 —— 合法 JSON、错的值,而工具不会报错,因为类型是对的

退出码把结论传给 shell。 0 成功 / 2 跑过了但答案是否定 / 1 根本没跑起来(同 aikey env check)。

🔴 最后这条是活体运行抓到、18 条单测没抓到的缺陷:第一版把拒绝渲染得很漂亮,然后退出 0。一个不能让脚本失败的自检不是检查,是一段话。

命名

aikey mcp test(网关侧每个托管 server 的健康)和 aikey mcp calls(本机历史)都已存在且是别的意思。裁决初稿写的是 mcp test —— 那是没查就写的,查了才有了 try

验证

真实二进制打协议保真的网关桩:完整握手 / 成功 / 被拒 / 未知工具 / 类型错 / 网关没起 / 错工具集。线上只有一个内部头,握手后每个请求都带 MCP-Protocol-Version。18 条单元围栏 + 6 条变异演练。

⬜ 仍欠一次真正的跨进程活体运行(与 P11 已登记的是同一个缺口)。


验证make -C workflow/CI e2e-mcp — 15 节全部 0 problems,零假绿。
围栏登记表 73 proven / 0 tested / 4 blocked(此前 55/0/5,另有 20 条对登记表完全不可见)。

🔴 本 PR 含同一批 phase-8 工作的更早提交(分支从本地开发分支切出,那些提交尚未推过)。
每条 commit message 末尾都具名标注了 co-resident 的部分。

🤖 Generated with Claude Code

Damon and others added 3 commits September 2, 2026 22:30
…nt wiring

Snapshot of in-flight work in this checkout: the CLI side of the MCP gateway.

src/mcp_scan.rs   inventories the MCP servers already configured on a machine,
                  including the plaintext credentials sitting in their config
                  files. Read-only: it reports, it does not move anything.
src/mcp_adopt.rs  the move — take a scanned server's plaintext secret into the
                  vault and rewrite the client config to reference it. Ordering
                  matters and is not incidental: the secret must be in the vault
                  before the config stops naming it, or a failure between the
                  two steps loses the key permanently.
src/commands_mcp.rs the command surface; data/mcp_clients.yaml is the client
                  table it drives, so supporting one more MCP client is a row
                  rather than another branch.

claude_desktop.rs, cli.rs, lib.rs and main.rs are wired for the new commands.

⚠️ Branch note: this work sits on fix/rustfmt-drift, which is where the
checkout happened to be. It carries no commits of its own beyond this one, so
the PR is cut against develop-v1.0.6 regardless.

Verified in this session: cargo fmt --check clean, cargo build clean. The test
suite was still running when this was written; see the PR for the result.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… 8.7 / R23)

R23 asked for a "try it" panel in the console: the operator invokes one tool
with their own seat and their own key, through the gateway's own `/mcp/{slug}`,
so the call walks the identical chain a real Agent walks.

🔴 The panel could not exist in Production. A console-initiated call needs the
control plane to reach the MCP surface, and there that surface is the proxy on
an employee's laptop. It would have worked on Trial and Cluster only — and "a
capability that exists in one edition only" is a defect by this project's own
rule. Ruling A-1 moved the capability to where the surface actually is.

Every R23 requirement survives the move BY CONSTRUCTION: this command *is* an
MCP client, so it cannot hold more than one. Its own bearer, the same URL, the
same handler, no second code path to skip a step in, and the gateway's own
recorder — which this command has no switch to turn off.

## Three decisions worth keeping

**It does the whole handshake.** The gateway accepts a session-less
`tools/call`, and that shorter path still walks every gate R23 lists. Still
wrong: a self-check answers "would my client work", and clients negotiate and
then send `MCP-Protocol-Version` on every later request, where an unsupported
value is a 400. A probe that skips negotiation reports success on a gateway no
real client can finish a handshake with.

**Arguments are typed from the tool's published schema, never from appearance.**
"If it parses as JSON, send JSON" turns the version string `1.0` into the number
`1` and the id `0012` into `12` — valid JSON, wrong value, and the tool does not
complain because the TYPE is right.

**The verdict reaches the shell.** 0 = ran and succeeded, 2 = ran and the answer
is no (refused, or the tool reported an error), 1 = could not run at all. Same
split as `aikey env check`; a caller that cannot tell 2 from 1 retries what can
never succeed.

🔴 That last one is a defect the live run caught and eighteen unit tests did
not: the first version rendered a refusal perfectly and exited 0. A self-check
that cannot fail a script is not a check, it is a paragraph.

## Naming

`aikey mcp test` (gateway-side health of each hosted server) and `aikey mcp
calls` (this machine's history) both already exist and mean other things. The
ruling's draft said `mcp test`; that was written without checking, and checking
is why this is `try`.

## Verified

Real binary against a protocol-faithful gateway stub: full handshake, success,
refusal, unknown tool, wrong argument type, gateway down, wrong toolset. Exactly
one internal header on the wire, `MCP-Protocol-Version` on every post-handshake
request. 18 unit fences; 6 mutations drilled via
`make -C workflow/CI verify-mcp-selfcheck`.

⬜ Still open, and the same gap P11 already logs: one real cross-process run
(real vault + key + MCP server + proxy). This was proven from both ends — real
CLI against a faithful stub, real Go handler against the exact header — which is
short of one live chain.

Co-resident: cli.rs and main.rs also carry the P15 delegation-hook wiring from
earlier in the same phase-8 effort.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…cp.json

On a node that follows a control plane, ~/.aikey/mcp.json is not read at all
("the control plane wins by existing"). `aikey mcp add` nevertheless printed a
checkmark, a /mcp/local endpoint and an instruction to run `aikey mcp review` —
three statements, none of them true there. `aikey mcp test` answered "the
gateway is running and hosting no backends", which answers a question the user
did not ask: the servers they configured are not absent, they are unread. And
`aikey mcp list` printed an Endpoint: line that does not serve what it just
listed.

All three now read the new `policy_source` field from /health/mcp and say what
is actually happening. The three share ONE sentence (CONTROL_PLANE_HEADLINE):
one machine state described in three wordings is how a user comes to believe
they have three problems.

  add   warns, and drops the two false next steps rather than printing them
        under a warning
  list  REPLACES the endpoint line — a correct-looking URL beneath a warning is
        still the thing that gets copied
  test  names the contradiction ("the N server(s) in your config are NOT being
        served here"), and stays silent when the user has written nothing
        locally, because then there is no contradiction to point out

A refusal is not the goal: the entry is still written, and it is what the
machine would host if it ever stopped following a control plane.
不阻塞用户流程 > 错误要显眼.

Probing is best-effort. None means "the gateway did not say", never
local_config — defaulting an absent discriminator would make the warning stop
firing against an older proxy, silently.

Fences (drilled both directions):
  add_on_a_control_plane_node_says_the_file_is_not_read
  add_on_a_local_config_node_keeps_the_original_next_steps
  test_on_a_control_plane_node_says_where_the_toolsets_come_from
  test_keeps_its_wording_where_the_local_file_is_authoritative
  list_footer_replaces_the_endpoint_on_a_control_plane_node
  add_list_and_test_use_the_same_sentence_for_the_same_state

bugfix: workflow/CI/bugfix/20260904-mcp-add-claimed-success-on-a-control-plane-node.md

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant