Server auth defaults silently produce RFC 9728-noncompliant discovery when the MCP endpoint has a path
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
- Bereich
- authentication, documentation
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:
mcpAuthRouter({...})withoutresourceServerUrlfalls back tobaseUrl, so Protected Resource Metadata (PRM) is mounted only at/.well-known/oauth-protected-resourcewithresource: "https://host/"— while the client connected tohttps://host/mcp.requireBearerAuth({ verifier })withoutresourceMetadataUrlreturns a401whoseWWW-Authenticatehas noresource_metadataparameter.
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-Authenticatewhen 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: "Theresourcevalue 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 selectResourceURL → checkResourceAllowed 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
- Docs/JSDoc (v1.x and main) — say explicitly that
resourceServerUrlshould be the MCP endpoint URL (not the origin) and thatresourceMetadataUrlis required by the MCP spec, with the consequence of omitting each. I have a small PR ready for both branches. - v2 (breaking changes acceptable) — consider making the intent impossible to miss, e.g. require
resourceServerUrlinmcpAuthRouter(the neutralAuthMetadataOptionsalready requires it) and/or requireresourceMetadataUrlinBearerAuthOptions, or accept a singlemcpServerUrland derive both. - Optionally, a one-time warning in v1.x when
requireBearerAuthruns withoutresourceMetadataUrl.
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
Erste Schritte
- Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
- Forken Sie das Repository und arbeiten Sie in einem Branch.
- Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.
Mehr aus modelcontextprotocol/typescript-sdk
-
Auth metadata discovery: fallback URL built on resource host instead of authorization-server host Offen
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 74/100
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 82/100
modelcontextprotocol/typescript-sdk#2783 · 1 Kommentar ·
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 88/100
-
Schwierigkeit 1/5 Unter einer Stunde Anfängerfreundlichkeit 92/100
modelcontextprotocol/typescript-sdk#2766 · 1 Kommentar ·
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 84/100
modelcontextprotocol/typescript-sdk#2742 · 2 Kommentare ·
Alle Issues in modelcontextprotocol/typescript-sdk
Ähnliche Issues
-
comp/dashboard P3 type/bug
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 86/100
NousResearch/hermes-agent#117722 ·
-
clawsweeper:fix-shape-clear clawsweeper:queueable-fix clawsweeper:source-repro impact:ux-friction issue-rating: 🦞 diamond lobster no-stale P3
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
-
community first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
Schwierigkeit 1/5 Unter einer Stunde Anfängerfreundlichkeit 76/100
-
code-quality refactoring
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 84/100
github/gh-aw-firewall#8816 ·
-
integration:quickjs org:external priority:backlog topic:code-interpreter topic:middleware type:feature
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 74/100
langchain-ai/deepagents#6450 ·