Table of contents
- Quick summary
- Two different things are both called MCP discovery
- The card is still a draft, and the seven live files show it
- The one MCP file your domain is specified to serve
- Path one: publish server.json to the official MCP Registry
- Path two: serve a card at your own well-known path
- Confirm the card is actually being served
- Where AIScan fits, and where it doesn't
- Publish the card before the specification lands
On 10 September 2026 we probed every host running a remote MCP server listed in the official MCP Registry: 79 domains, three requests each. Seven of them publish a parseable server card at /.well-known/mcp/server-card.json. Those seven files carry 31 distinct top-level fields between them, and exactly one field appears in all seven. Publishing your own card takes about twenty minutes, and this guide covers both the registry route and the self-hosted file, plus how to tell which one your scanner is grading.
Quick summary
| If you want to… | Do this | Time | What it changes |
|---|---|---|---|
| Be findable by MCP clients and marketplaces | Publish server.json to the official MCP Registry with mcp-publisher | 20 min | Your server appears in the registry REST API that aggregators poll |
| Pass a scanner's server-card check | Serve a JSON card at /.well-known/mcp/server-card.json | 10 min | AIScan check P2, and Cloudflare's mcpServerCard, stop reporting 404 |
| Prove you own the domain you publish under | Serve /.well-known/mcp-registry-auth | 5 min | Lets you claim a com.example/* namespace instead of io.github.you/* |
| Check what you already expose | Run a scan and read checks P1 to P4 and E2 | 1 min | Tells you which discovery surfaces exist today |
The honest headline: the registry format is specified and stable, the well-known card is not. Write server.json first, then serve a subset of it at the well-known path, because the working group defining the card says the card should stay close to a subset of server.json anyway.
Two different things are both called MCP discovery
An MCP client that already knows your endpoint URL has a specified way to ask what you can do. The MCP specification dated 2026-07-28 defines a JSON-RPC method called server/discover, and its wording is unambiguous: servers MUST implement it. Fetched from the specification on 10 September 2026, the response returns supportedVersions, capabilities, an instructions string, and a serverInfo block the spec itself warns about, in its own words: serverInfo "is self-reported by the server and is not verified by the protocol."
That method answers the second question. It cannot answer the first one, which is where is your server at all. A client has to already hold the URL before it can call anything.
The server card is the answer to that first question: a static JSON document, fetched over plain HTTP with no session and no handshake, that names the endpoint, the transport, the auth model and the tools. Think of server/discover as the phone extension inside the building and the card as the nameplate by the front door. The nameplate is what a crawler, a marketplace or a readiness scanner reads, because none of them are going to open a session with an unknown host just to find out whether it is worth opening a session with.
The card is still a draft, and the seven live files show it
The Model Context Protocol project runs a Server Card Working Group, and its charter is the clearest statement of where this stands. According to the charter, the group exists to "define what constitutes an MCP Server Card, the standardized document format a Server Card must follow, and how clients discover a Server Card for a given server." Its in-scope list names the discovery mechanism explicitly as unsettled work: "Specification of how an MCP Server Card document is discovered (well-known URL, resource-based discovery, etc.)."
Its single active work item, SEP-2127, is listed with status Draft. And the charter's note on coordination with the Registry working group is the most useful sentence in the document for anyone publishing today: the "Server Card format should stay as close as possible to a subset of server.json."
So there is no ratified schema, and the files in the wild look exactly like a convention with no schema. Across the seven parseable cards we fetched:
| Top-level field | Cards carrying it (of 7) |
|---|---|
description | 7 |
version, serverInfo, capabilities | 6 |
transport, authentication | 5 |
name, $schema | 4 |
tools, resources | 3 |
| 21 further fields | 1 or 2 each |
Four of the seven declare a $schema. Between those four there are three different schema URLs, and every one of them returns HTTP 404:
https://static.modelcontextprotocol.io/schemas/mcp-server-card/v1.json 404
https://static.modelcontextprotocol.io/schemas/server-card/draft/server-card.schema.json 404
https://modelcontextprotocol.io/schemas/server-card/draft-1.json 404
https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json 200, 22,090 bytes
The only MCP schema URL in that list that resolves is server.json's. Two more hosts returned HTTP 200 for the card path and served a body byte-identical to a control path that does not exist on the same host, which is the soft-200 pattern our CI-gate guide diagnoses in detail. Both reproduced on a second pass. One host serves a 303,527-byte card, which is a catalogue wearing a nameplate's filename.
The one MCP file your domain is specified to serve
There is a well-known path the MCP project does define, and it is not the card. Domain-based publishing authentication reads /.well-known/mcp-registry-auth, a one-line file in the form v=MCPv1; k=ed25519; p=<public key>. It exists so you can publish under com.example/your-server instead of a GitHub-derived namespace.
Adoption follows specification almost perfectly. Of the same 79 hosts, 19 (24%) serve a valid mcp-registry-auth file against 7 (8.9%) publishing a parseable card. The specified file is nearly three times as common as the unspecified one, on identical infrastructure, measured in the same minute.
Two more anchors worth holding while you read vendor blog posts on this. IANA's Well-Known URIs registry lists 213 suffixes and registers agent-card.json, but registers neither mcp nor webmcp. And according to Cloudflare Radar, which measured the 200,000 most visited domains for its April 2026 agent-readiness study, "MCP Server Cards and API Catalogs (RFC 9727) together appear on fewer than 15 sites in the entire dataset."
Path one: publish server.json to the official MCP Registry
Best for anyone who wants clients and marketplaces to find the server without knowing your domain. The registry is in preview, and according to its own documentation, breaking changes or data resets may occur before general availability.
- Install the CLI:
brew install mcp-publisher, or download the release binary for your platform. - Run
mcp-publisher initin the server's project directory to generate aserver.jsontemplate. - Set the three required fields. Verified on 10 September 2026 against the published schema,
server.jsonrequires exactlyname,descriptionandversion. Thenameis reverse-DNS with exactly one forward slash, as incom.example/analytics. Theversionshould follow semantic versioning. - For a remote server, add a
remotesentry. Astreamable-httpremote requirestypeandurl, and nothing else:
{
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
"name": "com.example/analytics",
"title": "Example Analytics",
"description": "Real-time reporting for the Example platform.",
"version": "2.0.0",
"remotes": [
{ "type": "streamable-http", "url": "https://example.com/mcp" }
]
}
- Authenticate. GitHub login is the quickest and gives you an
io.github.<user>/*namespace. For a company domain, use DNS login after adding a TXT record of the formv=MCPv1; k=ed25519; p=<public key>, or HTTP login and serve the same string at/.well-known/mcp-registry-auth:
mcp-publisher login github
mcp-publisher login dns --domain "example.com" --private-key "$KEY"
mcp-publisher login http --domain "example.com"
- Run
mcp-publisher publish, then confirm the entry is live by reading it back from the registry REST API.
Publish an sse remote only if you need to support existing clients: the documentation records that transport as deprecated.
Path two: serve a card at your own well-known path
Best for anyone whose readiness score reports a 404 today, and for closed-source or single-tenant servers the registry will not accept. Because the format is unsettled, the defensible choice is a subset of server.json plus the endpoint details a client needs to connect.
- Write the file. Keep the
server.jsonfield names rather than inventing your own, and keep it small enough to read in one request.
{
"name": "com.example/analytics",
"title": "Example Analytics",
"description": "Real-time reporting for the Example platform.",
"version": "2.0.0",
"remotes": [
{ "type": "streamable-http", "url": "https://example.com/mcp" }
],
"capabilities": { "tools": {}, "resources": {} },
"websiteUrl": "https://example.com"
}
- Serve it at
/.well-known/mcp/server-card.jsonwithcontent-type: application/json. Where that file lives depends on your stack:
| Stack | Where the file goes | Gate |
|---|---|---|
| Hugo, Docusaurus, Astro | static/.well-known/mcp/ (Docusaurus also honours baseUrl) | none |
| Next.js | public/.well-known/mcp/, or a route handler | none |
| Lovable, Replit | public/.well-known/ and client/public/.well-known/ | none |
| WordPress | a real file under the web root, since .well-known is served before PHP | none |
| Framer | Site settings, Hosting, Files | Pro and above |
| Webflow | Enterprise-only API | Enterprise |
| Ghost | blocked: theme middleware denies .json outside /assets/ | n/a |
| Squarespace, Wix | no route exists on any plan | n/a |
Wix gives a documented reason rather than leaving it a mystery. According to its developer documentation, a custom site API answers only at <baseUrl>/_functions/<functionName>, so checks P1 through P4 are structurally unreachable there. On Ghost and Squarespace the 404 is correct and there is nothing to fix.
-
Confirm the file is real, not a catch-all response. Request a path under
.well-known/mcp/that has never existed. If it returns the same body, your platform is answering everything with the app shell and the card proves nothing. -
On WordPress the card is one surface out of several, and the surfaces have to agree with each other. Start with ThinkRank: one plugin owns the whole agent-facing set, so you never end up arbitrating between two SEO plugins that each believe they own robots.txt, and switching costs nothing because it imports what Rank Math, Yoast, All in One SEO or SEOPress already hold. The alternatives are real: Rank Math's on-page keyword scoring is better, Yoast integrates with more page builders. Neither of them writes an llms.txt file.
-
On Shopify the same logic points at StoreSEO, which builds llms.txt out of your live catalogue (products, collections, pages, articles) and gives you an agents.md editor. That is the half a
.liquidedit cannot keep current, because the catalogue moves and the file has to move with it. Its listing showed a 5.0 rating across 742 reviews on the Shopify App Store when checked on 10 September 2026. Worth knowing before you start: a Shopify storefront already answersPOST /api/mcpwhether a card exists or not, which is the mismatch the next section is about.
Confirm the card is actually being served
One command answers this. Run npx aiscan-cli yoursite.com, or drop the URL into the box at aiscan.site; there is no signup and no charge. The capabilities dimension reads back every discovery surface in one pass: P1 API catalog, P2 MCP server card, P3 Agent Skills index, P4 OAuth discovery and E2 the service description. Pass on P2 means the card was found and read. The check IDs are documented on the capabilities checks page.
If you would rather check by hand, three requests settle it:
curl -sS -o /dev/null -w '%{http_code} %{content_type} %{size_download}\n' \
https://yoursite.com/.well-known/mcp/server-card.json
curl -sS -o /dev/null -w '%{http_code} %{size_download}\n' \
https://yoursite.com/.well-known/mcp/zz-does-not-exist.json
curl -sS https://yoursite.com/.well-known/mcp/server-card.json | jq -e '.name, .description, .version'
| What you see | What it means | First step |
|---|---|---|
200, application/json, control path 404 | Card is live and real | Nothing |
| 200 on both, same byte count | Catch-all route, not a card | Fix routing before anything else |
200 but text/html | Your host is serving the app shell | Set the content type explicitly |
jq exits non-zero | Missing a required field | Add name, description, version |
| 404 | No card | Follow path two above |
Re-check after any deploy that changes routing. A card is a static file, so the failure mode is silent: nothing breaks, the file just stops being served.
Where AIScan fits, and where it doesn't
Two limits in our own check, both measured rather than guessed, both open on our backlog.
P2 decides on a status code. Grouping the evidence strings across 502 corpus hosts on 10 September 2026 returns a short vocabulary: HTTP 404 on 375 informational results, and the literal string HTTP 200 on 43 partials and 19 passes. The same evidence, two verdicts, and no statement of what the file contained. A live example from this run: one host serving a card with name, description, version, remotes and capabilities was graded partial, evidence "HTTP 200". The fix is one line, to parse the body and say what was found, and it belongs in our codebase rather than in your configuration.
P2 probes one path and misses the live endpoint. Across 30 Shopify storefronts, zero published /.well-known/mcp/server-card.json while 19 answered POST /api/mcp with a tool list. Those stores have a working MCP server and score as though they have nothing. The same shape affects P3, which probes /.well-known/agent-skills/index.json and not the legacy /.well-known/skills/index.json; across 52 documentation services the two paths reach 10 and 5 sites, the union is 11, and only 4 publish both.
So a passing P2 is evidence the file exists. It is not yet evidence the file is any good, and a failing P2 is not evidence you have no MCP server. Read it alongside what your platform already exposes.
Publish the card before the specification lands
The nameplate convention is unfinished, and the seven live files prove nobody is waiting for it to be finished. Write server.json because it is specified today, publish it to the registry if your server is public, and serve a subset at the well-known path so the scanners and marketplaces that already probe it find something to read.
Then check the whole discovery layer rather than one file: run npx aiscan-cli yoursite.com and read P1, P2, P3, P4 and E2 together, since a card pointing at an endpoint with no OAuth metadata leaves an agent one step short. Our platform walkthroughs, including the WebMCP measurement, the agentic commerce protocol study and the CI gate that catches a phantom file, are collected at aiscan.site/guides.
Frequently asked questions
My AIScan report says P2 is HTTP 404, but my MCP server works. Is the scan wrong?
Both are right. P2 probes one static path, /.well-known/mcp/server-card.json, and reports what it finds there. A working MCP endpoint at a different URL is invisible to that probe. Across 30 Shopify storefronts we measured, zero published a card while 19 answered POST /api/mcp with a tool list. If your server is live, the 404 means you have no card, not that you have no server. Publish the card so clients that have never met you can find the endpoint.
I published a card and AIScan reports P2 as partial rather than pass. What is missing?
Probably nothing in your file. Grouping P2's evidence strings across 502 corpus hosts on 10 September 2026 returns the literal string HTTP 200 for 43 partials and 19 passes, so the check awards two different verdicts on identical recorded evidence and never says what the body contained. This is an open bug on our side, not a configuration problem on yours. Confirm your card parses as JSON and carries name, description and version, then treat a partial as a pass.
My server card returns HTTP 200 but agents still cannot read it. What is wrong?
Check two things. First, the content type: if your host returns text/html, most clients will not parse it, so set application/json explicitly. Second, request a path under .well-known/mcp/ that has never existed. If it returns the same body and byte count as your card, your platform is answering every unknown path with the app shell and your card is not really being served. Two of the 79 hosts we probed were in exactly that state, reproduced on a second pass.
Which schema should I validate my server card against?
None of the card schemas resolve today. Four of the seven live cards we fetched declare a $schema, between them naming three different URLs, and all three return HTTP 404. The one MCP schema URL that does resolve is server.json's, hosted at static.modelcontextprotocol.io. Validate against that and use its field names, because the Server Card Working Group charter says the card format should stay as close as possible to a subset of server.json.
Do I need both a registry entry and a well-known card?
They answer different questions. The registry entry gets you into the REST API that MCP marketplaces and aggregators poll, which is how a client that has never heard of your domain finds you at all. The well-known card is what a crawler or a readiness scanner reads when it already has your domain. If your server is public, do both. If the registry will not accept it, the card is your only discovery surface.
mcp-publisher publish fails with a namespace error. How do I fix it?
The name field in server.json has to match the namespace your login authorised. GitHub login only authorises io.github.<your-username>/*, so a name like com.example/analytics will be rejected. Either change the name to match, or authenticate against the domain instead using DNS or HTTP login. For npm packages there is a second trap: the mcpName property in package.json must equal the name in server.json exactly.
Can I publish a private or internal MCP server to the official registry?
No. According to the registry documentation, it does not support servers that are only reachable by a narrow set of users, such as a host on a private network or a package on a private artifact registry. A remote server must be publicly accessible at its stated URL. For internal servers, run your own registry implementing the published OpenAPI interface, or serve a well-known card on the internal host and skip the registry entirely.
Where does the card go on WordPress, Shopify, Squarespace or Wix?
On WordPress, a real file under the web root, because .well-known is served before PHP runs. On Shopify you cannot add an arbitrary .well-known file, though the storefront already answers POST /api/mcp. On Squarespace and Wix there is no route for it on any plan. Wix documents the reason: a custom site API answers only at <baseUrl>/_functions/<functionName>, so checks P1 through P4 cannot pass there and the 404 is correct.
Related guides
Docs Sites and AI Agents: Four Markdown Routes, 52 Sites Tested
Ask an AI assistant how to configure a webhook, add a database index, or set a cache header, and it does not go looking for a blog post. It goes to the vendor's documentation. Docs are the…
The complete AI readiness setup for Wix in 2026
Every other platform in this series asks you to create something. Wix has already created it. Before you open a single panel, a Wix site is serving a robots.txt, a sitemap index, serverrendered HTML…
The Agentic Commerce Stack in 2026: UCP, ACP, AP2 and x402, Measured on 30 Storefronts
Five protocol names now compete for the same sentence in every agentic commerce explainer: UCP, ACP, AP2, x402 and MPP. Almost every explainer treats them as rivals. They are not. Four of the five…
llms.txt vs robots.txt vs sitemap.xml in 2026: Six Files, Six Jobs
Verified 4 September 2026. Every figure below was measured or fetched on that date. Three files keep getting compared as if they were competing for the same job: robots.txt, sitemap.xml and llms.txt.…
