Skip to content

Arion HTTP filter that serves a Model Context Protocol endpoint, maps tools onto REST, OpenAPI and MCP upstreams, and runs agent scripts in code mode.

AttachmentType URL (@type)Per-route overrideBuild feature
HTTP filter chain (http_filters)type.googleapis.com/arion.extensions.filters.http.mcp.mcp_gateway.v3.McpGatewaydisabled onlyNone; codemode for code_mode

What it does

The MCP gateway is an Arion extension that answers Model Context Protocol traffic over streamable HTTP on /mcp and owns a registry of namespaced tools. Tools come from static REST or MCP backends, from OpenAPI documents and from dynamic MCP servers, and TDS streams more over xDS. In tools mode agents list and call the tools directly. In code mode they send scripts to a single execute tool. The overview explains the moving parts; this page is the field reference.

Minimal configuration

This fragment is an entry in http_filters, before the router:

- name: arion.filters.http.mcp
  typed_config:
    "@type": type.googleapis.com/arion.extensions.filters.http.mcp.mcp_gateway.v3.McpGateway
    server_info:
      name: arion-mcp-gateway
      version: "1.0.0"
    tools_mode: {}
    tools:
      - namespace: weather
        name: get_forecast
        description: Get the weather forecast for a city
        disclosure_mode: EAGER
        input_schema:
          inline_string: >-
            {
              "type": "object",
              "properties": { "city": { "type": "string" }, "days": { "type": "integer" } },
              "required": ["city"]
            }
        rest_backend:
          cluster: weather_api
          method: GET
          path: "/v1/forecast/{{city | encode_path_segment}}"
          query_params:
            - name: days
              source: days

Top-level fields

FieldTypeRequiredDefaultDescription
server_info.namestringYes—MCP server identity. Must not be empty or contain /, and must be unique per Arion instance when tds is set.
server_info.versionstringYes—Version reported to clients.
tools_mode / code_modeoneofYes—The surface agents see. tools_mode: {} serves the registry as MCP tools. code_mode serves the single execute tool; see code mode. A config with neither fails with Missing field: mode.
toolslistNoemptyStatic tools; see tools.
open_api_tool_sourceslistNoemptyOpenAPI documents expanded into one REST tool per operation; see OpenAPI tool sources.
dynamic_mcp_serverslistNoemptyUpstream MCP servers whose tools are discovered at runtime; see dynamic MCP servers.
searchobjectNoBM25, 5 resultsTool search ranking and result limit; see search.
tds{config_name}NounsetSubscribes this filter to a Tool Discovery Service stream. config_name must not be empty or contain /.
upstream_timeoutdurationNo15sTimeout for upstream requests that set none of their own. Must be greater than zero.
max_upstream_response_bytesuint64No1048576Cap on a REST response body or a serialized MCP tool result. Must be greater than zero.

What belongs to which mode

The registry and its sources are shared between the modes; the mode decides the surface agents see. When a field matters in only one mode, this table and the notes in each section say so.

ConfigurationTools modeCode mode
tools, open_api_tool_sources, dynamic_mcp_servers, tdsFill the registryFill the registry
disclosure_mode / tool_disclosure_modeDecides tools/list versus tool_searchIgnored: the catalog sees every permitted tool
searchRanks tool_search and POST /mcp/searchRanks catalog.tools.search
rbacHides and guards toolsAlso filters the catalog and toolkit visibility
code_mode limits—Sandbox and run limits; see code mode
code_mode.toolkits—Curated bundles in the execute description; see toolkits
POST /mcp/searchSessionless search404

tool_search arguments

In tools mode, tool_search is served automatically when the caller has at least one permitted progressive tool. It takes:

ArgumentTypeRequiredDefaultDescription
querystringYes—Free text, ranked against tool names and descriptions with BM25 or the configured embeddings model.
namespacestringNoall namespacesRestrict matches to one namespace.
limituint32Nosearch.max_allowable_resultsMost results to return, clamped to search.max_allowable_results.

It returns the best-matching progressive tools as full MCP definitions (name, description, inputSchema, outputSchema) in the result's text content. POST /mcp/search takes the same three arguments as its JSON body. In code mode, the counterpart is catalog.tools.search in the SDK reference.

Tools

Every tool is addressed by its qualified name, namespace.name. That name appears in tools/list, in tools/call, in code-mode scripts and in metrics. Each advertised tool also carries _meta: {"arion/namespace": "<namespace>"}.

FieldTypeRequiredDefaultDescription
namespacestringYes—Letters, digits, _ and -. A namespace belongs to exactly one kind of source: static or TDS tools, one OpenAPI source, or one dynamic server.
namestringYes—Local name: letters, digits, _, - and .. The qualified name must be unique and at most 128 bytes. For an MCP server backend, this is the upstream tool name.
descriptionstringYes—Description shown to models and indexed by search.
input_schemadata sourceNoemptyJSON Schema (inline_string, filename, …), compiled at load and enforced on every call. Ignored, with a warning, for MCP server backends.
output_schemadata sourceNoemptyJSON Schema for results. When set on a REST tool, responses are parsed, validated and returned as structured content. Ignored for MCP server backends.
rest_backend / mcp_server_backendoneofYes—Where calls go; fields below. function_graph_backend is not supported.
rbacobjectNounsetPer-tool authorization; see tool RBAC.
embeddinglist of floatNoemptyPrecomputed vector for remote-embedding search. Must be finite and match search.embeddings.dimensions.
disclosure_modeenumNoPROGRESSIVETools mode only. EAGER lists the tool in every tools/list. PROGRESSIVE leaves it out; agents find it with tool_search and can call it by name at any time. The code-mode catalog ignores it. See tool search.

REST backend

FieldTypeRequiredDefaultDescription
clusterstringYes—Arion cluster the rendered HTTP request is sent to, resolved directly without the route table.
methodstringYes—HTTP method.
pathstringYes—Request path. Double-brace placeholders take call arguments. The encode_path_segment filter ({{id | encode_path_segment}}) percent-encodes a scalar value for one path segment.
query_paramslistNoempty{name, source} pairs. source is a dot path into the arguments. Absent values are omitted, and an array becomes repeated parameters.
body_templatedata sourceNounsetBody template rendered from the arguments. Sent as application/json.
body_jsonstringNounsetDot path to one argument whose value is serialized as the JSON body. If the argument is absent, no body is sent. Mutually exclusive with body_template.
upstream_policy.authoritystringNounsetHost/:authority to send. Required for ORIGINAL_DST clusters.
upstream_policy.timeoutdurationNoupstream_timeoutPer-tool override of the gateway-wide timeout.
upstream_policy.retry_policyobjectNounsetRoute-style retry policy for the upstream call.

See tools and REST backends for the transcoding rules.

MCP server backend

FieldTypeRequiredDefaultDescription
transportenumNoSseSse or StreamableHttp. Only StreamableHttp is implemented for upstream connections, so set it explicitly.
urlstringYes—Absolute http or https URL of the upstream MCP endpoint. The gateway's MCP client dials it directly, with no cluster involved.

OpenAPI tool sources

Each entry expands an OpenAPI 3.x document into one REST-backed tool per operation, named {namespace}.{operationId}, when the configuration loads. See OpenAPI tools for the mapping rules.

FieldTypeRequiredDefaultDescription
specdata sourceYes—The OpenAPI 3.x document, JSON or YAML. Swagger 2.0 is rejected.
namespacestringYes—Namespace of every derived tool: letters, digits, _ and -. Must not be shared with another source.
clusterstringYes—Cluster every derived tool calls.
upstream_policyobjectNounsetauthority, timeout and retry_policy, applied to every derived tool.
rbacobjectNounsetApplied to every derived tool.
tool_disclosure_modeenumNoPROGRESSIVEDisclosure mode of every derived tool; tools mode only, as disclosure_mode.
path_prefixstringNopath of servers[0].urlBase path placed before every operation path. Replaces the spec's server path.
skip_unsupported_operationsboolNofalseBy default, an operation that cannot become a tool fails the whole configuration. When true, it is skipped with a warning.
include_operationslist of stringNoempty (all)Glob patterns (*, ?) matched against an operation's operationId and its METHOD /path form.
exclude_operationslist of stringNoemptySame patterns, applied after include_operations.

Dynamic MCP servers

Each entry is polled with tools/list, and every discovered tool joins the registry as {namespace}.{upstream tool name}.

FieldTypeRequiredDefaultDescription
namespacestringYes—Namespace of this server's tools, and the server's identity over TDS.
descriptionstringNo—Human description.
transportenumNoSseSame values and caveat as the MCP server backend.
urlstringYes—Absolute http or https URL of the upstream MCP endpoint.
cache_durationdurationNounsetMinimum interval between refreshes. If unset, a successful first fetch is cached indefinitely. A failed fetch is retried after 30 seconds, and the last good tool list keeps serving meanwhile. Must be greater than zero.
rbacobjectNounsetApplied to every tool materialized from this server.
tool_disclosure_modeenumNoPROGRESSIVEDisclosure mode of every tool materialized from this server; tools mode only, as disclosure_mode.

Tool RBAC

rbac guards tool calls and hides unauthorized tools from listing, search and the code-mode catalog. It uses the JWT validated by JWT authentication; details are in tool authorization.

FieldTypeRequiredDefaultDescription
actionenumNoALLOWALLOW grants access on a matching permission; DENY blocks on one.
permissionslistYes—At least one jwt_claim: {field, value} or jwt_header: {field, value}. Any match decides.

search tunes how tool_search, POST /mcp/search and the code-mode catalog.tools.search rank tools; see tool search.

FieldTypeRequiredDefaultDescription
max_allowable_resultsuint32No5The most results one search returns. A caller's limit defaults to this value and is clamped to it. Must be greater than zero.
embeddingsobjectNounsetOpenAI-compatible embeddings endpoint. When omitted, tools are ranked with the built-in BM25 scorer.
embeddings.clusterstringYes—Cluster used to reach the endpoint.
embeddings.model_idstringYes—Model id sent in the request body.
embeddings.pathstringNo/v1/embeddingsRequest path on the endpoint.
embeddings.timeoutdurationNo5sRequest timeout.
embeddings.dimensionsuint32Yes—Vector dimension produced by the model. Must be greater than zero.
embeddings.max_batch_sizeuint32No64Most tools sent in one embeddings request. Must be greater than zero.

Code mode

requires: codemode

code_mode replaces the tool list with the single execute tool. A binary built without the codemode feature rejects it. Numeric fields left at 0 take their defaults. The script API and the full list of limits are in the code mode SDK reference.

FieldTypeRequiredDefaultDescription
execution_timeoutdurationNo30sWall-clock limit for one run. Must be greater than zero. A caller's timeout_ms can only lower it.
max_host_callsuint32No100Tool calls, catalog calls and log lines allowed in one run.
max_concurrent_executionsuint32No32Runs at once on this listener. Further calls are refused, not queued. Must not exceed runtime.codemode.pool_capacity.
max_concurrent_tool_callsuint32No8Tool and catalog calls in flight within one run. Extra calls wait.
max_output_bytesuint64No1048576Cap on the final result and on each call result a script receives. At least 1024.
memory_limit_bytesuint64No67108864Sandbox memory for one run. Must not exceed runtime.codemode.max_memory_bytes.
engine_artifact_pathstringNobundledPath to a replacement sandbox component. Its imports are checked when the listener loads.
toolkitslistNoemptyCurated tool bundles offered to scripts; fields below.

Toolkits

A toolkit appears in the execute description and in catalog.toolkits only for callers who may use every tool it names.

FieldTypeRequiredDefaultDescription
namestringYes—Unique name: letters, digits, _, - and ..
descriptionstringYes—The kind of task the toolkit is for, shown in the execute description.
instructionsstringYes—Guidance on composing the tools, returned by catalog.toolkits.get.
toolslistYes—At least two distinct {namespace, name} references. A missing tool hides the toolkit instead of failing the configuration.

Endpoints

RequestModeAnswer
POST /mcpBothJSON-RPC: initialize, ping, tools/list, tools/call. Every request after initialize carries mcp-session-id.
DELETE /mcpBothEnds the session.
GET /mcpBoth405. There is no SSE stream.
POST /mcp/searchTools modeSessionless search: body {query, namespace?, limit?}, answer {"tools": [...]} over every permitted tool. 404 in code mode.

Ordering and interactions

Place the filter after CORS and after JWT authentication, because tool RBAC needs the validated claims, and before the router. Tool calls dispatch to their configured clusters, and the route table handles only requests the filter does not, so a plain fallback route is enough. TDS resources arrive as {server_info.name}/{config_name}/{resource_name}; see TDS resources. Tool invocations, including every call a code-mode script makes, are counted in the mcp_* metrics under the qualified tool name.

Continue

Start typing to search the site.

↑ ↓ to move Enter to open Esc to close