router: multi-domain MCP gateway (per-domain + merged) #118

Merged
mahmoud merged 4 commits from development_router_multidomain_mcp into development 2026-06-14 14:50:18 +00:00
Owner

Closes #117.

Makes the router's MCP gateway multi-domain. Previously every service collapsed to its first domain; multi-domain services (e.g. hero_proc = jobs/logs/secrets/system) only exposed one domain over MCP.

What changed

  • T1 — per-domain spec cache (acca99c): ServiceEntry.domain_specs (domain→spec); the scanner now fetches every domain's /api/<d>/openrpc.json (not just the first) and sums method counts. spec_for_domain() / domain_spec_pairs() helpers.
  • T2/T3 — per-domain + merged MCP (51eef73): new POST /mcp/{id}/{domain} (one domain, plain tool names, forwarded to /api/{domain}/rpc); POST /mcp/{id} is now a merged endpoint (multi-domain tools qualified as <domain>__<tool>, single-domain/legacy keep flat names + old behavior). tools/call resolves the domain (route or prefix) and forwards to the right /api/{domain}/rpc. Tool map re-keyed (service,domain,tool); GET/DELETE per-domain wrappers.
  • T4 — UI (f48fb3e): the /mcp overview lists the merged + each per-domain endpoint; the OpenRPC explorer was already domain-aware.
  • T5 — tests (1aef9bf): fake_service --domains multi-domain mode + functional/mcp.rs cases (merged qualified names, per-domain plain names, per-domain & merged tools/call routing).

Model

Single rpc.sock per service; domains are path segments (/api/<domain>/rpc|openrpc.json|events, GET /api/domains.json) — matches the current openrpc_server! services.

Verification

Built + cargo clippy clean (only pre-existing unrelated warnings). Verified live standalone (router + a 2-domain fake service):

  • merged tools/listalpha__alpha_ping, beta__beta_ping, …
  • per-domain /mcp/svc/alpha → plain alpha_ping
  • per-domain & merged tools/call → routed to alpha (pong:alpha)

(The full hero_router_test harness needs a live hero_proc to register against — currently down/being stabilized — but the standalone run proves the code and the tests compile.)

Out of scope (follow-ups)

  • Router's own control-plane server stays the hand-rolled dispatcher (chicken-and-egg; later).
  • Service-detail page previews the first domain only (explorer is the canonical per-domain view).
  • access.rs proc-secrets wire fix (tracked separately).
Closes #117. Makes the router's **MCP gateway multi-domain**. Previously every service collapsed to its first domain; multi-domain services (e.g. `hero_proc` = jobs/logs/secrets/system) only exposed one domain over MCP. ## What changed - **T1 — per-domain spec cache** (`acca99c`): `ServiceEntry.domain_specs` (domain→spec); the scanner now fetches **every** domain's `/api/<d>/openrpc.json` (not just the first) and sums method counts. `spec_for_domain()` / `domain_spec_pairs()` helpers. - **T2/T3 — per-domain + merged MCP** (`51eef73`): new `POST /mcp/{id}/{domain}` (one domain, plain tool names, forwarded to `/api/{domain}/rpc`); `POST /mcp/{id}` is now a **merged** endpoint (multi-domain tools qualified as `<domain>__<tool>`, single-domain/legacy keep flat names + old behavior). `tools/call` resolves the domain (route or prefix) and forwards to the right `/api/{domain}/rpc`. Tool map re-keyed `(service,domain,tool)`; GET/DELETE per-domain wrappers. - **T4 — UI** (`f48fb3e`): the `/mcp` overview lists the merged + each per-domain endpoint; the OpenRPC explorer was already domain-aware. - **T5 — tests** (`1aef9bf`): `fake_service --domains` multi-domain mode + `functional/mcp.rs` cases (merged qualified names, per-domain plain names, per-domain & merged `tools/call` routing). ## Model Single `rpc.sock` per service; domains are path segments (`/api/<domain>/rpc|openrpc.json|events`, `GET /api/domains.json`) — matches the current `openrpc_server!` services. ## Verification Built + `cargo clippy` clean (only pre-existing unrelated warnings). Verified **live standalone** (router + a 2-domain fake service): - merged `tools/list` → `alpha__alpha_ping`, `beta__beta_ping`, … - per-domain `/mcp/svc/alpha` → plain `alpha_ping` - per-domain & merged `tools/call` → routed to alpha (`pong:alpha`) (The full `hero_router_test` harness needs a live hero_proc to register against — currently down/being stabilized — but the standalone run proves the code and the tests compile.) ## Out of scope (follow-ups) - Router's own control-plane server stays the hand-rolled dispatcher (chicken-and-egg; later). - Service-detail page previews the first domain only (explorer is the canonical per-domain view). - `access.rs` proc-secrets wire fix (tracked separately).
ServiceEntry gains a domain_specs map (domain -> OpenRPC JSON); the scanner
now fetches EVERY discovered domain's spec via /api/<domain>/openrpc.json
instead of only the first, and sums methods_count across domains. openrpc_json
keeps the first domain's spec for back-compat. Adds spec_for_domain() and
domain_spec_pairs() helpers for the MCP gateway/explorer.

Refs #117
Adds POST /mcp/{id}/{domain} exposing a single domain's tools (plain names,
forwarded to /api/{domain}/rpc), and turns the existing POST /mcp/{id} into a
MERGED endpoint that lists every domain's tools — multi-domain services get
<domain>__<tool> qualified names so they don't collide, single-domain/legacy
services keep flat names and behave exactly as before.

- mcp_handler/mcp_handler_domain delegate to a shared mcp_dispatch(domain: Option).
- tools/list builds per-domain or merged tool sets and records a per-(service,
  domain) tool->method map.
- tools/call resolves (domain, tool) — from the route or the <domain>__ prefix —
  and forwards to /api/{domain}/rpc.
- mcp_tool_map re-keyed to (service_id, domain, tool); proxy_to_service gains a
  domain param (path /api/{domain}/rpc, 'main' for legacy).
- GET/DELETE per-domain wrappers for the Streamable-HTTP session legs.

Refs #117
For multi-domain services the MCP overview now lists the merged /mcp/{id}
endpoint plus each per-domain /mcp/{id}/{domain} endpoint. The OpenRPC explorer
is already domain-aware (domain selector). Refs #117
test(router): multi-domain MCP coverage (per-domain + merged)
All checks were successful
Build & Test / check (pull_request) Successful in 12m27s
1aef9bf911
fake_service gains a --domains mode (alpha,beta) serving /api/domains.json +
per-domain /api/<d>/openrpc.json + /api/<d>/rpc (<d>_ping/<d>_echo). main spawns
a multidomain_svc; Env exposes it. functional/mcp.rs adds cases asserting the
merged endpoint lists <domain>__<tool> names, the per-domain endpoint lists plain
names, and tools/call routes to the right domain (pong:alpha) via both endpoints.

Verified live standalone (router + fake service): merged + per-domain tools/list
and tools/call all route correctly. Refs #117
mahmoud merged commit e7c9e49df9 into development 2026-06-14 14:50:18 +00:00
mahmoud deleted branch development_router_multidomain_mcp 2026-06-14 14:50:53 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_router!118
No description provided.