---
title: "Publish an MCP Server Card So Agents Can Find Your Tools"
slug: publish-mcp-server-card
published: 2026-09-10T08:16:54.449206+00:00
updated: 2026-09-10T08:16:54.449206+00:00
author: "Asif Rahman"
author_url: https://masifrahman.com
category: "AI Readiness"
tags: MCP, server card, agent discovery, well-known, AI readiness, check:P1, check:P2, check:P3, check:P4, check:E2
description: "We probed 79 hosts in the official MCP Registry and only 7 publish a server card. Here is how to write server.json and serve a card agents can really read."
url: https://aiscan.site/blog/publish-mcp-server-card
---

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.

1. Install the CLI: `brew install mcp-publisher`, or download the release binary for your platform.
2. Run `mcp-publisher init` in the server's project directory to generate a `server.json` template.
3. Set the three required fields. Verified on 10 September 2026 against the published schema, `server.json` requires exactly `name`, `description` and `version`. The `name` is reverse-DNS with exactly one forward slash, as in `com.example/analytics`. The `version` should follow semantic versioning.
4. For a remote server, add a `remotes` entry. A `streamable-http` remote requires `type` and `url`, and nothing else:

```json
{
  "$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" }
  ]
}
```

5. 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 form `v=MCPv1; k=ed25519; p=<public key>`, or HTTP login and serve the same string at `/.well-known/mcp-registry-auth`:

```bash
mcp-publisher login github
mcp-publisher login dns  --domain "example.com" --private-key "$KEY"
mcp-publisher login http --domain "example.com"
```

6. 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.

1. Write the file. Keep the `server.json` field names rather than inventing your own, and keep it small enough to read in one request.

```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" }
  ],
  "capabilities": { "tools": {}, "resources": {} },
  "websiteUrl": "https://example.com"
}
```

2. Serve it at `/.well-known/mcp/server-card.json` with `content-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.

3. 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.

4. On WordPress the card is one surface out of several, and the surfaces have to agree with each other. Start with [ThinkRank](https://thinkrank.ai): 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.

5. On Shopify the same logic points at [StoreSEO](https://storeseo.com/), 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 `.liquid` edit 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](https://apps.shopify.com/storeseo) when checked on 10 September 2026. Worth knowing before you start: a Shopify storefront already answers `POST /api/mcp` whether 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](https://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](https://aiscan.site/docs/checks/capabilities).

If you would rather check by hand, three requests settle it:

```bash
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](https://aiscan.site/blog/webmcp-do-you-need-it-yet), [the agentic commerce protocol study](https://aiscan.site/blog/agentic-commerce-protocols-explained) and [the CI gate that catches a phantom file](https://aiscan.site/blog/aiscan-ci-gate-github-actions), are collected at [aiscan.site/guides](https://aiscan.site/guides).
