SEP-2643: Structured Authorization Denials#2643
Conversation
|
|
||
| - `reason` (string, REQUIRED). Indicates that the failure is an authorization denial and classifies its general nature. This SEP defines a single value, `insufficient_authorization`. Additional values MAY be defined by future SEPs. | ||
| - `authorizationContextId` (string, OPTIONAL). A server-issued correlation handle. When the server includes this field, the client MUST echo it on retry as described in "Retry Echo via `_meta`". The handle is not authorization material, and the server MUST NOT rely on it for authorization decisions when the credential presented on retry is sufficient on its own. | ||
| - `credentialDisposition` (string, OPTIONAL). Describes the lifecycle relationship between a credential newly issued through the remediation flow and any existing credential held by the client. Defined values: |
There was a problem hiding this comment.
I struggle with this part as it conflates classic OAuth roles, whereby the lifecycle of tokens it for the authorization server (AS) to say, and not in the resource server's (RS) domain.
This introduces undesirable coupling between AS and RS.
The MCP Server might accept tokens from several authorization servers, to properly serve this response attribute it would need to maintain metadata pertaining to how each of the authorization servers handles the remediation.
Furthermore this metadata even if managed, may change over time.
AS and RS might be from different providers, and there are no established mechanisms to communicate this added metadata.
I see it simpler, remove credentialDisposition and:
- Client should decide whether it wants to request a separate, limited-use credential for remediation purpose only, or make a request "upgrading" to a multi-purpose credential, combining the remediation RAR and scopes previously granted.
- Client should then assess credential response's expires_in attribute, to decide if previously existing creedntials should be retained.
- Note - OAuth has no normative "single-use semantics" for access tokens. Each grant is independent and bears no normative outcome on results of previously completed grants.
There was a problem hiding this comment.
Agreed that the tight coupling of token semantics to resource is not great.
Client should then assess credential response's expires_in attribute, to decide if previously existing creedntials should be retained.
This works with scope semantics where access tokens can be compared against each other directly, but not with RAR semantics. If clients are dealing with many resources that require RAR semantics, they may end up dealing with many tokens that exist at the same time. We need to define a strategy for how clients can manage multiple tokens and select the appropriate one to use for each request.
There was a problem hiding this comment.
The rar metadata draft offers resource server rar semantics to help clients navigate which token satisfies which resource:
https://www.ietf.org/archive/id/draft-zehavi-oauth-rar-metadata-02.html#name-oauth-20-protected-resource
According to the proposal, RFC 9728 resource server metadata response shall include authorization_details_types_supported which guides clients on choosing the right token (compare required RAR types to those granted to tokens they have).
Also worth mentioning that the challenge is simplified in case short lived tokens are used by authorization servers for high-risk RAR use cases, as clients will prune expired tokens.
There was a problem hiding this comment.
authorization_details_types_supported is a good start, but how can clients make this determination when clients have multiple tokens with the same type?
For example, say that a client is involved in two transactions simultaneously. THe client has one token containing the following details object:
{
"type": "payment_initiation",
"instructedAmount": { "currency": "EUR", "amount": "100.00" },
"creditorAccount": { "iban": "AAAAAAAAAAA"}
}
And another token containing the following details object:
{
"type": "payment_initiation",
"instructedAmount": { "currency": "GBR", "amount": "50.00" },
"creditorAccount": { "iban": "BBBBBBBBBBBB"}
}
How can the client choose which token to send for each request in a deterministic and efficient manner?
authorization_details_types describes a class of token, but the client needs to determine the correct instance of token.
There was a problem hiding this comment.
Thanks @yaron-zehavi and @max-stytch — both points well taken. What you are collectively pointing at is that MCP clients MAY need a deterministic way to manage multiple credentials, and UC3 as currently described in this SEP, especially the credentialDisposition: additional framing tied to client-side single-use semantics, is not the right design for that. I see @max-stytch has posted a doc on multi-token management for RAR to start the discussion. So I think once there is convergence on the problem and design then we can decide what UC3 should look like in this SEP and update accordingly
There was a problem hiding this comment.
The challenge of client selecting the right token among several options is generic and not specific to MCP.
And I think the challenges it involves, including @max-stytch 's example are navigable without requiring new normative language and without requiring clients to understand RAR object structure:
Clients of Authorization Servers that issue discrete separate short lived RAR tokens, simplify client token selection, because operations permitted by said tokens are handled one at a time in a short time frame, and said RAR tokens quickly expire: client doesn't end up with 2 payment tokens in parallel.
Clients of Authorization Servers that issue long lived RAR tokens, e.g to represent a permission policy, can:
- Request to incrementally add additionally required RAR objects and Scopes to a single credential -> resulting in one token, no confusion what to choose.
- If client has multiple credentials, choose based on RAR type satisfying resource's requirements.
- If duplicates exist, try using all existing tokens.
- If none work, request new token
There was a problem hiding this comment.
@yaron-zehavi I think I follow, but I still struggle to see what guidance we must give to MCP client developers.
Stepping back, if we introduce the idea in this SEP that the client MAY have to keep track of multiple tokens, then we MUST provide deterministic guidance on how to do it. (Notably the baseline today is that MCP clients are not expected to handle more than 1 access token.)
There was a problem hiding this comment.
I see @max-stytch is already ahead of me in digging into that problem 🙂 thanks!
| } | ||
| ``` | ||
|
|
||
| ### Use Case 3 — Additional short-lived credential |
There was a problem hiding this comment.
If you accept my previous comment, UC3 can be removed completely as in general an MCP server should examine the remediating credential based on it containing the missing scope / authorization details, being not expired and not previously used.
An MCP server should in general not care about the lifetime of the credential in taking authorization decisions (except as mentioned alredy enforce that it's not expired).
|
|
||
| ## Motivation | ||
|
|
||
| 1. **Stdio has no defined authorization challenge mechanism**: The MCP specification defines authorization procedures for the HTTP transport, including OAuth 2.1 flows, `WWW-Authenticate` challenges, and Protected Resource Metadata discovery. These mechanisms explicitly exclude stdio, which the specification directs to rely on credentials retrieved from the environment instead. As a result, a stdio MCP server that needs to reject a request on authorization grounds has no standard mechanism to communicate that the failure is an authorization problem, to indicate the nature of the required remediation, or to carry structured data the client can act on. |
There was a problem hiding this comment.
Additionally, current guidance is that STDIO servers that require OAuth remediation steps must handle the OAuth flow themselves and act as the OAuth client. This obscures the identity of the MCP Client from other participants in the system.
There was a problem hiding this comment.
Agreed. Will add this to Motivation#1
There was a problem hiding this comment.
I agree, although I do want to make sure we don't venture into trying to solve auth for stdio.
|
|
||
| 1. **Stdio has no defined authorization challenge mechanism**: The MCP specification defines authorization procedures for the HTTP transport, including OAuth 2.1 flows, `WWW-Authenticate` challenges, and Protected Resource Metadata discovery. These mechanisms explicitly exclude stdio, which the specification directs to rely on credentials retrieved from the environment instead. As a result, a stdio MCP server that needs to reject a request on authorization grounds has no standard mechanism to communicate that the failure is an authorization problem, to indicate the nature of the required remediation, or to carry structured data the client can act on. | ||
|
|
||
| 2. **Support for signaling server-side state remediation**: Some authorization denials are not about the client's credential. The server may deny a request because user approval, resource selection, or another server-side state must be established through direct user interaction. OAuth authorization challenges such as `insufficient_scope` or `invalid_token` are shaped around credential changes and do not cover this case. MCP has URL-mode elicitation as a primitive for out-of-band user interaction, but it is not itself an authorization denial signaling mechanism. |
There was a problem hiding this comment.
I want to echo Yaron's point below about conflating of OAuth roles - as this reads, it indicates that the MCP Server sometimes requires explicit user consent. However, the collection and management of user consent is in the domain of the AS. Suggest leading with a different example which is not as clearly within the domain of the AS.
Perhaps insufficient information to contact upstream systems, e.g. needing to collect credit card details?
There was a problem hiding this comment.
Fair enough. I propose changing
Current wording:
Some authorization denials are not about the client's credential. The server may deny a request because user approval, resource selection, or another server-side state must be established through direct user interaction.
To Updated Wording:
Some authorization denials are not about the client's credential. The server may deny a request because information needed to contact upstream systems (for example, payment details collected from the user), resource selection, or another piece of server-side state must be established through direct user interaction.
Does that address the concern?
There was a problem hiding this comment.
In general I would suggest being very explicit about MCP server in this doc rather than just saying server. Perhaps a further updated wording could be:
Some authorization denials are not about the client's credential. The MCP server may deny a request because it has insufficient information to contact an external system, needs confirmation from the user, or another piece of server-side state must be established before the request can succeed.
|
|
||
| - `reason` (string, REQUIRED). Indicates that the failure is an authorization denial and classifies its general nature. This SEP defines a single value, `insufficient_authorization`. Additional values MAY be defined by future SEPs. | ||
| - `authorizationContextId` (string, OPTIONAL). A server-issued correlation handle. When the server includes this field, the client MUST echo it on retry as described in "Retry Echo via `_meta`". The handle is not authorization material, and the server MUST NOT rely on it for authorization decisions when the credential presented on retry is sufficient on its own. | ||
| - `credentialDisposition` (string, OPTIONAL). Describes the lifecycle relationship between a credential newly issued through the remediation flow and any existing credential held by the client. Defined values: |
There was a problem hiding this comment.
Agreed that the tight coupling of token semantics to resource is not great.
Client should then assess credential response's expires_in attribute, to decide if previously existing creedntials should be retained.
This works with scope semantics where access tokens can be compared against each other directly, but not with RAR semantics. If clients are dealing with many resources that require RAR semantics, they may end up dealing with many tokens that exist at the same time. We need to define a strategy for how clients can manage multiple tokens and select the appropriate one to use for each request.
| - `authorizationContextId` (string, OPTIONAL). A server-issued correlation handle. When the server includes this field, the client MUST echo it on retry as described in "Retry Echo via `_meta`". The handle is not authorization material, and the server MUST NOT rely on it for authorization decisions when the credential presented on retry is sufficient on its own. | ||
| - `credentialDisposition` (string, OPTIONAL). Describes the lifecycle relationship between a credential newly issued through the remediation flow and any existing credential held by the client. Defined values: | ||
| - `replacement` (default). The newly-issued credential supersedes any existing credential for the same grant context. This matches current OAuth behavior and is the implicit default when `credentialDisposition` is absent. | ||
| - `additional`. The newly-issued credential coexists with the existing credential. The client MUST retain the existing credential for its original purpose. The newly-issued credential's usable scope is determined by its grant, and the client selects the appropriate credential per request. |
There was a problem hiding this comment.
the client selects the appropriate credential per request.
We need a well-defined strategy for this. I will try to jot down some notes and examples later, but I wonder if the client can
- Try a request with its "base credential"
- Look at the WWW-Authenticate response
- Attempt to match the challenge to a "specific credential" within its bag of tokens
- Retry with the specific credential
- Attempt to get a new credential only on failure
There was a problem hiding this comment.
I'd add that in step 1 client can consult (rar-metadata draft's proposed edition to) resource's metadata endpoint for authorization_details_types_supported to guide choosing a satisfying credential.
Cllients that did so can skip to step 5 and request a new credential using provided remediationHints.
There was a problem hiding this comment.
Yes, short-circuiting is definitely possible.
For those not in the discord- We started discussing credential selection strategies / schemes in https://docs.google.com/document/d/1X4q8qVazt2MIuLs2kMpAqJn5rlyYnBGk0FLw2a9D-yU/edit?tab=t.0#heading=h.fpw5paiaqpja
There was a problem hiding this comment.
As this is a challenge faced successfully by clients outside MCP, I'm not sure a new normative language is required.
However, certainly outlining strategies using current and upcoming metadata is helpful.
| - `replacement` (default). The newly-issued credential supersedes any existing credential for the same grant context. This matches current OAuth behavior and is the implicit default when `credentialDisposition` is absent. | ||
| - `additional`. The newly-issued credential coexists with the existing credential. The client MUST retain the existing credential for its original purpose. The newly-issued credential's usable scope is determined by its grant, and the client selects the appropriate credential per request. | ||
|
|
||
| The lifetime of an additional credential is governed by its OAuth `expires_in` value. Client-side management of additional credentials (storage, selection per request, cleanup after expiry) is an implementation concern outside the scope of this SEP. Servers that require single-use semantics for an additional credential SHOULD enforce single-use at the resource server. |
There was a problem hiding this comment.
I don't think single use semantics should be modeled as an inherent property of the credential that need to be enforced. Instead, they are a property of the resource that the credential has the ability to act on. For example:
- A client wants to transfer money from account A to account B, and tries to do so with params
{ from, to, amount } - The server binds the params to an opaque
transfer_idand returns thattransfer_idwithin the RAR payload - The AS authorizes the client with a token scoped to that
transfer_id - The client retries the original request with the token, which succeeds
The single-use semantics are a property of the Transfer resource itself - once the transaction is complete the access token is useless because it has no more resources to operate on, not because it has been used.
This means that an access token can be used across:
- Multiple related tools that operate on the same transfer (or other short-lived object)
- Multiple tool calls that fail due to network issues, intermittent issues, etc.
There was a problem hiding this comment.
I think this makes assumptions that don't always hold true:
- Server's might avoid using a unique operation_id inside RAR object and instead enforce single-use by caching a used access token until its expiry
- Some servers may allow using said access token on several tools, some only on one
There was a problem hiding this comment.
Reflecting the "we must provide guidance to MCP client developers" point above, I don't think we should say here that e.g. token selection per request is a concern outside the scope of this SEP.
|
|
||
| Clients MUST ignore remediation hints whose `type` they do not recognize. | ||
|
|
||
| The envelope is additive. When a transport-level authorization challenge is present, for example an HTTP `WWW-Authenticate` header, that challenge remains authoritative for driving the client's authorization flow. The envelope provides a transport-agnostic failure classification and, where applicable, structured data that the transport challenge cannot easily express. For stdio, where the MCP specification defines no transport-level authorization challenge mechanism, the envelope is the sole authorization denial signal. |
There was a problem hiding this comment.
What happens when the envelope and the header disagree with each other? (for example, a scope mismatch)
There was a problem hiding this comment.
See line 152, such disagreement imo is avoided:
"When the challenge fully describes the required remediation (for example insufficient_scope with the required scopes), the envelope functions as pure classification metadata carrying reason and an optional authorizationContextId. "
|
|
||
| ### Retry Echo via `_meta` | ||
|
|
||
| When a server includes an `authorizationContextId` in the authorization denial envelope, the client MUST echo the value, verbatim, in the `_meta` block of the retry request under the key `io.modelcontextprotocol/authorization-context-id`. |
There was a problem hiding this comment.
Since this is an official SEP and not an extension, do we need to use the _meta field?
There was a problem hiding this comment.
My reasoning was the following:
- The echo is conditional. Clients MUST echo authorizationContextId only when the server issued one.
- Didn't want to expand the core envelope on every SEP — _meta absorbs conditional fields without changing top-level shape.
- As per my read of https://modelcontextprotocol.io/specification/2025-11-25/basic#_meta - it's for both first-party and 3rd-party. The difference is via the prefix
io.modelcontextprotocol/namespace.
But happy to hear what others think
|
|
||
| ```json | ||
| HTTP/1.1 403 Forbidden | ||
| WWW-Authenticate: Bearer error="insufficient_authorization_details", |
There was a problem hiding this comment.
@yaron-zehavi this is a really good example of where passing authorization_details in response body as described in the base draft doesn't work well. Individual ecosystems rely on the response body for their own unique error messages.
Being able to pass authorization details (or a reference to the details - GNAP style) in the WWW-auth header ensures the response body is free for application and ecosystem specific logic.
There was a problem hiding this comment.
I think it is the accepted convention that authentication & authorization error handling MAY trump ecosystem application standards.
So an HTTP 401 is usually going to return (following RFC 7807) something like:
HTTP 401 Unauthorized
WWW-Authenticate: Bearer error="invalid_token"
Content-Type: application/problem+json
{
"type": "https://example.com/errors/invalid-token",
"title": "Invalid access token",
"status": 401,
"detail": "The token has expired"
}
... And not something very different according to the ecosystem.
There was a problem hiding this comment.
In addition to what @yaron-zehavi says, note that a JSON-RPC 2.0 error is a fixed envelope (code, message, data), and we are placing the denial under a single named key, data.authorization. Servers are free to populate other keys in data for their own information.
A couple of reasons where the body helps in our context:
- Encoding practicality and header size. Realistic authorization_details arrays for cases like payment_initiation may run into header size limits. JSON in a body avoids that.
- Uniform envelope mechanism across transports. For a stdio MCP server to bubble up the denial to its MCP client (the pattern you mention in your Motivation#1 comment), the JSON-RPC body is the right carrier. keeps one parsing model across both transports.
NOTE -
- When
oauth_authorization_detailsis present, a recognizing client MAY use the array directly to construct its next authorization request, saving a metadata-discovery round trip. The SEP already requires AS and RS to validate, so the hint is not a trust boundary in either direction. The hint is opt-in. A server that prefers to keep the requirements out of band — sensitivity, integrity concerns, payload size — can omit it and leave the client on the WWW-Authenticate + resource-metadata path. - The SEP doesn't preclude future header-reference designs (such as the resource-set registration reference under discussion in Security considerations for client manipulation of authorization details yaron-zehavi/oauth-rich-authorization-requests-metadata#4, which I believe is what you are referring to, @max-stytch ).
|
|
||
| Remediation hints describe what a client should do in response to a denial. They MUST NOT carry information that would itself require authorization to read, such as the identity of another user whose approval would be required or the contents of a resource the client cannot access. | ||
|
|
||
| ### Client-side modification of `authorization_details` |
There was a problem hiding this comment.
There was a problem hiding this comment.
I think this merits a separate issue, see:
yaron-zehavi/oauth-rich-authorization-requests-metadata#5
|
|
||
| ### Authorization Denial Envelope | ||
|
|
||
| An MCP server that denies a JSON-RPC request on authorization grounds MAY return a JSON-RPC error whose `code` is `<AUTHORIZATION_DENIAL_TBD>` (integer value to be assigned, see Open Questions) and whose `data` contains an `authorization` object populated as defined in this section. The envelope is transport-agnostic and applies to both HTTP and stdio transports. |
There was a problem hiding this comment.
There may be more transports than just 2 in the future!
| An MCP server that denies a JSON-RPC request on authorization grounds MAY return a JSON-RPC error whose `code` is `<AUTHORIZATION_DENIAL_TBD>` (integer value to be assigned, see Open Questions) and whose `data` contains an `authorization` object populated as defined in this section. The envelope is transport-agnostic and applies to both HTTP and stdio transports. | |
| An MCP server that denies a JSON-RPC request on authorization grounds MAY return a JSON-RPC error whose `code` is `<AUTHORIZATION_DENIAL_TBD>` (integer value to be assigned, see Open Questions) and whose `data` contains an `authorization` object populated as defined in this section. The envelope is transport-agnostic. |
|
|
||
| An MCP server that denies a JSON-RPC request on authorization grounds MAY return a JSON-RPC error whose `code` is `<AUTHORIZATION_DENIAL_TBD>` (integer value to be assigned, see Open Questions) and whose `data` contains an `authorization` object populated as defined in this section. The envelope is transport-agnostic and applies to both HTTP and stdio transports. | ||
|
|
||
| The conceptual shape is: |
There was a problem hiding this comment.
| The conceptual shape is: | |
| The shape of the payload is: |
| } | ||
| ``` | ||
|
|
||
| Authorization of the retry is determined by the credential presented with the request, and the server MUST NOT reject a retry solely because the handle is absent or cannot be resolved. |
There was a problem hiding this comment.
If the server MUST NOT reject, then it is effectively optional. In that case, what server behavior does it drive?
|
|
||
| This use case covers denials where the client's credential is valid and does not need to change. Remediation requires an out-of-band user interaction at a URL, such as approving access or selecting resources, that changes server-side state. A representative example is a file picker in a cloud storage service, where the user's OAuth token is unchanged and the server records an approval tied to the user. | ||
|
|
||
| This use case composes with the MCP URL elicitation error (`URLElicitationRequiredError`, JSON-RPC error code `-32042`). The envelope MUST include a `remediationHints` entry of type `url`, and the URL elicitation remains in `data.elicitations`. |
There was a problem hiding this comment.
| This use case composes with the MCP URL elicitation error (`URLElicitationRequiredError`, JSON-RPC error code `-32042`). The envelope MUST include a `remediationHints` entry of type `url`, and the URL elicitation remains in `data.elicitations`. | |
| This use case composes with the MCP URL elicitation error (`URLElicitationRequiredError`, JSON-RPC error code `-32042`). The envelope MUST include a `remediationHints` entry of type `url`, and the URL elicitation remains in `data.elicitations` for backwards compatibility. |
|
|
||
| This use case covers denials where the client's existing credential carries insufficient authorization for the requested operation and remediation requires obtaining a new credential. The replacement may involve additional OAuth scopes, a Rich Authorization Requests `authorization_details` object (RFC 9396), or any other mechanism that yields a new credential. In contrast with Use Case 1, the failure cannot be resolved by server-side state changes alone. | ||
|
|
||
| On the HTTP transport, the `WWW-Authenticate` challenge remains authoritative for driving the client's reauthorization. When the challenge fully describes the required remediation (for example `insufficient_scope` with the required scopes), the envelope functions as pure classification metadata carrying `reason` and an optional `authorizationContextId`. When the required authorization cannot be fully described at the transport layer, the envelope MAY carry a structured remediation hint as described in Example 2 below. |
There was a problem hiding this comment.
I agree with this, but I wonder if we can make the relationship between the payload and the transport clearer here. I think the points we want to communicate are:
- The envelope always carries complementary information (reason, context ID)
- The envelope MAY carry a remediation hint
- If the transport declares a remediation, it is authoritative
Are there any nuances I missed in those bullets?
|
|
||
| #### Example 2 — Rich Authorization Requests remediation | ||
|
|
||
| When a denied request requires authorization beyond what OAuth scopes can express, the `WWW-Authenticate` challenge can signal that authorization is insufficient but cannot carry the structured requirements themselves. The server MAY supply those requirements inside the envelope so the client can construct its next authorization request directly, without navigating the metadata discovery chain. This approach mirrors the error-signaling pattern defined in [OAuth 2.0 RAR Metadata and Error Signaling](https://datatracker.ietf.org/doc/draft-zehavi-oauth-rar-metadata/02/) at the JSON-RPC layer. |
There was a problem hiding this comment.
| When a denied request requires authorization beyond what OAuth scopes can express, the `WWW-Authenticate` challenge can signal that authorization is insufficient but cannot carry the structured requirements themselves. The server MAY supply those requirements inside the envelope so the client can construct its next authorization request directly, without navigating the metadata discovery chain. This approach mirrors the error-signaling pattern defined in [OAuth 2.0 RAR Metadata and Error Signaling](https://datatracker.ietf.org/doc/draft-zehavi-oauth-rar-metadata/02/) at the JSON-RPC layer. | |
| When a denied request requires authorization beyond what OAuth scopes can express, the `WWW-Authenticate` challenge can signal that authorization is insufficient but cannot carry the structured requirements themselves. The server MAY supply those requirements inside the envelope so the client can construct its next authorization request directly, without additional metadata discovery. This approach mirrors the error-signaling pattern defined in [OAuth 2.0 RAR Metadata and Error Signaling](https://datatracker.ietf.org/doc/draft-zehavi-oauth-rar-metadata/02/) at the JSON-RPC layer. |
There was a problem hiding this comment.
Perhaps "without requiring additional metadata discovery."
|
|
||
| When a denied request requires authorization beyond what OAuth scopes can express, the `WWW-Authenticate` challenge can signal that authorization is insufficient but cannot carry the structured requirements themselves. The server MAY supply those requirements inside the envelope so the client can construct its next authorization request directly, without navigating the metadata discovery chain. This approach mirrors the error-signaling pattern defined in [OAuth 2.0 RAR Metadata and Error Signaling](https://datatracker.ietf.org/doc/draft-zehavi-oauth-rar-metadata/02/) at the JSON-RPC layer. | ||
|
|
||
| An HTTP MCP server that denies a request because the access token lacks sufficient authorization details MAY return a `remediationHints` entry of type `oauth_authorization_details`. The entry's `authorization_details` member, when present, SHALL be an OAuth Rich Authorization Requests `authorization_details` array as defined in RFC 9396. The server MUST still return an HTTP authorization challenge via `WWW-Authenticate`, which remains authoritative for remediation and discovery. The JSON-RPC remediation hint is supplemental and exists to carry an actionable `authorization_details` object inside the response body. |
There was a problem hiding this comment.
I think this guidance is broader than just HTTP - any MCP server that denies a request could return oauth_authorization_details, right?
| ```json | ||
| HTTP/1.1 403 Forbidden | ||
| WWW-Authenticate: Bearer error="insufficient_authorization_details", | ||
| resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource/payments", |
There was a problem hiding this comment.
Is a different resource_metadata URL required, or just a non-normative example here?
There was a problem hiding this comment.
The idea is that MCP Server would likely require different RAR object types per resource.
Therefore the resource_metadata url will match the resource (e.g /payments in the example here).
resource_metadata url's response would indicate the required RAR types, e.g:
"authorization_details_types_supported": {
"oneOf": ["payment_initiation", "payment_approval",
"beneficiary_designation"]
See in my draft:
https://www.ietf.org/archive/id/draft-zehavi-oauth-rar-metadata-02.html#name-oauth-20-protected-resource
There was a problem hiding this comment.
Makes sense, but are we saying here that Structured Authorization Denials requires this idea, or that it is merely supported? (Normative vs. non-normative)
| } | ||
| ``` | ||
|
|
||
| The client uses the `authorization_details` from the remediation hint to construct an OAuth authorization request: |
There was a problem hiding this comment.
Can you add specific client guidance (or reference an RFC) on how to do this?
There was a problem hiding this comment.
| "currency": "EUR", | ||
| "creditorName": "Merchant A", | ||
| "creditorAccount": "DE02100100109307118603", | ||
| "remittanceInformation": "Ref Number Merchant" |
There was a problem hiding this comment.
nit: This was called remittanceInformationUnstructured in the authorization_details block
Motivation and Context
This SEP defines a transport-agnostic JSON-RPC authorization denial envelope for the Model Context Protocol. The envelope complements transport-level authorization challenges (HTTP WWW-Authenticate with Protected Resource Metadata), carrying failure classification, a retry correlation handle, and an extensible set of structured remediation hints for cases the transport cannot easily express. This SEP defines two initial remediation hint types and illustrates their use through three scenarios: URL-based approval composed with MCP URL-mode elicitation, credential replacement using scopes and OAuth Rich Authorization Requests (RFC 9396), and additional short-lived credentials for per-transaction operations like payment initiation in banking. Further remediation hint types can be defined in follow-on SEPs without changes to the envelope.
How Has This Been Tested?
This has been reviewed in the MCP Fine-Grained Authorization Working Group over multiple review cycles.
Discord: MCP FGA WG channel
Please see the Acknowledgments section of the SEP for reviewers.
Breaking Changes
This SEP is fully backward compatible. Existing OAuth clients and libraries do not need to change as the envelope is additive.
Types of changes
Checklist
Additional context
Status: Draft — awaiting sponsor. Will coordinate with FGA WG Lead to update this
AI Assistance Disclosure:: This SEP was written by the author with AI assistance for editorial review (grammar, structural arrangement etc.)