Safelist GraphQL Operations for AI Agents | Cosmo MCP Gateway

The Cosmo Router MCP Gateway exposes your federated GraphQL schema to AI agents as reviewed, OAuth-scoped MCP tools. This is the implementation companion to Don't Let AI Agents Improvise Against Production GraphQL, which makes the case for the pattern: let agents discover in development, but only let them execute reviewed operations in production. Here we build it on the Cosmo Router MCP Gateway, end to end: the two postures, how operations become tools, OAuth scopes, and what happens when your schema changes.
The MCP Gateway is a feature of the Cosmo Router. It speaks the 2025-06-18 MCP specification over Streamable HTTP, and AI platforms like Claude, Cursor, and Windsurf connect to it directly. It operates on your composed supergraph, so a single tool call can resolve data across every subgraph in your federation. There is no separate service to deploy.
Two configuration flags define its posture:
expose_schemacontrols whether agents can read your full schema.enable_arbitrary_operationscontrols whether agents can compose and run GraphQL you never reviewed.
Open both in development. Close both in production. The operations you safelist in between are the contract that survives the transition.
The gateway reads .graphql operation files from a storage provider directory, validates each one against your composed schema, generates a JSON Schema for its variables, and exposes it as a discrete MCP tool. It runs inside the router and executes operations against the same composed supergraph and federation engine that serves your GraphQL traffic, so there is no separate sidecar or translation service to deploy and keep in sync. Because operations are validated at load time and re-validated on hot reload, a malformed or stale operation is caught before an agent can ever call it.
The tools an agent sees depend entirely on your configuration:
| Tool | When it exists | What it does |
|---|---|---|
execute_operation_<name> | One per safelisted .graphql file | Runs a single pre-approved operation. Inputs are typed from the operation's variables. |
get_operation_info | Always | Returns an operation's input schema and execution guidance so the agent calls it correctly. |
get_schema | Only when expose_schema: true | Returns the full GraphQL schema as a string for the agent to introspect. |
execute_graphql | Only when enable_arbitrary_operations: true | Executes any query or mutation the agent composes. Marked as a destructive, open-world tool. |
get_schema and execute_graphql are the only two tools that open the graph beyond your safelist, and both are off by default. A production gateway with both disabled exposes nothing but the operations you placed in the directory.
In development you want maximum leverage. Turn both flags on:
Now an agent connected through Cursor or Claude can call get_schema, understand your federated types, and use execute_graphql to draft, run, and refine queries against real data. You and the agent converge on the handful of operations that actually matter, and you save each one as a .graphql file.
A discovered query becomes a reviewed artifact when you save it as a named operation, with a triple-quoted block-string description that becomes the tool description the agent reads:
That file is now a persisted operation, a trusted document, and it goes through the same review as any other code: right fields, no unintended PII, the correct authorization scopes, safe for repeated execution, and query-only versus mutation-capable. The exploratory phase produces a tangible, reviewable artifact: a directory of operations. That directory is the contract.
Close both flags and point the gateway at your reviewed operations:
In this posture the agent can only call execute_operation_* tools, one per file you approved. It cannot read the full schema. It cannot compose a query. Every tool maps to an operation a human reviewed, and GraphQL's type system guarantees the agent can only pass inputs your operation declares. This collapses the attack surface from "the whole schema" to the queries you ship, and production rejects anything not on the allowlist.
The safelist is not a proprietary artifact. It is nothing but standard GraphQL operations. Move off this router tomorrow and those .graphql files are still valid GraphQL against your schema. You are encoding your AI-facing surface in the same language as your API, not in a format you can only read with one vendor's tooling.
Layer OAuth 2.1 on top and the control becomes per-operation. The gateway enforces JWT-based authorization on the MCP methods tools/list and tools/call, and applies per-tool scope requirements for the built-in tools such as get_schema and execute_graphql. It honors @requiresScopes directives so that each tool inherits the exact scopes its underlying GraphQL fields require. An agent does not just see fewer tools; it is authorized for exactly the ones its credentials permit. And because every agent presents its own JWT, each call carries an agent identity you can attribute in logs and traces, so machine-to-machine access stays investigable when an incident demands it.
For operations that need a stronger bar at call time, the gateway supports OAuth 2.1 scope step-up flows.
There is also a recognized safe middle ground. Keep expose_schema: true in production while leaving enable_arbitrary_operations: false. Introspection stays on, so agents can reason about your API surface, but execution is still restricted to the operations you safelisted. This is safe precisely because only registered operations ever run: schema visibility grants no execution capability.
The objection every platform team raises next is maintenance: if the schema changes, do I have to rewrite all my operations? No, not for additive changes, and that is the reason the model holds up over time.
Adding fields, types, or arguments to your schema does not break or require updating your safelisted operations. The Cosmo Router re-validates every operation against the new schema on each hot reload. Operations that still validate are re-registered automatically. Your safelisted queries keep working untouched; they simply do not use the new capabilities.
The maintenance contract is narrow and predictable:
- Additive schema changes are free. New fields, new types, new arguments. Existing operations are unaffected.
- New capabilities are exposed only on purpose. A new field becomes reachable by an agent only when you add or extend an operation that selects it. Nothing is auto-exposed. This is least-privilege as a default, not as a discipline you have to remember.
- You touch an operation only when a change breaks it. If a field an operation selects is removed or changes type incompatibly, the router logs the error and skips that one operation. The rest keep serving. You update the file to match and it re-registers on the next reload.
The hot-reload path means none of this requires a redeploy: push a new schema, the gateway re-validates and re-registers, and your scope requirements are recomputed from the current @requiresScopes directives automatically. This is governance as a guardrail rather than a review queue. The rule that new capabilities are exposed only on purpose cannot be quietly skipped by any team, so schema owners stop being the manual approval gate everyone routes around.
The reflex when you hit this problem is to build a second, "AI-safe" API: a curated REST surface that exposes only the approved slice of data. It works, and it is a trap. You now own two sources of truth that drift, and the curated one ages into exactly the legacy layer you will be migrating off in three years.
The safelist gives you the same safety without the second system.
| Parallel AI-safe API | Cosmo Router MCP Gateway | |
|---|---|---|
| Source of truth | A second API that drifts from the first | Your existing supergraph |
| Time to first safe operation | A new build-and-review cycle | Design in dev, save a .graphql file |
| Governance granularity | Whatever you hand-build | Per-operation, with OAuth scopes and @requiresScopes |
| Schema evolution | Manual sync across two surfaces | Automatic re-validation on hot reload |
| Federation support | Re-implement aggregation | Native; operations span subgraphs |
| Portability | Logic locked in a bespoke service | Standard .graphql persisted documents, valid against your schema anywhere |
| What the agent can reach | Whatever the second API exposes | Exactly the safelisted operations, nothing more |
You are not standing up infrastructure to make your API safe for AI. You are configuring a posture on infrastructure you already run.
Start with the MCP Gateway quickstart to have a server running in five minutes, then read the operations guide to design your first safelist. For the reasoning behind this whole setup, read the companion: Don't Let AI Agents Improvise Against Production GraphQL.
Principal Solutions Engineer at WunderGraph
Ahmet is a Principal Solutions Engineer at WunderGraph, helping teams adopt Cosmo. He leads technical evaluations, builds prototypes, and runs workshops to accelerate adoption, while improving SDKs, documentation, and onboarding to enhance the developer experience.

