AI Agent Governance: Frameworks, Standards and Tools
AI agent governance covers the policies and identity controls that keep autonomous agents in check, built on NIST, OWASP, ISO and EU AI Act rules.
7 min read
MCP authentication secures MCP servers with OAuth 2.1: how the authorization flow works step by step, how to implement it, and the mistakes that leak tokens.
Astrix Security analyzed more than 5,200 open source MCP server implementations and found that 88% require some form of credential to run. Only 8.5% of them use OAuth. The rest lean on static API keys or personal access tokens sitting in an environment variable, the same pattern web APIs mostly abandoned a decade ago. The Model Context Protocol's own specification asks for something better, and getting MCP authentication right is now the difference between a server anyone can call and one that knows exactly which agent, on whose behalf, is making each request.
MCP authentication is the OAuth 2.1 based system the Model Context Protocol specification defines for HTTP-hosted servers: the MCP server acts only as an OAuth resource server, a separate authorization server issues short-lived bearer tokens through a PKCE-protected flow, and the client sends that token with every request. Local, stdio-based servers are explicitly excluded and use environment credentials instead.

Early MCP servers ran on the same machine as their client, talking over stdio, so there was nothing to authenticate. Once servers moved onto the network over Streamable HTTP, an MCP client became something that could request another user's data on a remote box, and the protocol needed a real delegated-authorization model instead of a shared secret. The specification answered that by making the MCP server an OAuth 2.1 resource server rather than its own identity provider, so it validates tokens issued elsewhere instead of managing logins itself.
The stakes are not hypothetical. In June 2025, researchers documented an incident nicknamed "NeighborJack," in which hundreds of MCP servers were found bound to 0.0.0.0 by default, reachable by any device on the same network with no authentication at all. In January 2026, three CVEs landed in Anthropic's own reference Git MCP server, the template many developers copy first: a path traversal bug, a file deletion bug, and a chain with a filesystem server that produced remote code execution.
MCP clients such as Claude Code already run this exact flow when they connect to a remote, authenticated MCP server, so most of the work described below sits on the server side.
When an unauthenticated request hits your MCP endpoint, the server responds with 401 Unauthorized and points the client at a Protected Resource Metadata (PRM) document, defined in RFC 9728, through the WWW-Authenticate header:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="mcp",
resource_metadata="https://your-server.com/.well-known/oauth-protected-resource"
The PRM document at that well-known path tells the client which authorization server issues tokens for this resource and which scopes exist:
{
"resource": "https://your-server.com/mcp",
"authorization_servers": ["https://auth.your-server.com"],
"scopes_supported": ["mcp:tools", "mcp:resources"]
}The client then queries that authorization server's own metadata endpoint, using OpenID Connect Discovery or OAuth 2.0 Authorization Server Metadata (RFC 8414), to learn the actual authorize, token and registration endpoints. This is a hard requirement, not a suggestion: the specification's June 2025 revision removed the old fallback of guessing at /authorize, /token and /register paths in favor of mandatory RFC 9728 discovery.
Before the authorization code flow starts, the client needs a client_id. MCP's own tutorial uses Dynamic Client Registration for this, sending a request like the following to the authorization server's registration endpoint:
{
"client_name": "My MCP Client",
"redirect_uris": ["http://localhost:3000/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"]
}The July 28, 2026 spec revision changed one thing here worth knowing: registered credentials are now bound to the specific authorization server that issued them. If a resource migrates to a different authorization server, the client has to register again rather than reusing the old client ID.
The client opens a browser to the authorization server's /authorize endpoint with a PKCE code challenge attached. The user logs in, grants the requested scopes, and the authorization server redirects back with a code the client exchanges for tokens:
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"refresh_token": "def502...",
"token_type": "Bearer",
"expires_in": 3600
}PKCE is mandatory because most MCP clients, CLIs and desktop apps included, are public clients that cannot keep a secret. The specification's Resource Indicators requirement (RFC 8707) adds another constraint: the client must include a resource parameter naming the exact MCP server the token is meant for, in both the authorization request and the token request, so a token minted for one server cannot be replayed against another.

The client attaches the access token as a bearer token on every call:
GET /mcp HTTP/1.1
Host: your-server.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...The server, acting purely as a resource server, has to check more than whether the token is valid: it must confirm the token was actually issued for it, using the aud claim, per RFC 8707 Section 2. In MCP's official TypeScript example, the token verifier pulls the audience list out of the introspection response and rejects the request if none of the audiences match the server's own canonical URL, rather than falling back to trusting the caller.
aud claim, will process a token that was never meant for it.iss parameter on authorization responses against RFC 9207 to block mix-up attacks, clients declare an OpenID Connect application_type of web or native at registration, registered client credentials are bound to the issuing authorization server, refresh tokens are requested through the standard offline_access scope, scope step-up requests union previously granted scopes with newly required ones, and /.well-known/oauth-authorization-server discovery behavior is clarified.xmcp.config.ts instead of hand-writing the discovery and metadata routes.No. The specification calls authorization "OPTIONAL," but HTTP-based transports "SHOULD" implement it. Servers using stdio transport should skip this flow entirely and pull credentials from the environment instead, since a local process already runs under the user's own permissions.
MCP's own specification is titled "Authorization" and focuses on OAuth 2.1 delegated access: proving a client is allowed to call a specific tool on behalf of a specific user. Authentication, confirming who that user or agent actually is, happens at the authorization server the MCP server delegates to, not inside the MCP server itself.
You can, and Astrix Security's research shows most public MCP servers still do. A static, long-lived key cannot be scoped per tool, cannot be revoked for one client without breaking every other client using it, and gives an attacker who steals it standing access until someone notices. The spec's OAuth 2.1 flow exists to replace that pattern with short-lived, audience-bound, per-client tokens instead.
No. Keycloak, open source and self-hosted, is what MCP's own tutorial uses for its walkthrough. Hosted options like WorkOS AuthKit or Descope's Agentic Identity Hub also implement the OAuth 2.1 roles the spec expects, including Protected Resource Metadata and Dynamic Client Registration, so the MCP server only has to validate tokens rather than issue them.
Developers comparing MCP servers that ship with authentication already built in can browse the current listings on the MCP servers hub.
One email a week. New agents, MCP servers and skills, and what is actually getting traction.
AI agent governance covers the policies and identity controls that keep autonomous agents in check, built on NIST, OWASP, ISO and EU AI Act rules.
7 min read
A practical guide to MCP server deployment: picking a transport, choosing a host, and adding auth, with real examples from Cloud Run, AWS, Cloudflare and Vercel.
8 min read

An MCP gateway sits between AI agents and MCP servers. Here are 8 options compared, from open source projects like Docker and Microsoft's to managed platforms like Composio and Kong.
9 min read