Skip to content

oauthMetadataResponse requires a full AS metadata document to read one field from it #2623

Description

@quotentiroler

What

AuthMetadataOptions.oauthMetadata is a required full RFC 8414 document, but buildOAuthProtectedResourceMetadata reads exactly one field from it:

// packages/server/src/server/middleware/oauthMetadata.ts
export function buildOAuthProtectedResourceMetadata(options: AuthMetadataOptions): OAuthProtectedResourceMetadata {
    checkIssuerUrl(new URL(options.oauthMetadata.issuer), options.dangerouslyAllowInsecureIssuerUrl);
    return {
        resource: options.resourceServerUrl.href,
        authorization_servers: [options.oauthMetadata.issuer],
        ...
    };
}

authorization_endpoint, token_endpoint, response_types_supported and the rest are required by the type and never read when building the RFC 9728 document. They matter only for serving the AS document back verbatim on /.well-known/oauth-authorization-server, which the docstring describes as a convenience "so legacy clients that probe the resource origin still discover the AS".

So a server that wants only the protected-resource document, the one the MCP specification mandates, must supply an entire AS metadata document to obtain a single issuer string.

Why an issuer alone should be enough

The MCP specification 2026-07-28, Authorization Server Discovery puts the resource server's obligation at publishing issuers:

MCP servers MUST implement … Protected Resource Metadata (RFC9728) … The Protected Resource Metadata document returned by the MCP server MUST include the authorization_servers field containing at least one authorization server.

and puts AS metadata retrieval on the client, against the AS:

MCP clients MUST attempt multiple well-known endpoints when discovering authorization server metadata.

Its sequence diagram shows the same split. The client reads resource metadata from the MCP server, then goes to the authorization server for AS metadata. A conforming resource server can therefore be configured with issuer URLs alone and never hold an RFC 8414 document.

Where we hit it

@maxhealth.tech/mcp-http is an OAuth, CORS and observability layer over @modelcontextprotocol/server. It is configured with authorizationServer: "https://auth.example.com" and nothing else. We tried twice to delete local code in favour of the SDK's and backed out both times:

  1. buildProtectedResourceMetadata: adopting it meant fabricating three fields that are never read.
  2. oauthMetadataResponse: same wall, so we kept our own well-known routing.

Both times the workaround was keeping a local reimplementation of something the SDK does better, which seems opposite to the intent of shipping these helpers.

Proposed

Accept issuers directly, keeping oauthMetadata for the AS passthrough route:

authorizationServers?: URL[];   // issuers for the PRM document
oauthMetadata?: OAuthMetadata;  // optional; enables the AS well-known route

with a requirement that at least one is supplied, and oauthMetadata.issuer continuing to seed authorization_servers when only it is given, so existing callers are unaffected.

That would also let the PRM document carry multiple authorization servers. Today it emits [issuer], always exactly one, while the specification notes that PRM documents "can define multiple authorization servers" and makes selection the client's responsibility per RFC 9728 Section 7.6.

I may be misreading the intended usage. If issuer-only resource servers are meant to route the document themselves, a documented pattern for that would resolve this just as well. Happy to open a PR once you say which shape you would prefer.


AI disclosure per AI_POLICY: I directed this investigation and reviewed the result, including catching an ambiguity in an earlier draft about which specification was being cited. The research and drafting were done by Claude Code. I maintain @maxhealth.tech/mcp-http, the downstream package described above, and hit both cases in my own work. The implementation quote, the specification quotes and the downstream cases were verified against packages/server/src/server/middleware/oauthMetadata.ts on main and the published 2026-07-28 specification text rather than recalled.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions