anthropics / anthropics/claude-ai-mcp
Authless MCP server on a shared origin: broker ignores path-scoped `authorization_servers: []` (RFC 9728) and enters the co-hosted server's OAuth flow
- 主要语言
- 没有语言数据
- 星标
- 471
- 派生
- 76
- PR 合并指标
- 30 天内没有已合并 PR
描述
### MCP server URL
https://app.distro.media/mcp/reader/stream (also reproduced with https://app.distro.media/mcp/reader/v2)
### Where does the issue occur?
Both at add time (connector is created pre-gated with no server contact) and when pressing the Connect button.
### Transport used
streamable-http
### Client registration type
My server does not have auth
### When did you last reproduce this?
2026-08-05, ~22:17–22:19 UTC
### Browser and OS
claude.ai web in Chrome on macOS; same behavior in Claude Desktop (macOS).
### Describe the issue
Distro Reader is a public, read-only MCP server that requires **no authentication** — anonymous `initialize` returns HTTP 200 with full capabilities, and no reader response ever returns 401 or a `WWW-Authenticate` header. Until recently it worked as an authless custom connector. Now both claude.ai web and Claude Desktop gate it behind a "Connect" prompt that cannot be completed, so its tools never load.
This is the **shared-origin variant** of the authless-connector problem tracked in #262 (and #533, #697, #770): the same origin also hosts a *separate, OAuth-protected* MCP server (Distro Publisher, `/mcp/stream`), so the origin-level OAuth discovery documents legitimately exist and must remain. Two client behaviors combine:
1. **No probe at add time.** Adding the custom connector generates *zero* requests to our server (verified against timestamped origin access logs). The connector is created already in the "You are not connected yet" state with a Connect button — OAuth is assumed without checking.
2. **RFC 9728 path-scoped metadata is fetched, then overridden by the origin-level fallback.** On Connect, the broker receives our resource-specific protected-resource document explicitly advertising **no authorization servers**, then falls back to the origin-level document anyway — which describes the *other* (Publisher) server — and launches that server's OAuth flow against the wrong resource. The user lands on a publisher sign-in page they can never complete. This is the same wrong-precedence behavior reported in #779.
### Server configuration (all verified live on 2026-08-05)
- `POST /mcp/reader/v2` (anonymous, no Authorization header) → **200**, full `initialize` result.
- `GET /.well-known/oauth-protected-resource/mcp/reader/v2` → **200**:
```json
{"resource":"https://app.distro.media/mcp/reader/v2","authorization_servers":[],"bearer_methods_supported":[],"scopes_supported":[],"resource_documentation":"https://docs.distro.media/mcp"}
```
- `GET /.well-known/oauth-protected-resource` (origin-level) → 200 with `authorization_servers: ["https://app.distro.media"]` — this document describes **Distro Publisher**, the OAuth-protected MCP server on the same origin. It must remain for Publisher's one-click OAuth.
- `GET /.well-known/mcp/server-card.json` → lists `distro-reader` with `authentication: { "type": "none" }`.
### Reproduction (2026-08-05, ~22:17–22:19 UTC)
1. claude.ai → Settings → Connectors → Add custom connector.
2. Name: "Distro Reader Test", URL: `https://app.distro.media/mcp/reader/v2`. Click Add.
3. Connector is created and immediately shows **"You are not connected to Distro Reader Test yet."** with a Connect button. No tools are listed. **Origin access logs show no incoming request of any kind during this step** (checked repeatedly over the following ~30 seconds).
4. Click Connect. Origin logs (timestamps UTC):
```
22:19:00.656 POST /mcp/reader/v2 200 <- anonymous initialize SUCCEEDS
22:19:00.786 GET /.well-known/oauth-protected-resource/mcp/reader/v2 200 <- path-scoped doc: authorization_servers: []
22:19:01.071 GET /.well-known/oauth-protected-resource 200 <- FALLBACK to origin doc (Publisher's, advertises OAuth)
22:19:01.211 GET /.well-known/oauth-authorization-server 200
22:19:01.415 POST /register 201 <- dynamic client registration
22:19:02.341 GET /authorize?...&scope=publication&resource=https%3A%2F%2Fapp.distro.media 302
```
Note the authorize request's `resource=https://app.distro.media` — the origin document's resource, not the reader endpoint the connector was configured with.
5. The user lands on Distro **Publisher's** sign-in page ("Claude wants to connect to your DistroVerse publications"). A reader has no Distro account and no way to complete this. Abandoning it leaves the connector permanently stuck at "You started connecting to Distro Reader Test but didn't finish." — tools never load.
### Expected behavior
- A custom connector whose server answers `initialize` anonymously with 200 and never returns 401/`WWW-Authenticate` should be usable without a Connect step (per the MCP authorization spec, OAuth discovery is triggered by a 401 challenge).
- Per RFC 9728, the path-specific protected-resource metadata is authoritative for that resource. A 200 response with an empty `authorization_servers` array should terminate discovery as "no authorization server for this resource" — the client should not fall back to the origin-level document, which on a multi-server origin describes a different resource with different auth.
### Impact
Any origin hosting both an OAuth-protected MCP server and a public no-auth MCP server (a natural pattern: publisher/admin API + public read API) cannot expose the public one as a working connector. There is no server-side configuration that avoids this. We have already tried both documented signals:
- **404 on reader discovery** (the authless signal per the support article on remote MCP connectors) — the broker fell back to the origin-level document and started Publisher OAuth anyway.
- **Explicit empty-`authorization_servers` document** (RFC 9728) — ignored, as shown in the log excerpt above.
The support article's "404 on discovery = no auth required" heuristic is impossible to satisfy on a shared origin — only per-resource path-suffixed metadata can distinguish co-hosted servers, which is exactly what RFC 9728 path-scoped discovery is for. Honoring `authorization_servers: []` in the path-scoped document (or treating a successful anonymous `initialize` as authoritative) would fix this class of setups.
### Regression note
This connector previously worked in regular use as an authless custom connector. The gating behavior (tools refusing to load until "connected", and no probe at add time) appears to be a recent client-side change — server-side discovery behavior for reader paths has been in its current state since 2026-07-21.
### Ruled out: edge blocking
Several similar reports (#76, and commenters on #262) were ultimately resolved as Cloudflare's "Block AI bots" managed rule 403-ing the broker's `Claude-User` traffic at the edge. That does not apply here: the broker's full probe sequence demonstrably reached our origin (see the timestamped log excerpt above), and requests with `Claude-User`, `claude-web`, and `python-httpx` user agents all return 200 through our Cloudflare zone.
### Related issues
- #262 (canonical authless-handshake abandonment; #221 closed as its duplicate)
- #533, #697, #770 (broker insists on OAuth/DCR despite successful anonymous `initialize`)
- #779 (origin-level metadata used instead of the RFC 9728 path-suffixed document)
贡献指南
这个仓库没有索引到贡献指南
评估
这个 Issue 还没有评估数据。