# MCP gateway

> Reach your own MCP servers through INFRO with your gateway key: approved and pinned tools, grants per key, guardrails, and tool calls on the trace.

Source: https://infro.io/docs/mcp-gateway

> **Note:** INFRO is in private early access. Running these examples requires an early-access account, an active trial or subscription entitlement, and your own provider connection. Self-serve purchasing is not generally open. See [Early access](https://infro.io/early-access).

> **Note:** The MCP gateway is served at `https://mcp.infro.io/gateway/mcp` and `https://mcp.infro.io/gateway/<slug>/mcp`, on the Business plan and above.

The MCP gateway puts INFRO between your agents and the [Model Context Protocol](https://modelcontextprotocol.io) servers they use: GitHub, Linear, Supabase, Stripe, Datadog, your internal tools. An admin registers each server once, with its credential. Your agents then reach those servers through INFRO with the same gateway key they already use for model calls, and on every call INFRO:

- adds the server's credential from its vault, so the agent never holds it;
- shows each key only the tools an admin approved and granted to it;
- serves the tool definition an admin approved, and refuses a tool whose definition changed on the server since;
- validates the arguments against that definition;
- applies your organization's guardrails to the arguments and the result;
- records the call on the same trace as the agent's model calls.

INFRO never executes a tool and never hosts an MCP server or an agent. The servers are yours, reached with your credentials, and the services behind them bill you under your own accounts. Tool calls are not priced and do not count against your plan's request allowance.

This is a different product from INFRO's own [MCP server](https://infro.io/docs/mcp), which lets an AI assistant read and operate your INFRO organization with a management token. The two share the host `mcp.infro.io`.

## Endpoints

| Endpoint | Lists | Tool names |
| --- | --- | --- |
| `https://mcp.infro.io/gateway/mcp` | Every approved tool, on every server, that this key's grants allow. | `<slug>_<upstream name>`, for example `github_list_issues`. Use this one by default: one entry in the agent's configuration. |
| `https://mcp.infro.io/gateway/<slug>/mcp` | One server's approved tools that this key may use. | The server's own names. Change the URL and the header of an existing integration and nothing in your prompts has to change. |

Both take `Authorization: Bearer <gateway key>` and speak Streamable HTTP. Sandbox organizations use `sandbox-mcp.infro.io`. Recipes for each agent SDK and editor are on [Connect an agent](https://infro.io/docs/mcp-gateway/connect).

### Naming on the aggregated endpoint

- A server's slug is 1 to 12 lower-case letters or digits (`^[a-z0-9]{1,12}$`), unique in the organization. It has no underscore, so the first `_` of an exposed name always separates the slug from the tool's name.
- Every character of the upstream name outside `[A-Za-z0-9_-]`, `.` included, becomes `_`.
- Exposed names are at most 64 characters, the limit some model APIs set for function names. A longer name keeps its first 55 characters, then `_` and the first 8 hex characters of the SHA-256 of the full name.
- If two tools would get the same exposed name, registering or syncing the server is refused with the clashing names listed.
- The per-server endpoint keeps upstream names as they are. A name over 64 characters is served unchanged, and the console warns that some model APIs refuse it.

## Servers

Owners and admins register a server with a slug, a name and its HTTPS URL, and an authentication of either `none` or `headers`: up to eight header names and values, such as `Authorization: Bearer <GitHub token>` or `X-API-Key`. The values are sealed and never shown again; only the header names are. A server can be limited to some projects and some environments (an empty list reaches every one, as with provider connections), and carries its own limits: time limit, result size and calls per minute (see [Limits](https://infro.io/docs/mcp-gateway/limits)).

Registration only succeeds if the server answers `tools/list` with the credential you gave. INFRO detects the protocol it speaks at the same time: the current revision (2026-07-28) or the 2025 `initialize` revisions over Streamable HTTP. The deprecated HTTP+SSE transport (2024-11-05) is not supported. Servers that only take OAuth sign-in, such as Notion, Vercel and Asana, cannot be registered yet; see [Security model](https://infro.io/docs/mcp-gateway/security#what-is-not-supported-yet). In the console, **MCP & connectors → Servers** lists popular hosted servers that take a token or none (GitHub, Linear, Sentry, Supabase, Neon, PostHog, Stripe, Cloudflare, Zapier and several search and documentation servers): choosing one fills in its URL and the header its token goes in, and links to where the vendor makes the token.

Where a server offers a read-only endpoint or token, register that one unless your agents need to write: for example GitHub's `X-MCP-Readonly` header, Linear's read-only path or Supabase's `read_only=true`.

## The tool catalog

INFRO lists each server's tools when it is registered, when an admin presses **Sync**, and every 15 minutes after that (or at the server's own `ttlMs` when it asks for longer, at most every 6 hours). Every tool is in one of five states:

| State | Meaning | Agents see it |
| --- | --- | --- |
| `pending` | Seen for the first time, waiting for an admin. | No |
| `approved` | An admin approved this exact definition and set its class. | Yes, to keys whose grants allow it |
| `changed` | The server now lists a definition different from the approved one. | No; calls are refused with `tool_definition_changed` |
| `removed` | The server no longer lists the tool. If it comes back with the approved definition, it is `approved` again; with another, `changed`. | No |
| `blocked` | An admin blocked it. | No |

A failed sync is not a definition change: approved tools keep serving, the server's health and last sync error are updated, and repeated call failures raise [`mcp.server_failing`](#alerts).

### Pinned definitions

Each definition is hashed: SHA-256 over the canonical JSON (keys sorted) of its `name`, `title`, `description`, `inputSchema`, `outputSchema` and `annotations`. Approving a tool stores that hash and the exact definition, and `tools/list` serves the stored definition from INFRO, never the server's live list. When a sync finds a different hash for an approved tool, the tool becomes `changed`: it disappears from `tools/list`, a call to it is refused before anything reaches the server, and `mcp.tool_changed` is raised. It serves again only after an admin reviews the difference and approves the new definition.

### Review and approval

Owners and admins approve tools. The review shows the full definition, the scan findings and a suggested class, and a `changed` tool shows the difference from what was approved. Approval names the hash the reviewer saw, so a definition that changed between review and approval is refused. Approve, block, re-approve and class changes are written to the [audit log](https://infro.io/docs/org/audit-log). The console can approve every read tool of a server at once, after a review screen.

Each new or changed definition is scanned for known tool-poisoning and shadowing shapes. The scan calls nothing, and it never approves or blocks anything on its own: it informs the person who decides. What it looks for is on [Security model](https://infro.io/docs/mcp-gateway/security#the-definition-scan).

### Read and write classes

An admin classifies each approved tool as `read` or `write`. A server's own annotations (`readOnlyHint`, `destructiveHint`) come from the server, which the MCP specification says to treat as untrusted, so they only suggest a default: `read` when `readOnlyHint` is true, `write` otherwise. The class the admin set is what agents see: a `read` tool is served with `readOnlyHint: true`, a `write` tool with `readOnlyHint: false`.

## Grants

Access is denied by default: a key with no grant sees no tools. A grant gives one gateway key access to one server, in one of three modes:

| Mode | The key may call |
| --- | --- |
| `read` | The server's approved `read` tools. |
| `all` | The server's approved `read` and `write` tools. |
| `selected` | Only the approved tools named in the grant. |

Grants belong to the key's lineage, so [rotating a key](https://infro.io/docs/authentication) keeps them. The key must also be in one of the server's projects and environments. Grants are edited from the key (**Edit tool access**) or from the server.

### Risk labels and the lethal trifecta

Admins label each server with what it can do: **reads private data**, **reads untrusted content** (issues, email, web pages: text someone outside your organization can write) and **can send data out** (posting, emailing, writing to a public repository). When one key's grants would combine all three, the console warns before saving. That combination is what security researchers call the lethal trifecta: text the agent reads from an untrusted source can instruct it to collect private data and send it out, and no filter reliably tells such instructions apart from data. The grant is not refused, because the decision is yours; the warning and your confirmation are audited. Through the management API the same grant needs `confirm_trifecta: true`.

## Kill switches

Each of these refuses the next call, in every region. Server, tool, grant, key, hold and plan are read on every call without a cache.

- **Disable a server.** Its tools disappear and calls are refused. **Stop all tool traffic** disables every server in one audited action (`POST /mcp_servers/disable_all` in the management API).
- **Block a tool.** Hidden and refused, whatever the server lists.
- **Delete a grant.** The key loses that server.
- **Disable the key.** Refused with `403 key_disabled`, for model calls and tool calls alike.
- **An operator hold** on the organization: `403 organization_suspended`.

Disabling, blocking and deleting are never plan-gated. The plan gates only what enables access: registering a server, approving a tool and granting a key.

## Tool calls on the trace

Every call, refusals included, writes one record: time, server, tool, outcome, error code, latency, argument and result sizes, a SHA-256 of the arguments, guardrail counts, and the key, project, environment, trace, workflow and step. Send the same trace id on your model requests and your tool calls, and [`GET /v1/traces/{trace_id}`](https://infro.io/docs/observability/traces#group-requests-into-a-run) returns both. The gateway looks for the trace in this order:

1. `params._meta.traceparent` on the call;
2. the `X-INFRO-Trace-Id` header, with `X-INFRO-Workflow`, `X-INFRO-Step`, `X-INFRO-Feature` and `X-INFRO-User-Id`;
3. the `traceparent` header;
4. `?trace_id=` on the URL, for clients that cannot send headers per run.

Values follow the same rules as on `/v1` (letters, digits and `_ . : -`; a trace id up to 128 characters, the others up to 64); a malformed one is `400 invalid_attribution`. INFRO forwards a W3C `traceparent` to the server in the call's `_meta`, with its own span as the parent, and organizations that [export OpenTelemetry](https://infro.io/docs/observability/exports) get one span per tool call.

## Alerts

Two [alert](https://infro.io/docs/observability/alerts) events, routable to your existing email, Slack and webhook channels: `mcp.tool_changed` when a server changed an approved tool, and `mcp.server_failing` when a server's calls keep failing.

## Plans and roles

- The MCP gateway is included from the Business plan. Without it, the endpoints answer `403 plan_feature`.
- Tool calls are not priced and do not count against the plan's request allowance. The console shows them beside requests.
- Owners and admins register servers, approve tools and grant keys. Every role that can read usage can see servers, the catalog and the call log.
- The same configuration is available through the [management API](https://infro.io/docs/manage/resources#mcp-gateway) (writes from the Team plan's management API, with the Business plan's gateway).
