Protocols · November 11, 2026 · 8 min read

The MCP Registry for agent builders: publishing a server and reading the API

The official MCP Registry stores metadata, not code. Here is how server.json, namespace verification and the read-only REST API work as of November 2026, how to publish a local or remote server, and how an agent platform should consume the registry without hammering it.

If you build MCP servers, you have probably noticed that "where do people find this thing" is still a mess. Servers live in GitHub READMEs, awesome lists, vendor marketplaces and Discord threads, and every client has its own idea of how to describe an install command. The official MCP Registry is the protocol project's answer to that: one place where a server's name, install method and connection details are published in a standard format, so that marketplaces and clients can stop scraping READMEs.

It is worth understanding even if you never publish anything, because the registry's design decides what your agent platform can trust about a server and what it still has to check itself. This post covers what the registry actually stores, how publishing works for both local and remote servers, how namespace verification works, and how to read the API responsibly if you run a client, a marketplace or an internal catalog. One caveat up front: the registry docs still label it a preview, and they say breaking changes or data resets may happen before general availability. Build on it, but keep your own copy of anything you depend on.

What the registry is, and what it is not

The most important sentence in the docs is that the registry hosts metadata, not artifacts. Your actual server code still lives in a package registry like npm, PyPI or Docker Hub, or it runs as a remote service at a public URL. The MCP Registry stores a server.json document that points at those places and describes how to run or connect to the server. The about page gives the example of a weather-mcp package on npm, with registry metadata mapping "weather v1.2.0" to that npm package.

It is also deliberately unopinionated. There are no ratings, no download counts and no "verified safe" badge. The registry's own security story is namespace authentication plus basic spam controls, and it explicitly delegates code scanning to the underlying package registries and to downstream aggregators. That split matters for agent builders. A server being listed tells you who published it. It tells you nothing about whether its tools are safe to hand to an autonomous agent, which is still your job. Our posts on prompt injection defenses and sandboxing agents that run code still apply in full.

Finally, the registry is public only. The docs say it does not support private servers, meaning anything on an internal network or a private package feed. If you want a catalog of internal servers, the recommendation is to run your own registry that implements the same OpenAPI interface, so clients that understand the official API can read yours too. The maintainers also note that the official codebase is not designed for self-hosting and they do not support forks, so plan on building or buying that piece rather than cloning theirs.

The server.json file

Every listing is one server.json document. The quickstart shows the shape for an npm-packaged server that runs over stdio:

{
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
  "name": "io.github.my-username/weather",
  "description": "An MCP server for weather information.",
  "repository": {
    "url": "https://github.com/my-username/mcp-weather-server",
    "source": "github"
  },
  "version": "1.0.1",
  "packages": [
    {
      "registryType": "npm",
      "identifier": "@my-username/mcp-weather-server",
      "version": "1.0.1",
      "transport": { "type": "stdio" }
    }
  ]
}

The name is the server's identity in reverse-DNS form, and it is the part the registry actually verifies. The packages array says where the code lives and how to launch it. A package entry can also declare environmentVariables, each with a description, whether it is required, and an isSecret flag, which is how a client knows to prompt the user for an API key instead of printing it in a config file.

There is one cross-check that trips people up. For npm, the registry verifies that the package really belongs to the server by looking for an mcpName field in package.json that matches the name in server.json. If they differ, publishing fails with a "Registry validation failed for package" error. The package types guide covers the equivalent check for the other supported registries.

Remote servers get their own block

If your server runs as a hosted service instead of a package, you describe it with remotes rather than packages. The remote servers guide says a remote must be publicly reachable at its URL, and that it should use the Streamable HTTP transport. SSE is described as deprecated, so you would list an sse endpoint only to keep older clients working, and you can list both at different URLs.

Two features here are genuinely useful for anyone running a multi-tenant agent backend. First, URLs can contain template variables in curly braces, such as https://{tenant_id}.analytics.example.com/mcp, and each variable can carry a description, a default, a list of allowed choices and an isSecret flag. The client asks the user for the value and fills in the URL. Second, a remote can declare headers that the client should send, for example an X-API-Key header marked required and secret. That gives clients a standard way to know a server wants a key before the first request fails. For servers that use full OAuth instead, the flow is still the one we walked through in OAuth for remote MCP servers; the registry entry just tells the client where to connect.

You can also list packages and remotes in the same file. That is a good default if you ship both a local npm build and a hosted endpoint, because it lets the host application pick whichever install method suits it.

Namespaces and how publishing is verified

The name format is tied directly to how you log in. The authentication guide lists two families. With GitHub login, names must start with io.github.<username>/ or io.github.<orgname>/. With domain-based login, names use the reverse-DNS form of a domain you control, so a server owned by example.com would be named com.example/something.

GitHub login is the quick path. You run mcp-publisher login github, open GitHub's device page, type the code the CLI prints, and you are done. Domain login is the one to use for anything with a company name on it, and it comes in two flavors. DNS authentication has you generate an Ed25519 or ECDSA P-384 key pair and publish the public key in a TXT record that starts with v=MCPv1. HTTP authentication puts the same proof string in a file served at /.well-known/mcp-registry-auth on your domain. Both then log in by signing with the private key, and the guide also shows how to keep that key in Google Cloud KMS or Azure Key Vault instead of a local file.

The practical upshot is that a name under com.example/ can only be published by whoever controls example.com's DNS or web root. That is the main trust signal the registry gives you, and it is a good one. It stops a random account from publishing an official-looking server under a brand it does not own, which is a real risk in a world where agents pick tools by name and description.

Publishing in practice

The whole flow for a package-based server is short. Publish your package to npm (or whichever registry) first, since the MCP Registry only points at it. Install the mcp-publisher CLI, which the quickstart offers as a prebuilt binary or through Homebrew. Run mcp-publisher init in your project to generate a starting server.json, edit the versions and remove any template fields you do not need, then run mcp-publisher login and mcp-publisher publish. The quickstart's sample output ends with a line confirming the server name and version, and you can check the listing with a search call against the API.

Two habits make this painless. Keep the version in server.json in lockstep with the package version, because the registry stores each version as its own entry and you will want them to match what users actually install. And automate it: the registry docs include a GitHub Actions guide so publishing happens on release rather than whenever someone remembers.

Reading the registry from an agent platform

On the consuming side, the registry exposes an unauthenticated, read-only REST API at https://registry.modelcontextprotocol.io. The aggregators guide documents three endpoints: GET /v0.1/servers to list servers, GET /v0.1/servers/{serverName}/versions to list one server's versions, and GET /v0.1/servers/{serverName}/versions/{version} for a specific version, where the special version latest gives you the newest. Server names contain a slash, so they must be URL-encoded in the path, for example io.modelcontextprotocol%2Feverything.

When we called the list endpoint on November 11, 2026, each result came back as a server object holding the published server.json, plus a _meta block under the key io.modelcontextprotocol.registry/official with status, publishedAt, updatedAt and an isLatest flag. The list includes every published version, not just the newest, so a naive importer will show the same server several times. Filter on isLatest or group by name.

Paging is cursor-based. You pass limit, read metadata.nextCursor from the response, and send it back as cursor on the next call. For incremental syncs, updated_since takes an RFC 3339 timestamp and returns only entries that changed after it. The guide is clear about the intended usage pattern: aggregators should pull on a regular but infrequent schedule, around once an hour, and keep the data in their own store, because the registry makes no uptime or durability promises.

That last point is the one that matters most if you are tempted to have an agent query the registry live while it works. Don't. The docs say host applications are not meant to consume the official registry directly; they should read from a downstream registry or marketplace that implements the same OpenAPI spec. A sensible setup for an agent platform is a small hourly job that syncs with updated_since, writes to your own table, applies your own allowlist or scanning, and serves that to your agents. Your agents get fast, stable lookups, and you get a place to add the safety checks the registry leaves to you.

Watch the status field

Registry metadata is mostly immutable once published, with one exception: status can change, for example to deprecated or deleted. The aggregators guide says deleted usually means a server broke the moderation policy, which points to spam, malware or illegal content. If you sync the registry, re-check status on every run and pull deleted servers out of anything your agents can install. It is a cheap check, and it is the one signal the registry gives you that a listing has gone bad after you first imported it.

If you run a downstream catalog, the spec also lets you add your own data under _meta, such as ratings or scan results, under a key named after your own registry. That keeps your extras clearly separate from what the publisher claimed.

Where this leaves you

For server authors, the registry is a low-effort way to get a single, verified identity that every compliant client can read, and domain verification is worth the extra setup if the server carries a brand. For agent platforms, it is a clean upstream feed, not a trust layer. Sync it on a schedule, store it yourself, honor the status field, and keep doing your own vetting before an agent gets to call anything. If you are still deciding which tools belong in your stack, our tool directory and the MCP server explainer are good places to start.

Newsletter

The daily agent toolkit.

New tools, MCP servers and frameworks, in one short email every morning. Free, no spam. Sign up and get the free AI agent cost cheat sheet (PDF).