feat(auth): serve static MCP server card for directory scanners#11
Merged
hogan-yuan merged 3 commits intoApr 24, 2026
Merged
Conversation
Adds GET /.well-known/mcp/server-card.json returning serverInfo, authentication scheme and the full tool list. Lets scanners (Smithery, etc.) skip the authenticated tools/list probe and any DCR attempt against the Longbridge OAuth server, while clients that do support RFC 9728 discovery continue to follow the WWW-Authenticate challenge on /mcp as before. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Relative `docs/logo.png` renders broken when directory sites (mcp.so, PulseMCP, Smithery, ...) scrape the README and re-host the HTML. Swap to the canonical raw.githubusercontent.com URL so the logo shows up in every consumer. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 tasks
hogan-yuan
added a commit
that referenced
this pull request
Apr 27, 2026
## Summary Adds a fallback path \`/mcp/.well-known/mcp/server-card.json\` so the static MCP server card stays reachable even when the upstream API gateway only forwards \`/mcp/*\` to this service. Bumps version 0.1.8 → 0.1.9. ## Why Following PR #11, the host-root path works locally but **fails on prod**: \`\`\` $ curl https://openapi.longbridge.com/.well-known/mcp/server-card.json {"code":404000,"message":"api not found","data":null} \`\`\` The Longbridge API gateway has an explicit allowlist that only forwards specific \`.well-known/*\` paths (e.g. \`oauth-protected-resource\`) to this service; \`.well-known/mcp/*\` isn't in the allowlist yet. The gateway team is being asked to add the rule, but in the meantime mounting the same handler under \`/mcp/\` (which the gateway already forwards) gives Smithery and other directory scanners a working URL. ## What changed - **\`src/auth/mod.rs\`**: \`server_card_route\` now exposes the same handler at two paths: \`/.well-known/mcp/server-card.json\` (canonical, per Smithery docs) \`/mcp/.well-known/mcp/server-card.json\` (gateway fallback) - **\`Cargo.toml\` / \`Cargo.lock\` / \`server.json\`**: 0.1.8 → 0.1.9 (incl. OCI image tag in \`packages[0].identifier\`) Both routes are registered at the top-level router **before** the \`nest_service(\"/mcp\", ...)\` mount, so they bypass the auth middleware (verified locally — \`/mcp\` POST still returns 401). ## Test plan Local smoke (port 8000, no auth header): \`\`\` $ curl -s http://127.0.0.1:8000/.well-known/mcp/server-card.json | jq '{name: .serverInfo.name, version: .serverInfo.version, auth: .authentication, tools: (.tools | length)}' { "name": "Longbridge MCP", "version": "0.1.9", "auth": {"required": true, "schemes": ["oauth2"]}, "tools": 108 } $ curl -s http://127.0.0.1:8000/mcp/.well-known/mcp/server-card.json | jq '.serverInfo.name' "Longbridge MCP" $ curl -X POST -o /dev/null -w '%{http_code}\\n' -H 'Accept: application/json, text/event-stream' http://127.0.0.1:8000/mcp 401 \`\`\` - [x] cargo +nightly fmt - [x] cargo check --all-features --all-targets - [x] cargo clippy --all-features --all-targets (no warnings) - [x] both server-card paths return 200 with correct JSON locally - [x] /mcp POST still returns 401 (auth middleware untouched) ## Rollout After merge: 1. Build & push \`ghcr.io/longbridge/longbridge-mcp:0.1.9\` 2. Deploy to \`openapi.longbridge.com\` 3. Verify: \`curl https://openapi.longbridge.com/mcp/.well-known/mcp/server-card.json | jq .serverInfo\` 4. Re-run \`smithery mcp publish https://openapi.longbridge.com/mcp -n longbridge-official/longbridge-mcp\` 5. Re-publish to Official MCP Registry: \`mcp-publisher publish\` (server.json now references 0.1.9) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
GET /.well-known/mcp/server-card.jsonreturningserverInfo,authenticationscheme and the full tool list (built fromtools::list_tools(), cached in aLazyLock)./mcpauth middleware so scanners don't get 401'd.server.jsonversionand OCIidentifiertag).Unblocks Smithery directory scanning. Smithery was looping on our OAuth server's Dynamic Client Registration endpoint (RFC 7591 non-conformance: 200 instead of 201, missing
client_secretin response body). A static server card declaringauthentication.schemes = ["oauth2"]lets Smithery skip DCR entirely and fall back to its Client ID Metadata Document flow.What the endpoint returns
{ "serverInfo": { "name": "Longbridge MCP Server", "version": "0.1.8" }, "authentication": { "required": true, "schemes": ["oauth2"] }, "tools": [ { "name": "account_balance", "description": "...", "inputSchema": { ... } }, ... ] }