Enterprise-managed authorization and client credentials for MCP The Model Context Protocol (MCP) now has two official authorization extensions in its ext-auth repository: Enterprise-Managed Authorization (identifier io.modelcontextprotocol/enterprise-managed-authorization, stable as of October 2026) and OAuth Client Credentials (io.modelcontextprotocol/oauth-client-credentials, draft), covering centralized corporate identity-provider control and machine-to-machine access with no user present. In the enterprise flow the MCP client trades a corporate sign-in for an Identity Assertion JWT Authorization Grant (ID-JAG) at the company's identity provider — such as Okta, Microsoft Entra ID or a corporate SSO system — then exchanges the ID-JAG for an access token, so the user is never redirected to the MCP server's authorization server login page. The author reports that as of October 2026 the mainstream chat and IDE hosts list neither extension, and recommends a signed JWT assertion over a client secret for client credentials so the private key never leaves the service. Enterprise-managed authorization and client credentials for MCP Core Model Context Protocol MCP authorization pictures one person in a browser, approving one client for one server. That fits less well when a company’s information technology IT team wants to decide centrally who may use which server, or when the caller is a scheduled job with nobody there to click anything. Two official extensions cover those cases. This is part 28 of my series on what an MCP server does under the 2026-07-28 specification, after part 27 on Skills over MCP https://pournasserian.com/writing/mcp-2026-skills . It builds on part 18 https://pournasserian.com/writing/mcp-2026-oauth-resource-server for core authorization and part 23 https://pournasserian.com/writing/mcp-2026-extensions for how extensions work. The facts are as I read them in October 2026. In brief 1. Two official extensions cover what core authorization handles poorly. Enterprise-Managed Authorization stable puts MCP access under the company’s identity provider IdP ; OAuth Client Credentials draft serves callers with no user present. 2. In the enterprise flow, the user never sees your authorization server’s login page. The client trades a corporate sign-in for an Identity Assertion JSON Web Token JWT Authorization Grant ID-JAG at the IdP, then the ID-JAG for an access token. 3. For client credentials, prefer a signed JWT assertion to a client secret. The private key never leaves the service. 4. Support is early. As of October 2026, the mainstream chat and integrated development environment IDE hosts list neither extension. Which authorization model fits Both extensions live in the ext-auth repository https://github.com/modelcontextprotocol/ext-auth : | Extension | Identifier | Status, October 2026 | Solves | |---|---|---|---| | Enterprise-Managed Authorization https://modelcontextprotocol.io/extensions/auth/enterprise-managed-authorization | io.modelcontextprotocol/enterprise-managed-authorization | Stable | IT controls MCP access centrally through the corporate IdP, with no per-user, per-server consent | | OAuth Client Credentials https://modelcontextprotocol.io/extensions/auth/oauth-client-credentials | io.modelcontextprotocol/oauth-client-credentials | Draft | Machine-to-machine access with no user present | Both are optional and additive: they don’t change core behaviour, they compose with each other, and they’re versioned independently. Which one you need depends on who is calling. Core authorization uses a Proof Key for Code Exchange PKCE , and a local server on standard input and output stdio , as in part 7 https://pournasserian.com/writing/mcp-2026-transports , takes its credentials from the environment: Enterprise-managed authorization Letting each user authorize each server is right for consumers. In a company it causes friction and gaps: employees shouldn’t have to understand every server’s authorization details, security teams can’t enforce consistent policy, and onboarding and offboarding mean authorizing, then revoking, dozens of services by hand. The extension makes the organization’s IdP Okta, Microsoft Entra ID or a corporate single sign-on SSO system the authoritative decision-maker. Users sign in to the MCP client with corporate SSO. The client gets an ID-JAG from the IdP and exchanges it at your MCP server’s authorization server, which validates it against the IdP’s JSON Web Key Set JWKS and issues an access token. The user is never redirected to that authorization server’s login page. What the organization gets: - Central policy. The IdP holds the approved MCP servers and each one’s access rules. - One sign-in. No per-server consent prompts. - Enforcement before issuance. Users without access never receive a token. - Revocation in one place. Removing access at the IdP covers every client. - An audit trail for compliance. What each party does | Party | Duties | |---|---| | MCP client | Declare the extension on each request. Support SSO with the enterprise IdP and keep the identity assertion, an OpenID Connect OIDC ID token or a Security Assertion Markup Language SAML assertion. Exchange it for an ID-JAG, then redeem that at the MCP authorization server. Support IdP configuration at the organization level. Handle scope errors gracefully | | MCP server | Declare the extension in its authorization metadata to require the enterprise flow. Optionally publish a resource descriptor, so admins can configure policy in the IdP console | | MCP authorization server | Validate ID-JAGs: signature against the IdP’s JWKS, issuer, audience, expiry. Map claims to permissions. Link accounts using sub as the stable key, falling back to email for accounts created before enterprise management | In Python In the official Python software development kit SDK , mcp 2.3.0, IdentityAssertionOAuthProvider in mcp.client.auth.extensions.identity assertion covers the last exchange, presenting the ID-JAG with the JWT-bearer grant of Request for Comments RFC 7523. Getting the ID-JAG is left to you, through an assertion provider callback. It serves confidential clients only, so it needs a client secret. It also takes your authorization server’s issuer as configuration, so the MCP server can’t redirect the ID-JAG. OAuth client credentials Background services, scheduled jobs, continuous integration and delivery CI/CD pipelines, server-to-server integrations and daemons have no human present and can’t open a browser. This extension adds the OAuth 2.0 client credentials grant, so they authenticate with the application’s own credentials instead of a user’s. If a human should explicitly authorize access, use the standard flow. It allows two credential formats: | | JWT bearer assertion, RFC 7523 | Client secret | |---|---|---| | Secret on the wire | No, the private key stays local | Yes | | Lifetime | Short, minutes | Long-lived until rotated | | Risk if leaked | A limited window | Silent impersonation until rotation | | Guidance | Preferred | Store in a secrets manager, rotate regularly, scope minimally | - Clients declare the extension on each request, obtain a token before connecting, send it as a bearer token, and refresh it before expiry. These tokens often live shorter than user tokens. - Servers validate the JWT’s signature and claims against the authorization server’s JWKS, check scopes per operation and, as a recommendation, advertise the extension in server/discover https://pournasserian.com/writing/mcp-2026-server-discover . In Python mcp 2.3.0 has a provider for each format, in mcp.client.auth.extensions.client credentials : ClientCredentialsOAuthProvider for a client ID and secret, and PrivateKeyJWTOAuthProvider for a signed assertion the TypeScript SDK’s are ClientCredentialsProvider and PrivateKeyJwtProvider . Each plugs into httpx2 , the SDK’s Hypertext Transfer Protocol HTTP client. The assertion comes from your assertion provider callback: in production, the SDK says, usually from a workload identity provider. For testing and simple setups, SignedJWTParameters signs it with your private key, as here: python import httpx2 from mcp.client import Client, advertise from mcp.client.auth.extensions.client credentials import PrivateKeyJWTOAuthProvider, SignedJWTParameters, from mcp.client.streamable http import streamable http client from mcp.shared.auth import OAuthClientInformationFull, OAuthToken MCP URL = "https://mcp.example.com/mcp" CLIENT ID = "report-service" registered with the authorization server, with its public key class MemoryTokenStorage: """The SDK's TokenStorage protocol, kept in memory.""" def init self - None: self.tokens: OAuthToken | None = None self.client info: OAuthClientInformationFull | None = None async def get tokens self - OAuthToken | None: return self.tokens async def set tokens self, tokens: OAuthToken - None: self.tokens = tokens async def get client info self - OAuthClientInformationFull | None: return self.client info async def set client info self, client info: OAuthClientInformationFull - None: self.client info = client info def make provider private key pem: str - PrivateKeyJWTOAuthProvider: """The private key signs assertions here and never leaves this service.""" assertions = SignedJWTParameters issuer=CLIENT ID, subject=CLIENT ID, signing key=private key pem return PrivateKeyJWTOAuthProvider server url=MCP URL, storage=MemoryTokenStorage , client id=CLIENT ID, assertion provider=assertions.create assertion provider , issuer="https://auth.example.com", the only server an assertion is sent to async def nightly report http: httpx2.AsyncClient - str: """Runs with no user present; the provider gets the token.""" declared = advertise "io.modelcontextprotocol/oauth-client-credentials" async with Client streamable http client MCP URL, http client=http , extensions= declared as client: result = await client.call tool "list files", {} return result.content 0 .text I ran it with httpx2.AsyncClient auth=make provider key , in one process, against the SDK’s own MCP server, a stand-in authorization server and a throwaway key pair. The requests: php POST mcp.example.com/mcp - 401 GET mcp.example.com/.well-known/oauth-protected-resource/mcp - 200 GET auth.example.com/.well-known/oauth-authorization-server - 200 POST auth.example.com/token - 200 POST mcp.example.com/mcp - 200 POST mcp.example.com/mcp - 200 POST mcp.example.com/mcp - 200 What the token endpoint received: grant type: client credentials client assertion type: urn:ietf:params:oauth:client-assertion-type:jwt-bearer resource: https://mcp.example.com/mcp scope: files:read client secret sent: False assertion claims: {'iss': 'report-service', 'sub': 'report-service', 'aud': 'https://auth.example.com'} lifetime: 300 s What the run showed: - No secret on the wire. The JWT travels as a client assertion beside grant type=client credentials and lives 300 seconds, the SignedJWTParameters default. The scope came from the server’s Protected Resource Metadata. - One line declares the extension. advertise ... put it in the client capabilities of every request’s meta . - issuer= pins where credentials go. Without it, 2.3.0 warns that 3.0 will require it. - Tokens come after a 401. The first token came only after a 401 and a discovery of both metadata documents. When the two-second demo token expired, the next request went the same way. That isn’t yet what the extension asks of clients: a token before connecting, and a refresh before expiry. Support, and my advice for Entra ID The official client matrix https://modelcontextprotocol.io/extensions/client-matrix lists Enterprise-Managed Authorization in Archestra.AI and in Apify’s mcpc command-line client, and Client Credentials in mcpc. As of October 2026, the mainstream chat and IDE hosts don’t list either. Enterprise-Managed Authorization also needs support on the IdP’s side, so check with your IdP vendor. What follows is my advice, not part of either extension. If your MCP servers sit behind Entra ID in a large organization, keep Enterprise-Managed Authorization on your roadmap. It removes the per-user consent friction that Entra’s lack of dynamic registration otherwise forces you to work around part 19 https://pournasserian.com/writing/mcp-2026-oauth-step-up-and-entra covers what to expect from Entra today . Until hosts support it, pre-registered clients with core authorization, and Client Credentials for automation, are the practical options. Method and caveats - Built from my guide’s chapter on authorization extensions, against the 2026-07-28 specification; the status and support facts are as of October 2026. - The Python sample ran against mcp 2.3.0 on 9 October 2026, in one process with no network; the output is what it printed. - Where my notes were less precise about the Python SDK which classes it ships, and how its providers renew a token , this article follows the installed package.