Server auth defaults silently produce RFC 9728-noncompliant discovery when the MCP endpoint has a path

Offen Anfängerfreundlich
#2,751 0 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

Bewertung

Schwierigkeit
2/5
Geschätzter Aufwand
1-2 Tage
Anfängerfreundlichkeit
72/100
Issue-Typ
Dokumentation
Klarheit
Klar beschrieben
Aktivitätsstatus
Aktiv
Tech-Stack
typescript

Rechercherichtung

Beginne mit den Optionsdefinitionen und dem JSDoc in server/auth/router.js und server/auth/middleware/bearerAuth.js und vergleiche dann die dokumentierten Standardwerte mit den in der Issue genannten MCP-Autorisierungsanforderungen. Aktualisiere die v1.x- und main-Dokumentation, um die MCP-Endpunkt-URL zu nennen, die Folgen des Weglassens von resourceServerUrl oder resourceMetadataUrl zu erklären und die erforderliche Konfiguration klar zu machen.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Beschreibung

Summary

When an MCP endpoint is served under a path (the common /mcp case), the default behaviour of mcpAuthRouter and requireBearerAuth produces an OAuth discovery surface that violates the MCP authorization spec and RFC 9728, and nothing warns the server author:

  1. mcpAuthRouter({...}) without resourceServerUrl falls back to baseUrl, so Protected Resource Metadata (PRM) is mounted only at /.well-known/oauth-protected-resource with resource: "https://host/" — while the client connected to https://host/mcp.
  2. requireBearerAuth({ verifier }) without resourceMetadataUrl returns a 401 whose WWW-Authenticate has no resource_metadata parameter.

Each option is documented as "optional", but for an MCP server both are effectively required:

  • MCP Authorization, Authorization Server Location: "MCP servers MUST use the HTTP header WWW-Authenticate when returning a 401 Unauthorized to indicate the location of the resource server metadata URL as described in RFC9728 Section 5.1."
  • RFC 9728 §3 puts the metadata for a resource with a path at /.well-known/oauth-protected-resource/<path>; §3.3: "The resource value returned MUST be identical to the protected resource's resource identifier … If these values are not identical, the data contained in the response MUST NOT be used."

The reference client in this SDK masks the problem: discoverMetadataWithFallback falls back to the root well-known and selectResourceURLcheckResourceAllowed accepts a resource that is a parent path of the endpoint. Clients that enforce §3.3 literally do not: Gemini CLI throws ResourceMismatchError (packages/core/src/mcp/oauth-utils.ts, exact protocol//host+pathname comparison) and aborts discovery before ever reaching registration_endpoint, so dynamic client registration never starts. Antigravity CLI (agy) behaves the same. The result for server authors is "works in Claude Code / Codex, silently fails in Gemini CLI / Antigravity" with no hint that the server is at fault.

Reproduction

@modelcontextprotocol/sdk 1.25.1 (same on 1.29.0 and main), Node 24.

import express from 'express';
import { mcpAuthRouter, getOAuthProtectedResourceMetadataUrl } from '@modelcontextprotocol/sdk/server/auth/router.js';
import { requireBearerAuth } from '@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js';
import { ProxyOAuthServerProvider } from '@modelcontextprotocol/sdk/server/auth/providers/proxyProvider.js';
import { InvalidTokenError } from '@modelcontextprotocol/sdk/server/auth/errors.js';

const BASE_URL = 'https://mcp.example.com';
const provider = new ProxyOAuthServerProvider({
  endpoints: { authorizationUrl: 'https://as.example.com/authorize', tokenUrl: 'https://as.example.com/token' },
  verifyAccessToken: async () => { throw new InvalidTokenError('invalid'); },
  getClient: async () => undefined,
});

const app = express();
// What the README-level wiring looks like today: only baseUrl/issuerUrl, no resourceServerUrl / resourceMetadataUrl
app.use(mcpAuthRouter({ provider, issuerUrl: new URL(BASE_URL), baseUrl: new URL(BASE_URL), scopesSupported: ['s1'] }));
app.all('/mcp', requireBearerAuth({ verifier: provider }), (_req, res) => res.json({ ok: true }));
app.listen(3000);
$ curl -si -X POST localhost:3000/mcp -H 'Content-Type: application/json' -d '{}' | grep -i www-authenticate
WWW-Authenticate: Bearer error="invalid_token", error_description="Missing Authorization header"
$ curl -s localhost:3000/.well-known/oauth-protected-resource
{"resource":"https://mcp.example.com/","authorization_servers":["https://mcp.example.com/"],"scopes_supported":["s1"]}
$ curl -s -o /dev/null -w '%{http_code}\n' localhost:3000/.well-known/oauth-protected-resource/mcp
404

Passing the two options fixes the surface (path-scoped PRM with resource: ".../mcp", resource_metadata in the challenge), but the default silently produces the broken shape above.

Suggested changes

  1. Docs/JSDoc (v1.x and main) — say explicitly that resourceServerUrl should be the MCP endpoint URL (not the origin) and that resourceMetadataUrl is required by the MCP spec, with the consequence of omitting each. I have a small PR ready for both branches.
  2. v2 (breaking changes acceptable) — consider making the intent impossible to miss, e.g. require resourceServerUrl in mcpAuthRouter (the neutral AuthMetadataOptions already requires it) and/or require resourceMetadataUrl in BearerAuthOptions, or accept a single mcpServerUrl and derive both.
  3. Optionally, a one-time warning in v1.x when requireBearerAuth runs without resourceMetadataUrl.

Happy to open the PRs once the direction is agreed.

Vorherrschende Sprache
TypeScript
Sterne
13.4k
Forks
2.2k
Ø Merge
3 T. 15 Std.
Gemergte PRs (30 T.)
4

Beitragsleitfaden

Beitragsleitfaden öffnen

Erste Schritte

  1. Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
  3. Forken Sie das Repository und arbeiten Sie in einem Branch.
  4. Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.

Mehr aus modelcontextprotocol/typescript-sdk

Alle Issues in modelcontextprotocol/typescript-sdk

Ähnliche Issues

Weitere Issues zu TypeScript

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.