Skip to content

VoiceMode Connect: POST /consent returns empty-bodied 503, MCP OAuth can never complete #511

Description

@estowe10

Summary

Authorising an MCP client against https://voicemode.dev/mcp cannot complete. The consent screen renders correctly, but clicking Approve results in POST https://voicemode.dev/consent returning HTTP 503 with an empty body. Because there is no body to render, the browser stays on the consent page and the button appears to do nothing — no error is surfaced to the user.

No authorization code is issued, the loopback listener is never called, and no tokens are ever written.

Notably, the CLI auth path works fine (voicemode connect auth login → logged in, refresh token present) and the web client is connected. It is specifically the MCP OAuth path that fails, which should help narrow this down.

Environment

  • macOS 14.6 (Darwin 23.6.0), Claude Code
  • voicemode plugin 8.12.0p0, mcp-remote 0.1.38, Node v22.22.0
  • Reproduced in both Chrome and Safari (separate profiles, separate cookie jars)
  • Reproduced before and after a complete plugin uninstall/reinstall (plugin, cache, marketplace, data dirs, and ~/.mcp-auth state all purged; same version reinstalled)
  • Account authenticates via Auth0 (login.us.auth0.com)

Steps to reproduce

  1. npx -y mcp-remote https://voicemode.dev/mcp 5450
  2. Open the printed /authorize URL.
  3. Complete the Auth0/Google sign-in leg — this succeeds; the consent card renders with the correct name, email and avatar.
  4. Click Approve.

Expected: 302 to http://localhost:5450/oauth/callback?code=…, tokens written to ~/.mcp-auth/.

Actual: POST /consent503, empty body. The page does not navigate. mcp-remote sits at Authentication required. Waiting for authorization... indefinitely.

Reproduced across 9 clean flows.

Key finding: submission method changes the outcome

Same origin, same session, same consent_key, same form body:

Submission method Result
Top-level form navigation (the Approve button) 503, empty body
Background fetch() with identical body 302 redirect issued correctly
Replayed / previously-failed consent_key 400 "Session Expired"

The handler works when invoked as a subresource request and fails when invoked as a navigation. That points at the navigation path specifically — possibly the Sec-Fetch-Mode: navigate / Upgrade-Insecure-Requests: 1 request, or whatever issues the plaintext http://localhost redirect for a document-level request.

This is not browser-specific: Chrome and Safari behave identically, so it is not an Upgrade-Insecure-Requests quirk of a single engine.

Ruled out (client side)

  • Stale local auth state~/.mcp-auth/ wiped between every attempt.
  • Stale plugin install — full uninstall + reinstall changed nothing.
  • Click interceptiondocument.elementFromPoint() at the button centre returns the button itself; no overlay.
  • JS preventing submit — the submit event fires and is not defaultPrevented. The page's only script is the Cloudflare Insights beacon.
  • Loopback binding / IPv6 — the listener binds 127.0.0.1:5450; http://localhost:5450/oauth/callback resolves and answers (400 for a missing code, as expected).
  • Racing consent sessions — suppressed mcp-remote's automatic browser launch so exactly one consent session existed. Still 503.

The MCP endpoint itself is otherwise healthy: POST /mcp returns 401 with a correct WWW-Authenticate: Bearer challenge, /.well-known/oauth-authorization-server resolves, and dynamic client registration at /register succeeds.

Related: can the bridge use the CLI token instead?

Since voicemode connect auth login already yields a valid token, I tried using it with the native bridge (VOICEMODE_MCP_URL / VOICEMODE_MCP_TOKEN) to bypass the consent flow:

Endpoint Result with CLI access token
https://voicemode.dev/mcp 401 invalid_token
https://app.voicemode.dev/mcp 401 invalid_token
https://api.voicemode.dev/mcp 530 (error 1033, no origin)

The Auth0 token appears to be a different audience from what /mcp accepts, so there is currently no way to reach the MCP endpoint without the broken consent flow. If the bridge is intended to work with CLI credentials, that would be a useful documented escape hatch while the consent bug is open.

Doc drift

.claude/skills/voicemode-connect/SKILL.md instructs users to add npx -y mcp-remote https://voicemode.dev/mcp as a separate MCP server, and links to docs/connect/README.md, docs/connect/architecture.md, docs/connect/setup/claude-code.md and docs/connect/reference/mcp-tools.md — none of which exist in the 8.12.0p0 package. It also doesn't mention the voicemode connect auth command group or the native VOICEMODE_MCP_URL bridge, which appear to be the actually-supported path.

Minor

https://voicemode.dev/logo.png returns 404 on the consent page (broken image in the card), which may indicate a partial deploy.

Suggested fixes

  1. Fix the 503 on the navigation path of POST /consent.
  2. Regardless of the above — never return an empty-bodied 5xx to this form. Render an error page so the failure is visible rather than presenting as a dead button.
  3. Refresh SKILL.md to describe the supported Connect path.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions