Skip to content

Return 401 challenge for unauthenticated /mcp requests - #6

Merged
ebeigarts merged 1 commit into
mainfrom
bugfix/fix-imap-config-missing
Aug 6, 2026
Merged

Return 401 challenge for unauthenticated /mcp requests#6
ebeigarts merged 1 commit into
mainfrom
bugfix/fix-imap-config-missing

Conversation

@ebeigarts

Copy link
Copy Markdown
Member

Problem

An /mcp request without a Bearer token returned [nil, nil] from resolve_mcp_context, so the MCP server was built with a nil server_context. The mcp gem wraps whatever it is given in MCP::ServerContext, which delegates unknown methods to the wrapped object via method_missing. With nil wrapped, tools/list still succeeded with a 200 — but any actual tool call blew up with an opaque JSON-RPC -32603:

undefined method 'imap_config' for an instance of MCP::ServerContext

Fix

Reject the request up front with a 401 and the WWW-Authenticate: Bearer resource_metadata=… pointer, which is what tells an MCP client to re-run the OAuth flow. Extracted the response builder into a private unauthorized_response(description) helper, reused by the existing JwtService::Error rescue.

Testing

Added a spec for the missing-header case. Verified it fails without the app.rb change (returns 200, since tools/list never touches the context — the same shape as the original bug). Full suite: 78 examples, 0 failures. Rubocop clean.

Note

Bare OPTIONS /mcp preflights are now also rejected. The app has no CORS handling today, so browser-based clients were already broken; if one is added later it will need to bypass this check.

Gemfile.lock carries an incidental mail_mcp (1.0.0)(1.0.1) sync picked up when bundler resolved against the v1.0.1 version file.

🤖 Generated with Claude Code

An /mcp request without a Bearer token built the MCP server with a nil
server_context. MCP::ServerContext wraps it and delegates unknown methods
via method_missing, so tools/list still returned 200 while any actual tool
call failed with an opaque JSON-RPC -32603:

  undefined method 'imap_config' for an instance of MCP::ServerContext

Reject the request up front with a 401 and the WWW-Authenticate
resource_metadata pointer, so MCP clients re-run the OAuth flow instead.
Extracted the response builder into unauthorized_response, reused by the
existing JwtService::Error rescue.

Note: bare OPTIONS /mcp preflights are now also rejected. The app has no
CORS handling today, so browser clients were already broken; adding one
will need to bypass this check.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ebeigarts
ebeigarts merged commit 21b498e into main Aug 6, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant