Per-agent routing
By default, every enabled server in mcphub.yaml reaches every non-disabled agent. That is the right default for one person with one toolbox — but in a multi-agent setup you often want different agents to see different subsets: a review agent that only needs code search, a deploy agent that should never see the database server, a minimal agent that gets nothing but the gateway's meta-tools.
Per-agent routing does exactly that with two optional lists on an agent entry:
agents:
codex:
type: codex
path: ~/.codex/config.toml
mode: gateway
servers: [codemap, vecgrep] # which servers it may reach
tools: [codemap__find, vecgrep__search] # which exact tools it may callservers— which downstream servers this agent may reach.tools— which exactserver__toolnames it may call. Gateway mode only: a direct agent talks to each server itself, so mcphub has no proxy in the path to filter individual tools.mcphub doctorrejectstoolson a direct-mode agent.
Curation, not a security boundary
Routing controls what the gateway advertises and honors for a well-behaved agent — it keeps context lean and agents on-task. It is not a hard isolation layer: anything that can run mcphub mcp serve without --agent (or talk to the downstream servers directly) sees everything. Don't use routing to protect secrets from a hostile process; use it to stop your review agent burning tokens on deployment tools.
Omitted vs empty: the three states
Each list is a pointer in the config — absent and empty mean different things:
In mcphub.yaml | Meaning |
|---|---|
| key omitted entirely | All. No restriction — the pre-routing behavior. |
servers: [] / tools: [] | None. A deliberately minimal agent. |
servers: [a, b] / tools: [a__x] | Only these. |
So an agent with no servers and no tools keys behaves exactly as before, and adding routing to one agent never changes what the others see.
agents:
claude:
type: claude
path: ~/.claude.json
mode: gateway
# no servers/tools keys -> sees every enabled server
minimal:
type: opencode
path: ~/.config/opencode/opencode.json
mode: gateway
servers: [] # sees NO downstream servers, only the mcphub meta-toolsA listed server that is disabled (or missing from the enabled set) is dropped silently — routing selects within the enabled servers, it does not enable anything. A servers entry naming a server that doesn't exist in the config at all is a validation error.
Call scope vs advertised surface
servers and tools control what the agent may reach. Gateway-only pin and tool_schema_budget independently control which downstream definitions spend provider context:
agents:
local-agent:
type: local-agent
path: ~/.config/local-agent/config.yaml
mode: gateway
servers: [bob, cortex, vecgrep]
pin:
- bob__context # in expose: lazy, pin the context-read tool
tool_schema_budget: 8KBUnder expose: lazy, omitting pin inherits the top-level pin list. An explicit pin: [] hides pinned downstream definitions without changing the servers/tools call scope, so hidden tools remain discoverable and callable through lazy meta-tools. Pins do not change expose: all. A schema budget applies in either exposure mode, admits complete definitions in deterministic name order, never truncates a schema, and treats 0 as meta-tools only.
How it works in gateway mode
When a gateway-mode agent has routing or advertisement config (a servers/tools/pin key, even empty, or tool_schema_budget), mcphub sync writes its harness entry with an extra flag:
mcphub mcp serve --agent codexinstead of the plain mcphub mcp serve that unscoped agents get. When that gateway starts, it looks up the named agent's servers/tools allowlists and:
- Advertises only the subset. In
expose: all, only in-scopeserver__toolnames are mounted; a schema budget can narrow that visible subset further without removing lazy call authority. Per-agent pin overrides apply only inexpose: lazy. - Scopes the meta-tools too. In
expose: lazy,list_servers,search_tools,describe_tool, andresolve_toolonly surface in-scope servers and tools. - Refuses out-of-scope calls.
call_toolon a tool outside the allowlists fails withtool server__x is out of scope for this agent, andmcphub_get_resultscope-checks the stored server/tool before paging a spooled result.
A bare mcphub mcp serve (no --agent) is unscoped and serves everything, as is --agent <name> for an agent with no routing keys. An --agent naming an agent that doesn't exist in the config is an error — a stale flag in a harness file fails fast instead of silently serving everything or nothing.
Restart to apply
Routing is enforced at gateway startup. After editing servers/tools, run mcphub sync (the --agent flag in the harness entry may need to appear or disappear) and restart the agent so it relaunches its gateway.
How it works in direct mode
A direct-mode agent gets no proxy, so routing happens at sync time: mcphub sync writes only the listed enabled servers into the agent's config, verbatim. servers: [] writes none. tools is not allowed — with no gateway in the path there is nothing to enforce a per-tool list.
agents:
opencode:
type: opencode
path: ~/.config/opencode/opencode.json
mode: direct
servers: [vecgrep] # only vecgrep is written into opencode.jsonAll the usual sync guarantees still apply: dry-run by default, a timestamped .bak before any write, and pruning scoped to entries mcphub owns.
tools rules
The tools list is validated by mcphub doctor (and on every config load):
- Entries are exact namespaced names:
server__tool. No wildcards — to allow a whole server, put it inserversand omittoolsrestrictions for it. - Each entry's server prefix must name a server that exists in the config.
- If the agent also has a
serverslist, everytoolsentry must reference a server on that list — a tool is only callable when both lists allow it. toolson amode: directagent is rejected.
agents:
reviewer:
type: claude
path: ~/.claude.json
mode: gateway
servers: [codemap]
tools: [codemap__find] # OK: codemap is in servers
# tools: [vecgrep__search] # error: vecgrep not in this agent's servers list
# tools: [codemap__*] # error: wildcards are not supportedInspecting routing
mcphub status --server codemap # which agents route to codemap + proxied-call count
mcphub doctor --server codemap # single-server registration/routing/usage summary
mcphub sync # dry run: see the exact --agent args and server setsmcphub sync (dry run, the default) is the fastest way to confirm what each agent will actually receive before you --write anything. Once calls flow, mcphub stats tells you whether a scoped-down agent is actually using what you left in scope.
Routing vs the other knobs
Routing composes with mcphub's other context-budget features rather than replacing them:
expose: lazyshrinks how tools are advertised (meta-tools + on-demand discovery); routing shrinks which tools exist for an agent at all. A scoped lazy gateway discovers only in-scope tools.enabled: falseon a server removes it for everyone; a routing list removes it for one agent.disabled: trueon an agent skips the agent during sync entirely;servers: []still syncs it, just with an empty (gateway) or absent (direct) server set.