OAuth discovery does not fall back to well-known URI when 401 carries a non-Bearer WWW-Authenticate (e.g. Negotiate)
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 76/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Quiet
- Tech stack
- typescript
- Domain
- api, authentication
Research direction
Start in src/client/streamableHttp.ts at the 401 handler in StreamableHTTPClientTransport.send, comparing the current behavior with the fallback described in the issue and related PR #1045. Reproduce with a non-Bearer challenge and a path-suffixed well-known URI; the work is done when discovery falls back to those URIs and the OAuth flow can proceed.
Written by the indexing model from the issue text.
Description
Describe the bug
StreamableHTTPClientTransport (TypeScript SDK 1.29.0) does not fall back to well-known protected-resource metadata discovery when a 401 response carries a non-Bearer WWW-Authenticate challenge such as Negotiate. The 401 is bubbled to the caller and the OAuth flow never starts, even though valid metadata is hosted at the spec-defined well-known URI.
The MCP authorization spec (Protected Resource Metadata Discovery Requirements) states:
MCP clients MUST support both discovery mechanisms and use the resource metadata URL from the parsed WWW-Authenticate headers when present; otherwise, they MUST fall back to constructing and requesting the well-known URIs in the order listed above.
"Otherwise" should include the case where WWW-Authenticate is present but does not advertise Bearer ... resource_metadata=.... Today the fallback only fires when the header is missing entirely (per PR #1045 / SEP-985).
To Reproduce
Steps to reproduce the behavior:
-
Stand up an MCP server that returns 401 with a non-Bearer challenge for unauthenticated requests, and hosts valid protected-resource metadata at the path-suffixed well-known URI:
$ curl -i -X POST https://server/api/v1/help_python_skill/mcp \ -H 'Content-Type: application/json' \ -H 'Accept: application/json, text/event-stream' \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}' HTTP/1.1 401 Unauthorized www-authenticate: Negotiate content-type: text/plain Authentication required. $ curl -i 'https://server/.well-known/oauth-protected-resource/api/v1/help_python_skill/mcp' HTTP/1.1 200 OK content-type: application/json {"resource":"https://server/api/v1/help_python_skill/mcp", "authorization_servers":["https://server"], "scopes_supported":[".default"], "bearer_methods_supported":["header"]} -
Connect to it from MCP Inspector (uses TS SDK 1.29.0):
npx @modelcontextprotocol/inspector -
Enter the server URL and pick
streamable-httptransport. Click Connect. -
Observe the connection fails with a bare 401; no request is ever made to either well-known URL.
Expected behavior
On any 401 from the MCP endpoint, after parsing WWW-Authenticate:
- If a
Bearerchallenge is present → use itsresource_metadataURL. - Otherwise (header missing OR present with a non-Bearer scheme) → fall back to:
<origin>/.well-known/oauth-protected-resource<resource_path>- then
<origin>/.well-known/oauth-protected-resource
Step 2 currently only fires when the header is missing entirely; it should also fire when the header advertises a non-Bearer scheme.
Logs
Inspector logs from a failing connection attempt:
[MCP] New StreamableHttp connection request
[MCP] Query parameters: {"url":"https://server/api/v1/help_python_skill/mcp","transportType":"streamable-http"}
[MCP] Created StreamableHttp client transport
[MCP] Client <-> Proxy sessionId: 86af1624-e651-42ae-90a3-1fb0dade5bc5
[MCP] Error from MCP server: StreamableHTTPError: Streamable HTTP error: Error POSTing to endpoint: Authentication required.
[MCP] at StreamableHTTPClientTransport.send (.../@modelcontextprotocol/sdk/dist/esm/client/streamableHttp.js:364:23)
[MCP] at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
[MCP] code: 401
[MCP] }
Additional context
- Affected file:
src/client/streamableHttp.ts— the 401 handler inStreamableHTTPClientTransport.send(compiled atdist/esm/client/streamableHttp.js:364). - Downgrading to
@modelcontextprotocol/sdk@1.17.5makes the connection work — discovery proceeds and OAuth completes — confirming this is a regression introduced in a later version's stricter handling of the 401 response. - Workaround: pin
@modelcontextprotocol/sdkto1.17.5via npmoverridesin the consuming package. - Real-world impact: any MCP server that (a) supports OAuth via the well-known URI mechanism and (b) sits behind middleware that emits a non-Bearer challenge (Kerberos/SPNEGO is common in enterprise environments) cannot be connected to from any client built on this SDK — including MCP Inspector.
- Environment:
@modelcontextprotocol/sdk: 1.29.0 (regression), 1.17.5 (works)@modelcontextprotocol/inspector: 0.16.6 and 0.21.1 (both reproduce — same underlying SDK)- Node: v20.14.0
- Related: PR #1045 (SEP-985 fallback — handles only missing-header case), Issue #822, Issue #758.
- Dominant language
- TypeScript
- Stars
- 13.4k
- Forks
- 2.2k
- Avg merge
- 3d 12h
- Merged PRs (30d)
- 3
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from modelcontextprotocol/typescript-sdk
-
Auth metadata discovery: fallback URL built on resource host instead of authorization-server host Open
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
modelcontextprotocol/typescript-sdk#2783 · 1 comment ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
Difficulty 1/5 Under an hour Newbie friendliness 92/100
modelcontextprotocol/typescript-sdk#2766 · 1 comment ·
-
Difficulty 2/5 1-2 days Newbie friendliness 72/100
All issues in modelcontextprotocol/typescript-sdk
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
Eynzof/Hermes-CN-Desktop#610 ·
-
bug clawsweeper:linked-pr-open clawsweeper:needs-live-repro clawsweeper:no-new-fix-pr impact:message-loss issue-rating: 🐚 platinum hermit P2 regression
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
calcite-components needs triage refactor
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
Esri/calcite-design-system#15203 ·
-
Difficulty 1/5 Under an hour Newbie friendliness 78/100
fullcalendar/fullcalendar#8106 ·