@omega.js/mcp-router
v0.51.0
Published
One lazy MCP endpoint for every OMEGA session — a stdio server that proxies many upstream MCP servers and surfaces each upstream's tools only once it is activated
Maintainers
Readme
@omega.js/mcp-router
One MCP endpoint for a whole session, and it stays cheap.
An MCP client that declares five servers pays for five: every one is launched, every one's full tool schema sits in the context window from the first token. The router flips that around. It is a single stdio MCP server that proxies many upstream servers: tool schemas come from a cache on disk, upstream processes are spawned lazily on the first tool call that actually needs one, and a session turns upstreams on and off for itself without touching disk or restarting anything.
Tools surface to the client as <upstream>__<tool> — inside Claude Code that reads mcp__mcp-router__chrome-devtools__click.
A child is as short-lived as its use: it starts on the first call that needs it, and it is closed once nothing has called it for 15 minutes — with everything it started, not just the pid the router holds — after which the next call spawns a fresh one. A call in flight is never closed under, and a call that lands while a close is running is served by a fresh child rather than failing. When the host goes away, by a signal or simply by closing the router's stdin, the router closes every upstream, waits out the grace that kills what they started, and exits.
The omega Claude plugin launches the router straight from the monorepo checkout it ships in, and on a bare clone the bin installs its own dependencies on first launch — no install step for a user, no publish for a dev editing the checkout.
What ships with it
Four defaults, ready on install day:
| Upstream | What it does | Default |
|---|---|---|
| chrome-devtools | A private, isolated, throwaway-profile Chrome per session | auto |
| chrome-devtools-electron | Attaches to an already-running omega desktop dev app on $OMEGA_CDP_PORT | auto |
| chrome-devtools-extension | Chrome for Testing with the unpacked extension at $OMEGA_EXTENSION_PATH pre-loaded | on-demand |
| omega-extension | The automation bridge to the OMEGA Companion extension, shipped here in servers/omega-extension/ | auto |
An on-demand upstream stays invisible until a session asks for it — that is how a noisy 34-tool server costs nothing until it is wanted.
The meta-tools (per session, from inside the client)
router__list_upstreams— every upstream, its on-disk enabled state, whether it is locked, whether it is active in this session, its cached tool count, and — when a child is running — that child'spidand how long it has been idle (idle_ms).router__enable_upstream {name, env?}— activate for THIS session, unless the upstream is locked.envsets vars on the child (passing it restarts a running child so they take effect).router__disable_upstream {name}— deactivate and stop the child. Disk is untouched.router__refresh_upstream {name}— spawn once, re-read the tool list, and cache it.
Config layering
Two layers, and only one of them is ever written:
- Bundled defaults —
servers/<name>/config.jsoninside this package. Read-only: for a consumer they live innode_modules. - Your overlay —
~/.omega/mcp-router/servers/<name>/config.json, plus~/.omega/mcp-router/.envfor secrets.
The merge is shallow and field-level: an overlay entry's top-level keys win over the bundled entry's, and everything else survives.
// ~/.omega/mcp-router/servers/chrome-devtools/config.json
{ "enabled": false } // turns the bundled default off, keeps its command + tools cache// ~/.omega/mcp-router/servers/playwright/config.json
{ "enabled": true, "default": "on-demand", "command": "npx", "args": ["-y", "@playwright/mcp@latest", "--isolated"] }// ~/.omega/mcp-router/servers/playwright/config.json
{ "enabled": false, "locked": true } // stays off: every enable is refused, `--force` is the only way past"locked": true holds an upstream off. omega-mcp enable refuses it (exit 1, naming the field) unless you pass --force, and the per-chat router__enable_upstream refuses it with no override at all. Disabling, removing, and refreshing a locked upstream stay allowed: the lock is against waking it, not against turning it off.
A name that only exists in the overlay is simply a private upstream of yours. Nothing you do forks the defaults, and an upgrade of this package never clobbers your overrides.
The CLI
omega-mcp manages the registry. Every command reads the merged view and writes the overlay.
omega-mcp list # every upstream, its source (bundled / yours), state, tool count
omega-mcp enable chrome-devtools-extension # writes {"enabled": true} to your overlay, then caches the schema
omega-mcp disable chrome-devtools # writes {"enabled": false}
omega-mcp enable playwright --force # the only way past a {"locked": true} entry
omega-mcp add my-server npx -y my-mcp@latest
omega-mcp remove my-server # a bundled default cannot be removed — disable it instead
omega-mcp refresh chrome-devtools # re-fetch and cache the tool schemasRegistering the router with a client is not this CLI's job: the omega Claude plugin declares it once, in its .mcp.json.
Environment
| Variable | What it does |
|---|---|
| OMEGA_CDP_PORT | The port chrome-devtools-electron attaches to (default 9222) |
| OMEGA_EXTENSION_PATH | The unpacked extension directory chrome-devtools-extension loads |
| MCP_ROUTER_SERVERS_DIR | Overrides the overlay servers dir (tests, power users) |
| MCP_ROUTER_ENV_FILE | Overrides the overlay .env path |
| MCP_ROUTER_SPAWN_TIMEOUT_MS | How long a cold spawn (or either refresh surface's one-shot: router__refresh_upstream and omega-mcp refresh share one helper) gets to finish the MCP handshake before it is given up on (default 30000) |
| MCP_ROUTER_IDLE_MS | How long an upstream's child may sit with no call before the router closes it; the sweep runs at a quarter of it, floored at a second (default 900000) |
Placeholders
An upstream's command, args, and env may carry ${NAME} placeholders, resolved from ~/.omega/mcp-router/.env first and then the environment — so a token lives in the .env, never in a config file. ${NAME:-default} takes the same lookups and falls back to the literal default when they all miss, so an optional value needs no sh -c wrapper around the command; other shell forms (${VAR:+…}) pass through untouched.
One name is reserved: ${MCP_ROUTER_ROOT} always resolves to this package's root directory, before any .env or environment lookup. It is how a bundled upstream points at a launcher script the package ships with, wherever the package is installed.
Docs
The full guide — architecture, the launchers, the plugin wiring, the noted gaps — is docs/mcp-router/index.md in the OMEGA monorepo.
License
Elastic License 2.0. The source is free to use and modify; a license key unlocks payments in production deploys and removes the attribution (local dev and test payments are always free); and you may not offer @omega.js/mcp-router to third parties as a hosted or managed service.
