You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/router/mcp.mdx
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,7 +6,7 @@ icon: "robot"
6
6
7
7
For a high-level introduction, see the [MCP Gateway overview](https://wundergraph.com/mcp-gateway).
8
8
9
-
WunderGraph's MCP Gateway is a feature of the Cosmo Router that enables AI models to interact with your GraphQL APIs using a structured protocol. Instead of allowing arbitrary queries, it exposes a predefined set of validated GraphQL operationsas tools that AI models can discover and use.
9
+
WunderGraph's MCP Gateway is a feature of the Cosmo Router that enables AI models to interact with your GraphQL APIs using a structured protocol. We can expose a predefined set of safelisted operations, or capabilities as MCP tools, or allow agents to execute GraphQL directly.
10
10
11
11
<Framecaption="Cosmo Router exposes your GraphQL Supergraph over MCP, enabling AI platforms like Cursor, Windsurf, and Claude to discover and execute operations.">
@@ -12,14 +12,7 @@ The MCP server supports OAuth 2.1 authorization with JWKS-based JWT validation,
12
12
13
13
## Overview
14
14
15
-
When OAuth is enabled, every HTTP request to the MCP server must include a valid JWT bearer token in the `Authorization` header. The server validates the token using configured JWKS providers and enforces scope requirements at multiple levels:
16
-
17
-
1.**HTTP level** — `initialize` scopes are checked on every request before any JSON-RPC processing
18
-
2.**Method level** — Additional scopes can be required for specific MCP methods (`tools/list`, `tools/call`)
19
-
3.**Per-tool level** — Individual tools can require additional scopes derived from `@requiresScopes` directives in your GraphQL schema
20
-
4.**Runtime level** — The `execute_graphql` tool (when enabled) checks `@requiresScopes` for the specific fields in each query at request time
21
-
22
-
All scope enforcement happens at the HTTP transport level per the MCP specification, returning standard `403 Forbidden` responses with `WWW-Authenticate` headers that enable MCP clients to perform step-up authorization.
15
+
When OAuth is enabled, every HTTP request to the MCP server must include a valid JWT bearer token in the `Authorization` header. The server validates the token using configured JWKS providers and enforces scope requirements at multiple levels. All scope enforcement happens at the HTTP transport level per the MCP specification, returning `403 Forbidden` responses with `WWW-Authenticate` headers that enable [step-up authorization](#token-upgrade-flow).
23
16
24
17
## Quick Start
25
18
@@ -177,99 +170,15 @@ type Query {
177
170
178
171
And you have an operation `getTopSecretFacts.graphql` that queries `topSecretFacts`, calling that tool will require either `read:fact` OR `read:all` in addition to any `initialize` and `tools_call` scopes.
179
172
180
-
The `@requiresScopes` directive uses **OR-of-AND** semantics:
181
-
- `[["read:fact"], ["read:all"]]`means the token needs `read:fact` **OR** `read:all`
When a token lacks the required scopes, the server returns a `403 Forbidden` with a `WWW-Authenticate` header containing the **best** scope challenge — the scope group that requires the fewest additional scopes based on what the token already has.
185
-
186
-
#### Multi-Field Scope Combination
187
-
188
-
When an operation touches **multiple** fields that have `@requiresScopes`, the MCP server computes the **Cartesian product** of their OR-groups to produce the combined scope requirement for the tool. This ensures the token satisfies the requirements of every field in the operation simultaneously.
173
+
The `@requiresScopes` directive uses OR-of-AND semantics. When an operation touches multiple fields with `@requiresScopes`, the MCP server computes the combined scope requirement using the [Cartesian product rules](/federation/directives/requiresscopes#combining-scopes-in-the-same-subgraph). For a full explanation of how scopes combine, see the [`@requiresScopes` directive documentation](/federation/directives/requiresscopes).
The resulting combined requirement for the tool is:
225
-
226
-
```
227
-
(read:employee AND read:private AND read:fact)
228
-
OR (read:employee AND read:private AND read:all)
229
-
OR (read:all AND read:fact)
230
-
OR (read:all)
231
-
```
232
-
233
-
A token satisfying **any one** of these four groups can call the tool.
234
-
235
-
#### Smart Scope Challenge Selection
236
-
237
-
When a token lacks the required scopes, the server doesn't just return an arbitrary scope group — it picks the **best** one based on what the token already has. The algorithm:
238
-
239
-
1. For each AND-group, count how many scopes the token is **missing**
240
-
2. If any group has 0 missing, the token is sufficient (no challenge needed)
241
-
3. Pick the group with the **fewest missing** scopes (ties go to the first group)
242
-
243
-
Using the combined requirement above, if a client presents a token with scopes `read:employee read:private`:
The client can then request the additional `read:fact` scope from the authorization server and retry.
259
-
260
-
<Info>
261
-
The `scope` parameter in the `WWW-Authenticate` header contains the **complete AND-group** (not just the missing scopes), per RFC 6750. This tells the client exactly which scopes are needed for the operation. If `scope_challenge_include_token_scopes` is enabled, the token's existing scopes are also included — see [Scope Challenge Behavior](#scope-challenge-behavior).
262
-
</Info>
263
-
264
-
<Info>
265
-
Per-tool scopes are computed at startup (and on config reload) by analyzing which fields each operation touches. This means scope requirements are enforced at the HTTP level before the GraphQL query is executed, with zero runtime overhead per request.
266
-
</Info>
175
+
Per-tool scopes are computed at startup (and on config reload), so they are enforced at the HTTP level with zero runtime overhead per request.
267
176
268
177
### Runtime Scopes (`execute_graphql`)
269
178
270
179
When `enable_arbitrary_operations` is enabled, the `execute_graphql` tool allows AI models to craft custom GraphQL queries. Since the server cannot know which fields will be queried ahead of time, scope checking happens at request time by parsing the GraphQL query and extracting `@requiresScopes` requirements for the fields it references.
271
180
272
-
This runtime check uses the same OR-of-AND semantics and smart challenge selection as per-tool scopes. If the token lacks required scopes, the server returns a `403 Forbidden` with an appropriate scope challenge before the query is executed.
181
+
This runtime check uses the same [OR-of-AND semantics](/federation/directives/requiresscopes) and smart challenge selection as per-tool scopes. If the token lacks required scopes, the server returns a `403 Forbidden` with an appropriate scope challenge before the query is executed.
273
182
274
183
<Info>
275
184
If the GraphQL query cannot be parsed, the request is passed through to the GraphQL engine, which handles the error. Scope checking is best-effort and does not block malformed queries.
@@ -287,7 +196,7 @@ This runtime check uses the same OR-of-AND semantics and smart challenge selecti
287
196
288
197
## `get_operation_info` and Scope Discovery
289
198
290
-
The built-in `get_operation_info` tool includes scope requirements in its response when a tool's underlying operation has `@requiresScopes` directives. This allows AI models to understand what scopes are needed before attempting to call a tool:
199
+
The `get_operation_info` tool includes scope requirements in its response, allowing AI models to discover what scopes a tool needs before calling it:
This enables AI assistants to inform users about required permissions before attempting operations that may fail due to insufficient scopes.
300
-
301
208
## Token Upgrade Flow
302
209
303
-
Because authentication happens at the HTTP level, tokens can be upgraded **on the same MCP session** without reconnecting:
304
-
305
-
1. A client connects with a token that has `mcp:connect` scope
306
-
2. The client calls `tools/call` and receives a `403 Forbidden` with `insufficient_scope`
307
-
3. The client obtains a new token with additional scopes from the authorization server
308
-
4. The client retries with the new token on the same session (same `Mcp-Session-Id`)
210
+
Tokens can be upgraded **on the same MCP session** without reconnecting:
309
211
310
-
This step-up authorization pattern is fully supported and does not require re-establishing the MCP session. The flow is the same regardless of which scope level triggered the 403 (method, per-tool, or runtime).
212
+
1. Client connects with a token that has `mcp:connect` scope
213
+
2. Client calls `tools/call` and receives `403 Forbidden` with `insufficient_scope`
214
+
3. Client obtains a new token with additional scopes from the authorization server
215
+
4. Client retries with the new token on the same session (same `Mcp-Session-Id`)
311
216
312
217
## Scope Challenge Behavior
313
218
314
-
When the server returns a `403 Forbidden` response, the `WWW-Authenticate` header includes a `scope` parameter telling the client which scopes to request. The `scope_challenge_include_token_scopes` option controls how this parameter is built.
219
+
When the server returns a `403 Forbidden` response, the `WWW-Authenticate` header includes a `scope` parameter per [RFC 6750](https://datatracker.ietf.org/doc/rfc6750/) telling the client which scopes to request.
220
+
221
+
For per-tool and runtime challenges where multiple scope groups can satisfy the requirement (OR-of-AND), the server selects the **best** group — the one requiring the fewest additional scopes based on what the token already has:
222
+
223
+
1. For each AND-group, count how many scopes the token is **missing**
224
+
2. Pick the group with the **fewest missing** scopes (ties go to the first group)
225
+
226
+
For example, suppose a tool requires `(read:employee AND read:private AND read:fact) OR (read:all)` and a client presents a token with scopes `read:employee read:private`:
Both groups have 1 missing scope. The server picks the first, returning the **complete AND-group** (not just the missing scopes):
315
234
316
-
Per [RFC 6750](https://datatracker.ietf.org/doc/rfc6750/), the `scope` parameter should contain the scopes *"required for accessing the requested resource"*. The server returns only the scopes needed for the specific operation that failed — not all scopes the server supports.
For per-tool and runtime scope challenges where multiple scope groups can satisfy the requirement (OR-of-AND), the server selects the **best** group: the one requiring the fewest additional scopes based on what the token already has. This minimizes the authorization step-up needed.
239
+
The client can then request the additional `read:fact` scope from the authorization server and retry.
0 commit comments