Orchestrata Ultimate › Plans › briefs › 2026-10-13-mcp-gateway-transport-and-deployed-registration

Brief: ADR-0038, orchestrata half — the gateway's MCP transport generated from its handler table, and both servers registered with Claude by the deployed configuration

Source: plans/briefs/2026-10-13-mcp-gateway-transport-and-deployed-registration.md at 8a2cb42ad · last changed 2026-10-13 · first committed 2026-10-13 · 50 lines.

Prose pending.

Plan text

Brief: ADR-0038, orchestrata half — the gateway's MCP transport generated from its handler table, and both servers registered with Claude by the deployed configuration

Date: 2026-10-13 · DESIGN_WAIVER: ADR-0038 (Accepted ruling, rules 2, 3, 4; the codetools half landed on codetools master at b39d26f and 74cad1e) · Executor: Sonnet builder in worktree orchestrata-ultimate-worktrees/mcp-gateway, branch feat/mcp-gateway-transport off origin/master (afd39e93f or newer) · Design settled below; no design forks are open. If a fork appears, STOP and report.

GOAL: the coordinating Claude session calls orchestrata and codetools as tools. (a) orchestrata_ultimate.gateway gains an MCP transport over stdio whose tool table is generated from the gateway's handler table, with the same approval contract codetools ships (single-use, tool-bound, 300 s token issued by an approve tool; refusals carry a ledger row id). (b) deploy/install-claude-config.sh registers both servers at user scope from the deployed release paths, idempotently, and the identity check refuses success unless each registered command resolves inside its deployed release.

RULED DESIGN (ADR-0099, Accepted 2026-10-13, rules 1 to 3; these replace the earlier premise that the gateway had an HTTP verb surface and mutation markers — it has neither):

  1. Transport. orchestrata-mcp is a WebSocket JSON-RPC client of the deployed gateway at GATEWAY_HOST:GATEWAY_PORT, authenticating with GATEWAY_TOKEN from the deployed secrets layer (never a flag, never in the ledger); every tool call is forwarded as the JSON-RPC method it names and passes the gateway's own ScopeGuard, auth and audit. No new HTTP RPC route, no in-process dispatch.
  2. One declarative method table. Refactor _build_method_registry in gateway/rpc.py into a module-level METHODS: tuple[MethodSpec, ...] in a new gateway/method_table.py (name, handler by dotted path, mutates: bool, expose: bool, optional params_schema), importable without constructing anything; RPCHandler builds its dict from it and McpTransport builds its tool list from it; a test proves a method added to the table appears in both and that RPCHandler's dict equals the table.
  3. Gated unless marked read-only; not everything is a tool. mutates defaults to True; the read-only set is marked explicitly after reading each handler (start from names ending in list/get/query/status/info/rules/decisions/current/health plus ping); streaming methods (subscribe, publish, cxp.subscribe, …), legacy aliases and auth.authenticate are expose=False; with no schema the tool's input_schema is {"type":"object"} and the gateway validates. The report lists every method with its mutates and expose values. FILES gains: src/orchestrata_ultimate/gateway/method_table.py and method_spec.py (new, one class each), the rpc.py refactor; tests/gateway/test_method_table.py. The mcp SDK is confirmed as a new dependency (uv add mcp).

WHAT CODETOOLS ALREADY PROVIDES (read, do not reimplement in codetools):

FILES (repo-relative): Create:

CONSTRAINTS:

DONE WHEN:

  1. orchestrata-mcp over a real pipe lists one tool per gateway handler plus approve; a mutating call without a token is refused with a ledger row and nothing reaches the gateway; with a token it runs once; the reused token is refused.
  2. The extra-handler test shows the new tool with no transport edit.
  3. bash deploy/install-claude-config.sh --prefix <tmp> registers codetools-test and orchestrata-test at user scope, writes the flat document, and the identity checks pass; pointing the registration at a foreign path fails exactly the registration check.
  4. The --rebaseline-id log line carries both hashes (test updated).
  5. ruff, mypy on changed files, one class per file, coverage ≥ 90 on new modules, targeted tests and BDD green, clean tree, every commit through pre-commit.

Report back with: the generated tool table (name, mutates, approval), the approval flow transcript, the installer's registration lines, the identity lines, REQ ids, commit shas, and anything that stopped you. Observed numbers only; no "should".