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

Closed
opened 2026-06-14 12:32:58 +00:00 by mahmoud · 5 comments
Owner

Goal

Make the router's MCP gateway multi-domain. Today the router collapses every service to a single (first) domain; multi-domain services (e.g. hero_proc with jobs/logs/secrets/system) only expose their first domain over MCP.

Model (confirmed): services serve one rpc.sock, domains are path segments — GET /api/domains.json, GET /api/{domain}/openrpc.json, POST /api/{domain}/rpc, GET /api/{domain}/events. Discovery/proxy already follow this; the gap is the MCP layer + spec cache + UI.

Gaps (verified on development)

  • scanner.rs caches only domains.first()'s spec -> other domains invisible.
  • ServiceEntry has a single openrpc_json + domains: Vec<String> (names only) — no per-domain specs.
  • MCP route is only POST /mcp/{id}; tools/list uses the one entry.openrpc_json.
  • tools/call isn't domain-aware; UI/explorer/markdown render one spec.

Tasks

  • T1 — per-domain spec cache (ServiceEntry + scanner fetches every domain)
  • T2 — per-domain + merged MCP endpoints (/mcp/{id}/{domain} + merged /mcp/{id})
  • T3 — domain-aware tools/call (forward to /api/{domain}/rpc)
  • T4 — UI/explorer/markdown render all domains
  • T5 — multi-domain MCP tests (two-domain demo server) + verify

Notes

Branch development_router_multidomain_mcp off development; PR -> development. Out of scope: router's own control-plane server stays hand-rolled (later); access.rs proc-secrets wire fix tracked separately.

## Goal Make the router's **MCP gateway multi-domain**. Today the router collapses every service to a single (first) domain; multi-domain services (e.g. `hero_proc` with `jobs/logs/secrets/system`) only expose their first domain over MCP. Model (confirmed): services serve **one `rpc.sock`**, domains are path segments — `GET /api/domains.json`, `GET /api/{domain}/openrpc.json`, `POST /api/{domain}/rpc`, `GET /api/{domain}/events`. Discovery/proxy already follow this; the gap is the **MCP layer + spec cache + UI**. ## Gaps (verified on `development`) - `scanner.rs` caches only `domains.first()`'s spec -> other domains invisible. - `ServiceEntry` has a single `openrpc_json` + `domains: Vec<String>` (names only) — no per-domain specs. - MCP route is only `POST /mcp/{id}`; `tools/list` uses the one `entry.openrpc_json`. - `tools/call` isn't domain-aware; UI/explorer/markdown render one spec. ## Tasks - [x] T1 — per-domain spec cache (ServiceEntry + scanner fetches every domain) - [x] T2 — per-domain + merged MCP endpoints (`/mcp/{id}/{domain}` + merged `/mcp/{id}`) - [x] T3 — domain-aware `tools/call` (forward to `/api/{domain}/rpc`) - [x] T4 — UI/explorer/markdown render all domains - [x] T5 — multi-domain MCP tests (two-domain demo server) + verify ## Notes Branch `development_router_multidomain_mcp` off `development`; PR -> `development`. Out of scope: router's own control-plane server stays hand-rolled (later); `access.rs` proc-secrets wire fix tracked separately.
mahmoud self-assigned this 2026-06-14 12:34:52 +00:00
mahmoud added this to the ACTIVE project 2026-06-14 12:34:55 +00:00
mahmoud added this to the now milestone 2026-06-14 12:34:58 +00:00
Author
Owner

T1 done — per-domain spec cache. ServiceEntry.domain_specs (domain→spec) added; scanner now fetches every domain via /api/<domain>/openrpc.json (not just the first) and sums method counts. Helpers spec_for_domain() / domain_spec_pairs(). Commit acca99c. Compiles clean (all-targets).

**T1 done** — per-domain spec cache. `ServiceEntry.domain_specs` (domain→spec) added; scanner now fetches every domain via `/api/<domain>/openrpc.json` (not just the first) and sums method counts. Helpers `spec_for_domain()` / `domain_spec_pairs()`. Commit `acca99c`. Compiles clean (all-targets).
Author
Owner

T2 + T3 done — multi-domain MCP gateway. Added POST /mcp/{id}/{domain} (per-domain, plain tool names) and made /mcp/{id} 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 <domain>__ prefix) and forwards to /api/{domain}/rpc; tool map re-keyed (service,domain,tool). Commit 51eef73. Compiles + clippy clean (only 2 pre-existing unrelated warnings remain).

**T2 + T3 done** — multi-domain MCP gateway. Added `POST /mcp/{id}/{domain}` (per-domain, plain tool names) and made `/mcp/{id}` 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 `<domain>__` prefix) and forwards to `/api/{domain}/rpc`; tool map re-keyed `(service,domain,tool)`. Commit `51eef73`. Compiles + clippy clean (only 2 pre-existing unrelated warnings remain).
Author
Owner

T4 done/mcp overview now lists the merged endpoint + each per-domain /mcp/{id}/{domain} for multi-domain services. The OpenRPC explorer was already domain-aware (domain selector). Service-detail page still previews the first domain only (explorer is the canonical per-domain view) — noted as a minor follow-up. Commit f48fb3e.

**T4 done** — `/mcp` overview now lists the merged endpoint + each per-domain `/mcp/{id}/{domain}` for multi-domain services. The OpenRPC explorer was already domain-aware (domain selector). Service-detail page still previews the first domain only (explorer is the canonical per-domain view) — noted as a minor follow-up. Commit `f48fb3e`.
Author
Owner

T5 done + PR opened. Added fake_service --domains multi-domain mode + functional/mcp.rs cases. Commit 1aef9bf.

Verified live (standalone router + 2-domain fake service): discovery cached both domains (methods=4); 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). Build + clippy clean (only pre-existing warnings).

Full hero_router_test harness run is pending a live hero_proc (currently down / being stabilized).

PR: #118 (development_router_multidomain_mcp → development).

**T5 done + PR opened.** Added `fake_service --domains` multi-domain mode + `functional/mcp.rs` cases. Commit `1aef9bf`. **Verified live (standalone router + 2-domain fake service):** discovery cached both domains (methods=4); 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`). Build + clippy clean (only pre-existing warnings). Full `hero_router_test` harness run is pending a live hero_proc (currently down / being stabilized). **PR:** #118 (development_router_multidomain_mcp → development).
Author
Owner

Follow-up: SSE alignment to the canonical /api/{domain}/events (drop sse.json, derive from the cached x-sse per-domain) is tracked separately in #120 — it was deliberately out of scope for #118.

Follow-up: SSE alignment to the canonical `/api/{domain}/events` (drop `sse.json`, derive from the cached `x-sse` per-domain) is tracked separately in #120 — it was deliberately out of scope for #118.
Sign in to join this conversation.
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#117
No description provided.