feat(app): add an MCP server card at the well-known discovery path - #554
Draft
claude[bot] wants to merge 1 commit into
Draft
feat(app): add an MCP server card at the well-known discovery path#554claude[bot] wants to merge 1 commit into
claude[bot] wants to merge 1 commit into
Conversation
https://docs.page/.well-known/mcp/server-card.json returned 404, swallowed by the .well-known catch-all. Root-domain agent-readiness scanners probe this SEP-1649 discovery path to detect MCP support, so the per-repo MCP servers already served at /{owner}/{repo}/mcp were invisible to them. Adds a static route serving a SEP-1649 server card. docs.page has no single root MCP server, so the card's transport points at the server for docs.page's own hosted docs and the description explains the per-repository pattern. Cache policy via a new ROOT_MCP_SERVER_CARD_CACHE_HEADERS constant. Opened as a draft: the well-known approach was called too opinionated, and the card carries unresolved questions noted in the pull request description. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y5HaatshAzKUdXWYUbAC4a
|
🚅 Deployed to the docs.page-pr-554 environment in docs.page
|
|
|
15 tasks
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.
Requested via Slack thread
One of five PRs replacing #530, which bundled all five root-domain discovery fixes into a single change. Opened as a draft — see "Open questions" below.
Summary
Before:
https://docs.page/.well-known/mcp/server-card.jsonreturns a 404, swallowed by the.well-knowncatch-all route (confirmed against production). Agent-readiness scanners probe exactly this SEP-1649 path to detect MCP support, so the per-repository MCP servers docs.page already serves at/{owner}/{repo}/mcpare invisible to them.After: that path returns a static SEP-1649 server card as
application/json. docs.page has no single root MCP server, so the card's transport points at the server for docs.page's own hosted docs and its description spells out the per-repository pattern. The tool list mirrors the two tools the real server exposes, andresourcesis declared"dynamic"because each repository serves its own set.In short: a scanner probing the well-known path discovers the MCP capability instead of a 404.
How: a new route at
app/src/app/.well-known/mcp/server-card.json/route.tsserves the card from a build-time constant. It is a more specific segment than the existing.well-known/[[...slug]]catch-all, so it takes precedence — the same mechanism the existing.well-known/jwks.jsonroute relies on. Cache policy comes from a newROOT_MCP_SERVER_CARD_CACHE_HEADERSconstant inapp/src/proxy.ts(day-long edge TTL, hourly browser revalidation). The pre-existingMCP_CACHE_HEADERSconstant, which serves the per-repo MCP route, is left untouched.Open questions — why this is a draft
The
.well-knownapproach was called too opinionated when it was discussed on 2026-08-25, and the card carries real unresolved problems. Each of these was checked against the current code and the live endpoint:The tool list is duplicated with nothing keeping it in sync. The card hardcodes
read_doc_pageandlist_doc_fileswith their input schemas, while the actual definitions live inapp/src/server/mcp/server.ts. Nothing links the two — no shared constant, no test, no codegen — so adding, renaming, or re-shaping a tool silently makes the card wrong. A comment asking future maintainers to keep it in sync is the only safeguard.The protocol version is a hardcoded pin, and this is subtler than it first looks. The card pins
protocolVersion: "2025-11-25". Probing the live server: it echoes back whatever version a client asks for when it supports it (it returned2025-06-18and2025-03-26on request), and falls back to2025-11-25for anything it does not recognise — so2025-11-25is the server's current ceiling and the pin is correct today. The problem is that the ceiling is set by the@modelcontextprotocol/sdkdependency, not by anything in this repo, so a routine SDK bump moves it and leaves this constant quietly stale. Serving a single fixed version in a discovery document also misrepresents a server that negotiates per request. Worth deciding whether to derive it from the SDK or omit the field.The
$schemaURL does not resolve.https://static.modelcontextprotocol.io/schemas/mcp-server-card/v1.jsonreturns a 404 (checked live). The card therefore points at a schema nobody can fetch, which is a poor look on a document whose whole purpose is machine consumption, and it means the card is not validated against anything.Underlying all three: this is a hand-maintained static file describing a dynamic, per-repository system, added to satisfy external scanners. If the team is not happy with that trade, closing this in favour of no root card is a legitimate outcome — the other four PRs in the split stand on their own.
Scope
app/(hosted site, MCP, Ask AI)packages/cli/packages/mdx-bundler/docs/(product documentation)Type of change
Test plan
biome ci .clean (the check CI runs)bun test— 140 pass, 0 failtsc --noEmitinapp/— clean$schemaURL fetched — 404Notes for reviewers
.well-known/jwks.jsonroute. The body is still a build-time constant and the CDN policy makes it effectively static at the edge.main, so this merges independently of the other four PRs.🤖 Generated with Claude Code
https://claude.ai/code/session_01Y5HaatshAzKUdXWYUbAC4a
Generated by Claude Code