Skip to content

feat(mcp): return browser-friendly hello page for GET /mcp from browsers#40309

Draft
aminghadersohi wants to merge 4 commits into
apache:masterfrom
aminghadersohi:research-mcp-hello-page
Draft

feat(mcp): return browser-friendly hello page for GET /mcp from browsers#40309
aminghadersohi wants to merge 4 commits into
apache:masterfrom
aminghadersohi:research-mcp-hello-page

Conversation

@aminghadersohi
Copy link
Copy Markdown
Contributor

SUMMARY

When a user navigates to the MCP server URL in a browser, they currently see a raw JSON 401 error ({"error": "invalid_token", "error_description": "Authentication failed"}). This confuses users who don't realize they need to configure an MCP client — they think the server is broken and file support tickets.

This PR detects browser requests by inspecting the Accept header: if it contains text/html and does not contain application/json or text/event-stream, a friendly 200 HTML page is returned instead. The page explains:

  • This is an MCP API endpoint, not a web page
  • How to configure it in Claude Desktop, Claude Code, or Cursor
  • What credentials are needed

All programmatic MCP clients (which always send application/json or text/event-stream) are completely unaffected — they continue to receive the existing JSON 401.

Implementation: _json_auth_error_handler() in superset/mcp_service/jwt_verifier.py — the Starlette on_error callback for AuthenticationMiddleware — already receives the HTTPConnection with headers, making this a minimal, surgical change.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

Before: {"error": "invalid_token", "error_description": "Authentication failed"} (401)

After: Clean HTML page with "Superset MCP Server" heading, explanation blurb, ready-to-paste JSON config block for MCP clients, and supported clients list.

TESTING INSTRUCTIONS

  1. Start the MCP server: superset run-mcp-server
  2. Open http://localhost:5008/mcp/ in a browser — you should see the friendly HTML page
  3. curl -H "Accept: application/json" http://localhost:5008/mcp/ — should still return JSON 401
  4. Run unit tests: pytest tests/unit_tests/mcp_service/test_jwt_verifier_browser_hello.py -v

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration
  • Introduces new feature or API
  • Removes existing feature or API

When a browser opens the MCP endpoint (Accept: text/html without
application/json or text/event-stream), return a 200 HTML page
explaining what the endpoint is and how to configure it in Claude
Desktop, Claude Code, or Cursor. API and SSE clients continue to
receive the existing JSON 401 response unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@netlify
Copy link
Copy Markdown

netlify Bot commented May 20, 2026

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit e9c25ff
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6a0e410d82cf940008655026
😎 Deploy Preview https://deploy-preview-40309--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 20, 2026

Codecov Report

❌ Patch coverage is 0% with 16 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.10%. Comparing base (38546d7) to head (5c393db).
⚠️ Report is 68 commits behind head on master.

Files with missing lines Patch % Lines
superset/mcp_service/jwt_verifier.py 0.00% 14 Missing ⚠️
superset/mcp_service/mcp_config.py 0.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #40309      +/-   ##
==========================================
- Coverage   64.15%   64.10%   -0.05%     
==========================================
  Files        2591     2591              
  Lines      138214   138357     +143     
  Branches    32056    32078      +22     
==========================================
+ Hits        88671    88698      +27     
- Misses      48013    48127     +114     
- Partials     1530     1532       +2     
Flag Coverage Δ
hive 39.40% <0.00%> (-0.06%) ⬇️
mysql 59.05% <0.00%> (-0.10%) ⬇️
postgres 59.13% <0.00%> (-0.10%) ⬇️
presto 41.08% <0.00%> (-0.06%) ⬇️
python 60.56% <0.00%> (-0.10%) ⬇️
sqlite 58.77% <0.00%> (-0.10%) ⬇️
unit 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

aminghadersohi and others added 3 commits May 20, 2026 23:40
…HEAD

- Introduce MCPJWTVerifier(JWTVerifier) base class that registers
  _auth_error_handler as the Starlette on_error callback; previously the
  callback was only wired inside DetailedJWTVerifier (MCP_JWT_DEBUG_ERRORS=True),
  so the HTML page was never shown in the default configuration
- mcp_config.py non-debug path now uses MCPJWTVerifier instead of bare
  JWTVerifier; DetailedJWTVerifier inherits MCPJWTVerifier
- Add _prefers_browser_html() helper: checks method (GET/HEAD only) and
  Accept header (case-insensitive); prevents POST/OPTIONS with text/html
  from incorrectly receiving a 200 HTML response
- Rename _json_auth_error_handler -> _auth_error_handler, return type
  narrowed to Response (Starlette base class, matching on_error signature)
- Add tests: POST+text/html -> 401, HEAD+text/html -> 200, uppercase Accept

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The PR renamed _json_auth_error_handler to _auth_error_handler in
jwt_verifier.py (to reflect that it now returns HTML for browsers
rather than always JSON), but test_jwt_verifier.py still imported
the old name, causing a collection-time ImportError that failed all
unit tests.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant