MCP protocol flows
This page describes what an MCP client and the gateway at POST /gateway/mcp send each other. Connect clients covers the setup for each client. This page is for building a client or reading a trace.
We checked the discovery responses and the method handling in Call tools on an instance. We have not run the sign-in steps by hand for this page.
Find the authorization server
-
The client calls
POST /gateway/mcpwith no token. Every method needs a token,initializeincluded. The answer is HTTP 401.HTTP/1.1 401
WWW-Authenticate: Bearer resource_metadata="https://your-environment.example/.well-known/oauth-protected-resource"
{"jsonrpc":"2.0","id":1,"error":{"code":-32001,"message":"Authentication required"}} -
The client fetches the protected resource metadata (RFC 9728).
{
"resource": "https://your-environment.example/gateway/mcp",
"authorization_servers": ["https://your-environment.example"],
"bearer_methods_supported": ["header"],
"scopes_supported": ["openid", "profile", "email", "mcp:tools", "mcp:resources", "read", "orderbook:read"]
}scopes_supportedlists the scopes in thecimdentry of thepbac.operator.controls.public_client_policyrow (GatewayDiscoveryController.java). A connector or managed resource that is exposed over MCP adds its scopes there. A REST resource with no tool route adds none. With the resources from Register a managed resource, our instance listedorderbook:readfrom the MCP resource and noordersscope from the REST one. It also listed the scopes of the connectors installed on it. -
The client fetches
/.well-known/oauth-authorization-server(RFC 8414) on the same origin. It names the endpoints,code_challenge_methods_supported: ["S256"], andregistration_endpoint.
Sign in
- The client registers itself at
/register(RFC 7591), or uses a client id metadata document. The metadata setsclient_id_metadata_document_supportedtotrue. - The client sends the user to
/authorizewith PKCE andcode_challenge_method=S256. The server refusesplain. Theresourceparameter is<environment URL>/gateway/mcp, which the server maps to the typeurn:mcp:server. - The client exchanges the code at
/tokenwith the code verifier and the sameresource.
Call tools
| Method | What the gateway does |
|---|---|
initialize | Checks only that a token is present. Answers with protocolVersion, capabilities: {"tools": {"listChanged": false}} and serverInfo. |
tools/list | Lists every tool of every enabled resource, plus the two policyarc__ tools. It does not filter the list by the token's scopes. For each MCP resource it fetches the live list, which calls /introspect. |
tools/call | Runs the full gateway check for that one tool. See How the managed gateway works. |
| Any other method | JSON-RPC error -32601, Method not found. |
On tools/call, an invalid or expired token is HTTP 401 with WWW-Authenticate: Bearer resource_metadata="...", error="invalid_token", so the client can refresh. A policy denial is a normal tool result with "isError": true and the text HTTP 403: Access denied.
A connected client is not told when the tool list changes, because listChanged is false. Reconnect it to see new tools.
Connect another account
We read this section in the code and did not run it. The bodies, the -32042 error and the /gateway/link refusal below are what the code builds, not responses we saw.
Some tools forward the user's own token from another identity provider, or a credential the user enters. If the user has not connected that account, or the stored token has expired and cannot be refreshed, the gateway returns a link instead of failing.
On /gateway/<name>/... the answer is HTTP 401 with this body.
{"error": "idp_link_required", "idp": "gitlab", "link_url": "https://your-environment.example/gateway/link?ls=..."}
On /gateway/mcp the answer depends on what the client declared in initialize.
- A client that declared
elicitation.urlgets the JSON-RPC error-32042. Itsdata.elicitations[0]holdsmode: "url",elicitationId,urlandmessage. - Any other client gets a tool result with
"isError": true, the link in the text, and_meta.idp_link_requiredwithidpandlink_url.
The link opens GET /gateway/link on the environment. The browser's PolicyArc session must belong to the same user the link was made for. With no session, the user signs in first. A session for a different user is refused with HTTP 403. After the user connects, the client retries the tool call.
For a tool that needs a credential the user enters, the error is user_secret_required, _meta holds user_secret_required, and the link opens the form at /gateway/connect.
Send feedback about this page