Enterprise MCP Auth: Agents Finally Get Service Accounts
SEP-1046 gives unattended agents a real OAuth story on MCP: client credentials with private_key_jwt. What it fixes, what it leaves open, and how to roll it out.

MCP now has a specified way for an agent to authenticate without a human in the loop. SEP-1046 adds the OAuth client_credentials grant to the Model Context Protocol as an official extension, with private_key_jwt (RFC 7523 JWT client assertions) as the recommended client authentication method and plain client secrets allowed for compatibility. It was accepted in October 2025, and over the past months it has landed in the TypeScript, Python, and now Ruby SDKs. If you run agents in an enterprise, this is the piece of the puzzle you were waiting for, whether you knew its SEP number or not.
A confession before the substance: this post nearly did not get written. The agentic AI space moves fast enough that writing about it is a scheduling problem in its own right; SEP-1046 sat in my drafts folder while three other things shipped, and I only found the time now because Brad Webb's synopsis of enterprise MCP authorisation patterns landed in my reading queue and made the picture complete enough to be worth your time. Read his piece too. This one is my shorter, more opinionated take on what actually matters for enterprise teams.
How a GitHub issue becomes a security layer
On July 24, 2025, Darin McAdams opened issue #1046 on the MCP specification repo. The pedigree matters here: McAdams spent almost 26 years at Amazon, the last stretch as a Senior Principal Software Engineer, and wrote this proposal in his final weeks there before moving to identity security company Defakto Security as an Architect. The proposal is short and surgical: the original MCP authorization spec mentioned the client credentials flow, later revisions dropped it, and the spec ended up silent on the single most common enterprise scenario, a workload that needs to call an MCP server when no end user is present to click through an interactive consent screen.
The proposal was accepted as SEP-1046 and finalized on October 30, 2025, shipping as the MCP extension io.modelcontextprotocol/oauth-client-credentials rather than a change to the core spec. Then the implementations followed: PrivateKeyJwtProvider in the TypeScript SDK, PrivateKeyJWTOAuthProvider in the Python SDK, and most recently a commit by Koichi ITO (koic, of RuboCop fame) adding private_key_jwt support to the official Ruby SDK, complete with a conformance scenario. That progression, proposal to accepted SEP to independent implementations passing a shared conformance suite, is what a protocol growing up looks like. Two years ago "MCP auth" meant a bearer token in an environment variable and a prayer.
Why client credentials matter: agents do not have a browser
The OAuth authorization code flow, which the base MCP spec builds on, assumes a human. Someone sees a consent screen, authenticates against the identity provider, and delegates a slice of their authority to the client. That model fits Claude Desktop connecting to your CRM. It does not fit the workloads enterprises actually deploy: a nightly reconciliation agent, a CI pipeline that queries an internal MCP server, a supervisor agent fanning out to specialist agents at 3 a.m. Nobody is awake to click "Allow".
Client credentials is the service account pattern from every enterprise IAM playbook, applied to MCP. The workload has its own identity, registered ahead of time in your identity platform, and authenticates as itself. No borrowed user session, no headless browser automation pretending to be a person, no long-lived personal access token duct-taped into a secrets manager. The agent is a first-class principal with its own entitlements, its own audit trail, and its own lifecycle. If your security team has been blocking MCP rollouts because "the agents authenticate as Dave", this is the answer to give them.
private_key_jwt: the right default, and its cost
SEP-1046 deliberately constrains client authentication to two options. Client secrets over HTTP Basic are allowed, because every identity provider on earth supports them today. But the recommended method is private_key_jwt: the client signs a short-lived JWT assertion with a private key and presents that instead of a secret. The design details in the SDK implementations are worth reading, because they encode real security judgment:
- The assertion carries
issandsubset to the client ID,audset to the authorization server's issuer identifier, a 300-secondexp, and a uniquejti. Stolen assertions age out in five minutes and cannot be replayed. - The private key never leaves the client and is never written to token storage. There is no shared secret sitting in the authorization server's database waiting to be dumped.
- The
client_idis omitted from the request body entirely; identity travels inside the signed assertion. The token request also carries the RFC 8707resourceparameter, so the token that comes back is bound to one specific MCP server rather than usable against anything that accepts the issuer.
The cost is key management. Asymmetric keys need generation, distribution, rotation, and revocation, and SEP-1046 intentionally does not yet specify how a server discovers the client's JWKS to validate assertions; that waits on the WIMSE headless JWT work and the Client ID Metadata draft maturing at the IETF. In practice this means most teams will ship with client secrets first and migrate to JWT assertions as their identity platform tooling catches up. That is fine. Choosing the compatible option today with a specified upgrade path is a different thing from having no path at all.
One deliberate omission drew debate in the issue thread: mTLS. Several commenters wanted RFC 8705 client certificates in scope, and the pushback explains why they are not: in real deployments TLS terminates at the CDN or load balancer, several hops before the application layer where MCP actually runs, so the server never sees the client certificate without infrastructure gymnastics. Enterprises with a working internal PKI can still do mTLS between their own services; the spec just refuses to make everyone else carry that assumption.
The patterns that survive contact with an enterprise
Webb's article frames the decision better than any protocol comparison table: start from where the agent's authority originates, not from a menu of OAuth flows. Everything else falls out of that question. Three patterns cover almost every enterprise case:
- Delegated user authority for interactive tools: the human's entitlements flow to the agent through the authorization code flow. The trap here is token pass-through, forwarding the upstream identity provider's token straight to the MCP server. It works in a demo and couples your agent platform to a foreign token format and trust model in production. Exchange the token for a short-lived one minted for the specific MCP server, with audience validation enforced. The spec's RFC 8707 resource indicator requirement exists precisely to make generic-token laundering fail.
- Machine identity for unattended workloads: SEP-1046 client credentials, prefer
private_key_jwt, one registered client per agent workload, not one shared "agents" client for the whole platform. Blast radius follows registration granularity. - Dual identity for the interesting middle: an agent acting on behalf of a user but running unattended. The token should represent both the actor (the agent) and the subject (the user), which token exchange supports and most authorization servers now express. This is the pattern that keeps "which human is accountable for this action" answerable in an audit, which is the question your compliance team will actually ask.
On registration itself: SEP-991 made OAuth Client ID Metadata Documents the preferred pattern for clients and servers with no pre-existing relationship, an HTTPS URL as a client identifier. Elegant for the open ecosystem; for a closed enterprise, pre-registration through your corporate identity platform remains the stronger default, because it gives you ownership, allow-listing, and lifecycle control, and it removes an entire class of metadata-fetch SSRF concerns from your threat model.
SEP-1046 is one lane, not the whole road
Spend an hour in the spec repo's auth label and the single-SEP framing falls apart: there is a standing auth working group with a meeting cadence, and a queue of proposals that sketches where enterprise MCP authorization goes next. SEP-1488 adds securitySchemes to tool metadata for mixed-auth servers, one server exposing public tools next to OAuth-gated ones. SEP-1489 standardizes the error a tool returns to trigger an OAuth flow mid-session, which is step-up authorization in agent terms. SEP-2742 wants remote servers to declare their authentication methods up front, so a client knows what it is walking into before connecting. HTTP message signing is back as PR #2752 after SEP-1415 went dormant. And the hardening keeps landing between the headline SEPs: mandatory RFC 9207 issuer validation against mix-up attacks (#1721), Rich Authorization Requests per RFC 9396 (#1670), refresh token forwarding for offline access (#1735), and an open effort to slim the authorization spec by delegating to the OAuth RFCs it builds on (#3054). If you own agent security in an enterprise, that issue queue is your roadmap. Read it the way you read your own backlog.
What to do with this on Monday
Concretely, for a platform or security team rolling out MCP inside an organization:
- Inventory every MCP integration and classify it: interactive, unattended, or acting-on-behalf-of. The flow follows the class, never the other way around.
- Give every unattended agent its own registered client in your identity provider. Start with client secrets if your tooling demands it, but put
private_key_jwton the roadmap now and check whether your SDK already supports it. TypeScript, Python, and Ruby do. - Enforce audience validation on every MCP server you operate. A token minted for one server must fail on another. This is the cheapest control in the whole stack and the most commonly skipped.
- Ban token pass-through in code review, the same way you ban string-built SQL. It is the SQL injection of agent identity: convenient, everywhere, and wrong.
- Keep agent permissions read-only by default and gate every mutating path, the same posture I argued for at the infrastructure layer in the Agents on Call gateway post. Authentication tells you who the agent is; it says nothing about what it should be allowed to do.
The larger point: every month, another piece of "enterprises cannot adopt agents because X" quietly gets resolved in a GitHub issue somewhere. Auth was the biggest X. A year ago the honest answer to "how do our unattended agents authenticate to MCP servers" was a shrug. Now it is a SEP number, two client authentication methods, and conformance tests in three SDKs. We are all learning what we can build, and how, a little more every week. The teams that track these seams as they close will ship a year ahead of the teams waiting for a finished textbook, because there will not be one.
Read this next
Identity is only half of agent security. IAM for LLM Apps: Least Privilege When the Caller Is a Model covers the authorization half on AWS. On the sister site, Agent Toolkit for AWS: The Docs Have a New Reader looks at what happens when the agent consuming your infrastructure is also the one reading the manual, and ercanermis.com has the full map of what I write where.
References
- SEP-1046: Support OAuth client credentials flow in authorization (modelcontextprotocol#1046)
- PR #1047: SEP-1046 and SEP-1502, client credentials flow via the MCP extension mechanism
- modelcontextprotocol/ext-auth: the oauth-client-credentials extension
- Ruby SDK commit 4327fc8: private_key_jwt client authentication (Koichi ITO)
- TypeScript SDK #1157: PrivateKeyJwtProvider
- Python SDK #1663: PrivateKeyJWTOAuthProvider
- MCP specification: Authorization
- RFC 7523: JWT Profile for OAuth 2.0 Client Authentication and Authorization Grants
- RFC 8707: Resource Indicators for OAuth 2.0
- Brad Webb: Enterprise MCP authorisation patterns, a detailed synopsis
- MCP spec repo: the auth label (working group notes and open SEPs)
More from Ercan
Two more sites, same author, different ground.
Cloud, AWS, EKS, Terraform, platform engineering.
Field notes from production systems. EKS, IAM, Terraform at organization scale, observability, cost optimization.
Visit ercan.cloud →The hub. About, consulting, contact.
Personal hub for both writing tracks. Who I am, how the consulting works, how to reach me.
Visit ercanermis.com →