MCP Protocol Flows
How MCP clients discover, authenticate with, and interact with the gateway. This covers the protocol-level flows that any MCP client (Claude Code, custom agents, etc.) follows.
Discovery and authorization
MCP clients follow the standard OAuth 2.0 authorization code flow with PKCE. The gateway exposes discovery endpoints so clients can locate the AS automatically.
Step-by-step
1. Initial call — no token
The client sends a request to POST /mcp without a valid Authorization header. The gateway returns HTTP 401 with a WWW-Authenticate header:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://gateway.policyarc.com/.well-known/oauth-protected-resource", scope="drive:read drive:write jira:read"
2. Discover protected resource metadata (RFC 9728)
The client fetches GET /.well-known/oauth-protected-resource:
{
"resource": "https://gateway.policyarc.com",
"authorization_servers": ["https://as.policyarc.com"],
"scopes_supported": ["drive:read", "drive:write", "jira:read"],
"bearer_methods_supported": ["header"]
}
3. Discover AS metadata (RFC 8414)
The client fetches GET /.well-known/oauth-authorization-server. The gateway proxies this to the AS and returns the metadata document unchanged. This proxy exists because the MCP spec requires clients to discover AS metadata at the MCP server's own base URL.
4. Dynamic Client Registration (optional)
If the AS supports DCR (RFC 7591), the client can register itself using the registration_endpoint from the AS metadata.
5. Authorization code flow with PKCE
The client redirects the user to the AS authorization endpoint. The resource parameter must be the gateway's resource indicator so the AS issues an audience-restricted token:
GET /authorize?
response_type=code&
client_id=<client_id>&
redirect_uri=<redirect_uri>&
scope=drive:read+jira:read&
resource=urn:mcp:gateway&
code_challenge=<pkce_challenge>&
code_challenge_method=S256
6. Token exchange
The client exchanges the authorization code for an access token:
POST /token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code=<code>&
redirect_uri=<redirect_uri>&
client_id=<client_id>&
code_verifier=<pkce_verifier>&
resource=urn:mcp:gateway
7. Call the gateway
POST /mcp
Authorization: Bearer <access_token>
Content-Type: application/json
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"drive_list_files","arguments":{}}}
Step-up authorization
When a client receives a 403 with error="insufficient_scope", it should re-initiate the authorization flow requesting the additional scope:
HTTP/1.1 403 Forbidden
WWW-Authenticate: Bearer error="insufficient_scope",
resource_metadata="https://gateway.policyarc.com/.well-known/oauth-protected-resource",
scope="drive:write",
error_description="Scope drive:write required for drive_write_file"
The client then redirects the user to re-authorize with scope=drive:write resource=urn:mcp:gateway. Only the incremental scope is needed.
Resource indicators
The gateway's resource indicator is configured via resource_indicator in gateway.yaml (default: urn:mcp:gateway).
Resource indicators (RFC 8707) allow the AS to issue audience-restricted tokens. When the gateway calls /introspect, it passes the resource indicator as context:
{"resource": "urn:mcp:gateway", "scopes": ["drive:read"]}
This context is passed to OPA at evaluation time, allowing policy rules to enforce both resource binding and scope requirements.
Methods that skip introspection
These MCP methods bypass token validation:
| Method | Behavior |
|---|---|
initialize | Returns protocol version, capabilities |
notifications/initialized | No-op |
tools/list | Lists all configured tools |
resources/list | Lists fixed-URI resources |
resources/templates/list | Lists URI-template resources |
A Bearer token is still required in the Authorization header (401 if absent), but its validity is not checked for these methods.
WWW-Authenticate header behavior
The gateway constructs WWW-Authenticate headers per RFC 6750:
| Status | When | Header includes |
|---|---|---|
| 401 | No Authorization: Bearer header | resource_metadata URL + all configured scopes |
| 403 | Token valid but missing required scope | error="insufficient_scope" + the specific scope needed |