Skip to main content
Version: Latest

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:

MethodBehavior
initializeReturns protocol version, capabilities
notifications/initializedNo-op
tools/listLists all configured tools
resources/listLists fixed-URI resources
resources/templates/listLists 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:

StatusWhenHeader includes
401No Authorization: Bearer headerresource_metadata URL + all configured scopes
403Token valid but missing required scopeerror="insufficient_scope" + the specific scope needed