microsoft / microsoft/vscode

MCP client triggers confusing "does not support automatic client registration" OAuth dialog for servers using only static headers (non-OAuth), e.g. on an expired credential

Open
#334,970 0 comments 0 reactions 1 assignee Claimed by @TylerLeonhardt View on GitHub
Dominant language
TypeScript
Stars
193k
Forks
42.4k
PR merge metrics
PR metrics pending

Description

Does this issue occur when all extensions are disabled?: Yes (this is core VS Code MCP client behavior, not extension-specific)

- VS Code Version: 1.106.x / Insiders (reproduced across multiple recent releases)
- OS Version: macOS

## Summary

An MCP server configured with only a static `headers` block (no `oauth` block at all in `mcp.json`) still triggers VS Code's OAuth Dynamic Client Registration (DCR) fallback dialog on any `401`/`403` response — including the completely ordinary case of an **expired credential in the configured header** (e.g. a bearer token or API key the user pastes in via an `${input:...}` prompt). This is confusing because the user never configured OAuth for this server and has no "client ID" to give it; the dialog implies something is broken with an authorization server that, from the user's perspective, doesn't even apply here.

## Root cause (traced in source)

In [`src/vs/workbench/api/common/extHostMcp.ts`](https://github.com/microsoft/vscode/blob/main/src/vs/workbench/api/common/extHostMcp.ts):

```ts
function isAuthStatusCode(status: number): boolean {
return status === 401 || status === 403;
}
```

`_fetchWithAuthRetry` calls this on every response and, if true, calls `createAuthMetadata(...)` to attempt OAuth resource/authorization-server discovery — **regardless of the `WWW-Authenticate` header's scheme name**, and regardless of whether the server's `mcp.json` entry has a static `headers` block that's already supposed to be sufficient auth. If discovery fails (no `.well-known/oauth-protected-resource` etc., as is the case for a server that was never meant to be OAuth-protected), `_addAuthHeader` (via `extHostAuthentication.ts`'s `$registerDynamicAuthProvider`) ultimately calls `$promptForClientRegistration`, producing:

> The authorization server 'https://.../' does not support automatic client registration. Do you want to proceed by manually providing a client registration (client ID)?

We initially assumed this was triggered by the literal `WWW-Authenticate: Bearer` scheme name and tried renaming our server's challenge scheme (`Bearer` -> custom tokens). This had no effect, which is expected once you look at `isAuthStatusCode`: the scheme name is never inspected, only the status code.

## Steps to Reproduce

1. Configure an MCP server (`"type": "http"`) in `mcp.json` with **only** a static `headers` block and **no** `oauth` block, e.g.:
```json
{
"servers": {
"my-server": {
"url": "https://example.com/mcp",
"type": "http",
"headers": { "Authorization": "Bearer ${input:my_token}" }
}
},
"inputs": [
{ "type": "promptString", "id": "my_token", "description": "Token", "password": true }
]
}
```
2. Start the server with a valid token — works fine.
3. Let the token expire (or configure/paste an invalid one), and trigger another request (any tool call, or restart the server).
4. The server correctly returns `401 Unauthorized` (with or without a `WWW-Authenticate` header — reproduces either way, we tested both a bare `WWW-Authenticate: Bearer` and a fully custom, non-OAuth-sounding scheme token).
5. Instead of surfacing a plain "request failed with 401" (or ideally re-prompting for the `${input:...}` value), VS Code shows the "does not support automatic client registration" dialog and asks for an OAuth client ID.

## Expected Behavior

If an MCP server's `mcp.json` entry has no `oauth` block configured, VS Code should not attempt OAuth discovery/DCR at all on a `401`/`403` — it should simply surface the failure as an authentication/authorization error against the configured static `headers`, ideally by re-prompting the relevant `${input:...}` value(s) rather than falling back to an unrelated OAuth flow the user never opted into.

## Actual Behavior

Any `401`/`403`, even from a server using only static headers with no OAuth configuration whatsoever, funnels through the same OAuth discovery -> DCR -> "manually provide a client ID" fallback dialog, which is confusing and unactionable for users of header-only auth.

## Additional Context

- Declining the dialog (Escape/Cancel) is safe — it just throws `Error('User did not provide client details')` internally (`extHostAuthentication.ts`), which is caught and logged as a warning in `extHostMcp.ts`'s `_addAuthHeader`, without corrupting the connection. But this isn't obvious to end users seeing the dialog, and the underlying UX problem (no re-prompt for the expired static credential) remains.
- We searched existing issues; the closest related ones are #279955 (DCR regression for servers that *do* support it) and #321834 (stale-token eviction, but only for servers that already completed a real OAuth flow) — neither covers this "no OAuth configured at all" case.
- Happy to provide further logs/repro details if useful.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.