You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Since the fix for #1, both protected-resource metadata documents resolve correctly. A related gap remains one layer up: an unauthenticated GET https://mcp.motion.dev/plus sent without MCP accept types returns 200 and a JSON server description, with no WWW-Authenticate header. The 401 OAuth challenge is only reachable when the request carries Accept: application/json, text/event-stream (or is a POST).
Clients whose OAuth discovery probe is a plain GET therefore never see the challenge. Some of them parse that 200 body as RFC 9728 protected-resource metadata and fail confusingly, because the description document has no resource field.
The base server behaves the same way (no-Accept=200, with-Accept=405), though it matters less there since it serves tools unauthenticated.
Impact
Codex CLI is a concrete case. codex mcp login motion-plus fails with:
Error: Metadata error: Protected resource metadata missing required resource field
Codex's discovery probe is a plain GET with no Accept header (openai/codex#37830), so it receives the 200 description document and parses it as protected-resource metadata. The error names a metadata document that was never fetched, which sent me a long way down the wrong path before I compared the two responses.
Forcing the accept types makes login succeed on the first attempt:
That is a client-side workaround for a specific client. Any other client that probes with a plain GET hits the same wall.
Expected
An unauthenticated request to a protected MCP endpoint should return 401 with WWW-Authenticate regardless of the Accept header, so auth discovery can find the challenge by the documented path.
If the descriptive JSON is worth keeping, serving it from a separate unprotected URL (or gating it behind an explicit Accept: application/json alone) would preserve it without masking the challenge.
Environment
Codex CLI 0.149.1, macOS 26.5.1 arm64
Signed-in Motion account without Motion+ (correcting an earlier version of this issue that said otherwise). The OAuth flow itself completes; entitlement is separate and does not affect anything above, since the reproduction is unauthenticated.
Summary
Since the fix for #1, both protected-resource metadata documents resolve correctly. A related gap remains one layer up: an unauthenticated
GET https://mcp.motion.dev/plussent without MCP accept types returns200and a JSON server description, with noWWW-Authenticateheader. The401OAuth challenge is only reachable when the request carriesAccept: application/json, text/event-stream(or is a POST).Clients whose OAuth discovery probe is a plain GET therefore never see the challenge. Some of them parse that 200 body as RFC 9728 protected-resource metadata and fail confusingly, because the description document has no
resourcefield.Reproduction
No
WWW-Authenticateheader is present on the 200. The challenge does appear on a POST:The base server behaves the same way (
no-Accept=200,with-Accept=405), though it matters less there since it serves tools unauthenticated.Impact
Codex CLI is a concrete case.
codex mcp login motion-plusfails with:Codex's discovery probe is a plain GET with no Accept header (openai/codex#37830), so it receives the 200 description document and parses it as protected-resource metadata. The error names a metadata document that was never fetched, which sent me a long way down the wrong path before I compared the two responses.
Forcing the accept types makes login succeed on the first attempt:
That is a client-side workaround for a specific client. Any other client that probes with a plain GET hits the same wall.
Expected
An unauthenticated request to a protected MCP endpoint should return
401withWWW-Authenticateregardless of theAcceptheader, so auth discovery can find the challenge by the documented path.If the descriptive JSON is worth keeping, serving it from a separate unprotected URL (or gating it behind an explicit
Accept: application/jsonalone) would preserve it without masking the challenge.Environment
/.well-known/oauth-protected-resourceand/.well-known/oauth-protected-resource/plusreturn 200 with a validresourcefield.Happy to test a fix.