Skip to content

Unauthenticated GET returns 200 without the MCP Accept header, hiding the 401 OAuth challenge #2

Description

@sumanthratna

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/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.

Reproduction

$ curl -sS -o /dev/null -w '%{http_code}\n' https://mcp.motion.dev/plus
200

$ curl -sS -o /dev/null -w '%{http_code}\n' \
    -H 'Accept: application/json, text/event-stream' https://mcp.motion.dev/plus
405

$ curl -sS https://mcp.motion.dev/plus | jq 'keys'
["description","docs","name","protocolVersion","tools","transport"]

No WWW-Authenticate header is present on the 200. The challenge does appear on a POST:

$ curl -sS -o /dev/null -D - -X POST https://mcp.motion.dev/plus \
    -H 'Accept: application/json, text/event-stream' -H 'Content-Type: application/json' \
    -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"probe","version":"1"}}}'
HTTP/2 401
www-authenticate: Bearer realm="motion", resource_metadata="https://mcp.motion.dev/.well-known/oauth-protected-resource/plus"

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:

[mcp_servers.motion-plus]
url = "https://mcp.motion.dev/plus"
http_headers = { "Accept" = "application/json, text/event-stream" }

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.
  • Verified 2026-08-24, after Motion+ MCP server OAuth fails with invalid_target — /plus resource not registered in auth metadata #1 was closed. Both /.well-known/oauth-protected-resource and /.well-known/oauth-protected-resource/plus return 200 with a valid resource field.
  • The 200/405 split reproduced 5 times out of 5.

Happy to test a fix.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions