TDS delivers MCP tools, OpenAPI tool sources, dynamic MCP servers and code-mode toolkits to a running MCP gateway as xDS extension resources. Tool catalogs change without replacing listeners, routes or TLS.
Enabling TDS on a gateway
TDS rides the same delta ADS stream as every other resource, so the bootstrap must configure dynamic_resources. On the gateway itself, set tds.config_name in the MCP gateway filter:
typed_config:
"@type": type.googleapis.com/arion.extensions.filters.http.mcp.mcp_gateway.v3.McpGateway
server_info:
name: tenant-a-mcp
version: "1.0.0"
tools_mode: {}
tds:
config_name: main
tools: [] # static tools may coexist with TDS-delivered ones
Arion subscribes to all four TDS type URLs with a wildcard when the first TDS-enabled gateway loads. Resources delivered over TDS land in that gateway's registry next to any statically configured tools, OpenAPI sources and dynamic servers.
Scopes and resource IDs
Every TDS resource is addressed to one gateway scope:
| Piece | Value |
|---|---|
| Scope | {server_info.name}/{tds.config_name}, for example tenant-a-mcp/main |
| Resource ID | {server_info.name}/{tds.config_name}/{resource_name}, for example tenant-a-mcp/main/weather.get_forecast |
server_info.name and tds.config_name must not contain /; the resource name may. The resource name is canonical for every type. If the payload's own name or namespace differs, Arion uses the resource name, so a later remove targets the same key.
| Type | Resource name |
|---|---|
Tool | namespace.name. A name that does not parse as a qualified name is NACKed. |
OpenApiToolSource | The source's namespace |
DynamicMcpServer | The server's namespace |
Toolkit | The toolkit's name |
Two gateways are isolated as long as their scopes differ: the same config_name under different server_info.name values never shares resources. For TDS-enabled gateways, Arion rejects duplicate server_info.name values in one runtime, which prevents accidental fan-out.
Resource payloads
Tool
Type URL: type.googleapis.com/arion.extensions.filters.http.mcp.mcp_gateway.v3.Tool
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
namespace | string | yes | — | Tool namespace; overridden by the resource name if they differ. |
name | string | yes | — | Local tool name; overridden by the resource name if they differ. |
description | string | yes | — | Description shown to MCP clients and used by tool search. |
input_schema | DataSource | no | empty object | JSON Schema for the tool's arguments. |
output_schema | DataSource | no | unset | JSON Schema for the result. |
rest_backend | RestBackend | one of | — | Execute by calling a REST upstream. |
mcp_server_backend | McpServerBackend | one of | — | Proxy the call to an upstream MCP server (transport: Sse or StreamableHttp, plus url). |
function_graph_backend | object | one of | — | Reserved backend kind; carries no fields. |
rbac | ToolRbac | no | unset | Per-tool JWT access rules. |
embedding | list of float | no | [] | Precomputed embedding vector for search. When it is omitted and remote embeddings are configured, Arion embeds the tool on the next search. |
disclosure_mode | enum | no | PROGRESSIVE | EAGER lists the tool in tools/list; PROGRESSIVE leaves it to search. |
RestBackend fields: method, path, query_params (a list of name/source), cluster, body_template (DataSource) or body_json (string), and upstream_policy (authority, timeout, retry_policy).
OpenApiToolSource
Type URL: type.googleapis.com/arion.extensions.filters.http.mcp.mcp_gateway.v3.OpenApiToolSource
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
spec | DataSource | yes | — | OpenAPI 3.x document, JSON or YAML. A filename is read on the Arion host when the resource arrives. |
namespace | string | yes | — | Namespace of every derived tool; overridden by the resource name if they differ. |
cluster | string | yes | — | Cluster every derived tool calls. |
upstream_policy | HttpUpstreamPolicy | no | unset | Applied to every derived tool. |
rbac | ToolRbac | no | unset | Applied to every derived tool. |
tool_disclosure_mode | enum | no | PROGRESSIVE | Disclosure mode of every derived tool. |
path_prefix | string | no | path of servers[0].url | Base path for every operation. |
skip_unsupported_operations | bool | no | false | Skip operations that cannot become tools instead of rejecting the resource. |
include_operations, exclude_operations | list of string | no | empty | Glob filters on operationId and METHOD /path; see OpenAPI tools. |
DynamicMcpServer
Type URL: type.googleapis.com/arion.extensions.filters.http.mcp.mcp_gateway.v3.DynamicMcpServer
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
namespace | string | yes | — | Namespace of the discovered tools, registered as {namespace}.{upstream_tool}. Overridden by the resource name if they differ. |
description | string | no | empty | Description of the server. |
transport | enum | no | Sse | Sse or StreamableHttp; only StreamableHttp is implemented. |
url | string | yes | — | Absolute http or https URL of the upstream MCP endpoint. |
cache_duration | duration | no | unset | Minimum interval between refreshes. When unset, a successful first fetch is cached indefinitely. |
rbac | ToolRbac | no | unset | Access rules applied to every tool materialized from this server. |
tool_disclosure_mode | enum | no | PROGRESSIVE | Disclosure mode of every tool materialized from this server. |
Toolkit
Type URL: type.googleapis.com/arion.extensions.filters.http.mcp.mcp_gateway.v3.Toolkit
Consumed only by gateways in code mode; tools-mode gateways ACK and ignore it.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | yes | — | Toolkit name; always replaced by the resource name. |
description | string | yes | — | The kind of task the toolkit is for. |
instructions | string | yes | — | Guidance on composing the tools. |
tools | list of {namespace, name} | yes | — | At least two distinct tool references. |
ToolRbac fields: action (ALLOW or DENY) and permissions, a list where each entry is a jwt_header or jwt_claim matcher with field and value. See tool authorization.
Update and remove semantics
| Event | Effect |
|---|---|
Tool update | Validates the tool through the same path as static config, then adds or replaces it in every registry of the scope. It can replace a static tool with the same qualified name. It is NACKed if an OpenAPI source or a dynamic server owns the namespace. An embedding failure does not fail the update. |
Tool remove | Removes the static or TDS-delivered tool with that qualified name. A static definition does not come back until the configuration reloads. Tools derived from an OpenAPI source or a dynamic server are not touched. |
OpenApiToolSource update | Expands the document and replaces every tool in the namespace with the result. An invalid document is NACKed, and the registry keeps its previous tools. |
OpenApiToolSource remove | Evicts every tool derived from the source. |
DynamicMcpServer update | Adds or replaces the server, fetches its tools/list straight away, and materializes the tools. Replacing a server evicts its previous tools. A failed fetch NACKs the update. |
DynamicMcpServer remove | Removes the server and evicts every tool materialized from it. |
Toolkit update | Adds or replaces the toolkit in every code-mode registry of the scope. |
Toolkit remove | Removes the toolkit. |
Malformed resource IDs and undecodable or invalid payloads are NACKed. An update or remove for a scope with no live gateway is ACKed as a no-op, so a control plane can publish resources before the matching listener exists.