Keep MCP tools under stable server-qualified provider names

Merged
VA
2d ago
+1162-79

MCP names used positional aliases, so adding a default tool changed the model-facing identity. Canonical names now produce stable, reversible wire aliases; mcp.docs.search becomes mcp__docs__search. Hooks, tool admission and approvals continue to use the canonical name.

Design

One tool-name table handles declaration, controls, native stream decoding and reverse lookup across Responses, Chat, Anthropic and Gemini. Overlong or colliding aliases use a stable digest and salted collision resolution. Exact alias capacity remains under native custody.

Native generation and token counting share Names::funded. It charges each digest's domain, salt and canonical bytes before hashing, including every collision retry. The wire index is sorted again only when collision resolution changes a binding.

Deletions

Removed positional aliases and the unconditional second wire-index sort. Replaced the arithmetic-only sort control with a mutation of the actual production sort. Relative to the recorded main base: +1,162 / -79, net +1,083 lines, predominantly regression proofs and HTTP fixtures.

Decisions

  • Canonical tool names remain the authority and history keys. Aliases exist only at the provider wire boundary.
  • Both stock tool execution and direct-tool dispatch construct approvals through tool_approval_request and its action binding. Hook pre/post matching receives the restored canonical name before policy or approval evaluation.
  • Consumer tests use the real HTTP adapters, hook matcher and approval constructor. They exercise the shared identity boundary used by manual, policy, LLM and parent-agent approval consumers rather than copying those consumers.

Guarantee

  1. Class: K/L (codec identity and disconnected consumers), H/F (unfunded work and retained allocation). A private reversible table owns the canonical-to-wire mapping. One funded builder admits every native digest before execution; unchanged bindings cannot reach the final sort.
  2. Proof: shrinking properties cover reversible, collision-free, order-independent mappings, exact allocation capacity, hash-byte admission including salt retries, and the actual sort/comparison counters. Production mutations skip hash admission and restore the redundant sort; the same oracles reject both. Real HTTP tests for all four adapters feed restored calls into pre/post hook matching and the common approval action binding. Production mutations substitute the wire name in each consumer; both fail the canonical identity oracle. Existing checked-in alias fuzz corpus and round-trip oracle remain covered; the grammar and decoder are unchanged by these review fixes.
  3. Entry points: tool declarations and named controls; portable/native adapter generation and restored call batches; native token counting; pre/post hooks; stock and direct-tool approval request construction. Adapter scenarios retain canonical tool execution/result round trips and insertion of an earlier-sorting default tool.
  4. Gaps: none.

Tests

At signed head 04ae436c19cd618194d043ca7029aaa004cd2d2f, through bs.ps1 (job 1792320623931-1250396):

  • Tool-name properties, exact accounting and production negative controls: 20 passed, 0.044 s.
  • Canonical hook/approval HTTP tests and production negative controls: 3 passed, 0.031 s.
  • Provider scenario runner (E2E_FILTER=providers/): passed, 1.909 s, including fixture validation.
  • Windows GNU Harness and Providers --lib --tests: passed, 6m08s.

Exact focused gate: Q:/lanes/co-features-982-review-gate.sh, submitted from Q:/wt/context-c-mcp through Q:/lanes/bs.ps1 bash -c. Linux test build 6m56s. One feature closure: acyclic-harness/filesystem-local,acyclic-harness/native-execution,acyclic-harness/terminal,acyclic-harness/mcp-http,acyclic-harness/test-support,acyclic-harness/grpc. Stable toolchain 1.98.1; tests rerun from the built binaries.

The initial build failed with ENOSPC during bb2's reported disk incident; it produced no test result. This run is the single authorized infrastructure retry.

The provider/consumer integration tier would have caught the live failure earlier. Earlier tests checked aliases and echo execution without checking canonical hook selection or approval action hashes; both are now explicit independent oracles.