Skip to content

feat(auth): serve static MCP server card for directory scanners#11

Merged
hogan-yuan merged 3 commits into
longbridge:mainfrom
hogan-yuan:feat/smithery-server-card
Apr 24, 2026
Merged

feat(auth): serve static MCP server card for directory scanners#11
hogan-yuan merged 3 commits into
longbridge:mainfrom
hogan-yuan:feat/smithery-server-card

Conversation

@hogan-yuan
Copy link
Copy Markdown
Member

@hogan-yuan hogan-yuan commented Apr 24, 2026

Summary

  • Adds GET /.well-known/mcp/server-card.json returning serverInfo, authentication scheme and the full tool list (built from tools::list_tools(), cached in a LazyLock).
  • Mounts the route outside the /mcp auth middleware so scanners don't get 401'd.
  • Bumps version to 0.1.8 (+ server.json version and OCI identifier tag).

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_secret in response body). A static server card declaring authentication.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": { ... } },
    ...
  ]
}

hogan-yuan and others added 3 commits April 24, 2026 16:26
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>
@hogan-yuan hogan-yuan merged commit 68e3330 into longbridge:main Apr 24, 2026
1 check passed
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>
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