Skip to content

Federating MCP servers

Put Arion in front of upstream MCP servers: auto-discover their tools under a namespace, and control disclosure, caching and refresh.

Dynamic MCP servers let one Arion gateway front a fleet of upstream MCP servers. Arion calls tools/list on each upstream, materializes the returned tools into its own registry under that server's namespace, and serves them next to your static tools. Use federation when agents should see one endpoint, with one authentication story and one search index, instead of connecting to every server separately.

Add a dynamic server

dynamic_mcp_servers sits next to tools under the filter's typed_config:

dynamic_mcp_servers:
  - namespace: github
    description: GitHub repository and pull request tools.
    transport: StreamableHttp
    url: http://127.0.0.1:5678/mcp
    cache_duration: 5m
    tool_disclosure_mode: PROGRESSIVE

Discovered tools are exposed under the server's namespace. If the upstream returns get_pull_request, agents see github.get_pull_request, and Arion sends the original name upstream when it forwards the call. The upstream stays authoritative for input schemas, output schemas and result shapes. A namespace belongs to that server alone: static tools and OpenAPI sources cannot use it.

tool_disclosure_mode applies to every tool from the server. Upstream catalogs tend to be large, so the default, PROGRESSIVE, keeps them out of tools/list and lets agents find them through tool search. Set EAGER for a small server whose tools every agent needs.

Always set transport: StreamableHttp explicitly. It is the only implemented upstream transport, and the field's default value, Sse, fails discovery. url must be an absolute http or https URL. The gateway dials it directly with its own MCP client, so upstream MCP traffic does not pass through a cluster or the route table.

Caching and refresh

  • Tools are fetched when the registry is first used, typically by the first tools/list, tool_search or code-mode catalog call.
  • cache_duration is the minimum interval between refreshes; the next list or search after it expires triggers one. If it is unset, the first successful fetch is cached indefinitely.
  • A successful refresh replaces that server's tools with the latest list. After a failed refresh, the previous tools keep serving, and Arion waits 30 seconds, or cache_duration if it is set, before trying again.
  • Every upstream tool name must be a valid local name, and names must be unique. A tool list that breaks either rule is rejected whole, and the previous tools keep serving.
  • Tool calls reuse per-session upstream connections and are bounded by the gateway's upstream_timeout and max_upstream_response_bytes.

Authorize a whole server

An rbac block on a dynamic server applies to every tool materialized from it, which is handy when one upstream serves one tenant or team:

dynamic_mcp_servers:
  - namespace: repo
    description: Repository management tools.
    transport: StreamableHttp
    url: http://repo-mcp.internal:5678/mcp
    cache_duration: 5m
    rbac:
      action: ALLOW
      permissions:
        - jwt_claim:
            field: tenant
            value: tenant-a

Per-tool rules use the same shape; see tool authorization.

Pin instead of discover

When you want only a few known tools from an upstream, with stable names and per-tool RBAC, declare them as static tools with an mcp_server_backend instead of federating the whole server. See tools and REST backends. Prefer static REST-backed tools when the backend is your own HTTP API and you want Arion to enforce schemas. Prefer federation when the catalog belongs to someone else and changes on their schedule.

Verify

Search the server's namespace over HTTP; the first search after startup performs the discovery, so it can take one upstream round trip:

curl -s http://127.0.0.1:8080/mcp/search \
  -H 'content-type: application/json' \
  -d '{"query":"pull request","namespace":"github","limit":5}'

The answer contains entries such as github.get_pull_request. With tool_disclosure_mode: EAGER, they also appear in tools/list next to your static tools.

Limits

  • Upstream transport is streamable HTTP only; there is no upstream SSE support.
  • Dynamic tools are owned by their server. They are replaced or removed with it and cannot be deleted one at a time, also over TDS.
  • Namespaces must be unique across servers, and a server's namespace cannot hold static or OpenAPI tools.
  • Input and output schema enforcement does not apply to forwarded calls.

Field-by-field details are in the filter reference.

Continue

Start typing to search the site.

↑ ↓ to move Enter to open Esc to close