The MCP Registry is the official catalog of metadata about publicly available MCP servers, hosted at registry.modelcontextprotocol.io. It does not store code: each entry is a server.json document that names the server, describes it and says where to get it, in a package registry such as npm or PyPI or at the address of a remote server. Publishing a server means sending its server.json to the registry with the mcp-publisher command-line tool after proving ownership of the server's name.
The registry started in February 2025 as a community project, when the authors of MCP, David Soria Parra and Justin Spahr-Summers, asked the PulseMCP and Goose teams to help build a central registry. It was launched in preview on 8 September 2025. The registry is developed by the Registry Working Group of the MCP project.
As of November 2026 its documentation still marks it as a preview: breaking changes or data resets may occur before general availability. The API version v0.1 has been frozen since 24 October 2025, and the current published version of the server.json schema is 2025-12-11. Basic concepts of the protocol are described in the article What is MCP.
Overview
| Property | Value |
|---|---|
| Type | Official catalog of metadata about public MCP servers, with a REST API |
| Address | https://registry.modelcontextprotocol.io |
| Status | Preview since 8 September 2025 |
| API | v0.1, frozen since 24 October 2025; read access without authentication |
| Entry format | server.json, schema version 2025-12-11 |
| Server names | Reverse-DNS namespace and server name, for example io.github.user/weather |
| Package types | npm, PyPI, NuGet, Cargo, Docker/OCI images, MCPB |
| Publishing tool | mcp-publisher CLI |
| Developer | Registry Working Group of the MCP project |
The problem it solves
An MCP server is distributed in a package registry or runs at a remote address. To find it, a user or an application needs a catalog. To install it, the catalog needs the exact package name, transport and settings.
The announcement of the registry describes its aim as standardizing how servers are distributed and discovered. The registry is meant as a primary source of truth: a single place where the maintainers of a server publish self-reported information about it. Other catalogs, called subregistries, take the data from it and add their own, such as curation, ratings and security checks.
The registry also ties each name to a verified GitHub account or domain, so a server cannot be published under someone else's name. The package that an entry points to must name the server, so the metadata cannot claim a foreign package.
History
The table lists the launch of the registry and the main changes of its API and of the server.json schema.
| Date | Event |
|---|---|
| February 2025 | Start of the registry as a community project |
| 9 July 2025 | First version of the server.json schema |
| 8 September 2025 | Launch of the official MCP Registry in preview |
| 16 September 2025 | Schema 2025-09-16: field names change from snake_case to camelCase (registryType, environmentVariables) |
| 29 September 2025 | Schema 2025-09-29: the status and registry metadata leave server.json and are managed by the registry |
| 11 October 2025 | Schema 2025-10-11: the package format depends on the package type; version is optional for OCI and MCPB |
| 24 October 2025 | API freeze at version v0.1: no breaking changes while integrators implement support |
| 11 December 2025 | Schema 2025-12-11: URL template variables for remote servers |
Place in the ecosystem
The diagram below shows the chain from code to host applications. According to the documentation, the official registry is not intended to be consumed by host applications directly: they read subregistries that implement the same API.
Package registries (npm, PyPI, NuGet, crates.io, Docker Hub, GitHub Releases)
| code and binaries
v
MCP Registry (registry.modelcontextprotocol.io)
| server.json metadata, REST API v0.1
v
Subregistries and marketplaces (public or private)
| curation, ratings, security scans, same API
v
Host applications (AI applications that install and connect servers)According to the documentation, the registry is intended primarily for aggregators. It is deliberately unopinionated: it does not rate servers and does not scan their code. Security scanning is left to the package registries and to downstream aggregators.
The registry accepts open-source and closed-source servers, provided that the package is in a public registry or the remote server is publicly accessible. Private servers, available only in a private network or a private package registry, are not accepted. For them the documentation recommends a private registry.
The server.json format
Fields
server.json is a JSON document described by a JSON Schema. Its address contains the schema version, and the document refers to it in the $schema field. The main fields:
| Field | Required | Content |
|---|---|---|
$schema |
no | URL of the schema, for example the 2025-12-11 version |
name |
yes | Unique name in reverse-DNS form, 3 to 200 characters, exactly one / |
description |
yes | What the server does, 1 to 100 characters |
version |
yes | Version of this publication, up to 255 characters; ranges are not allowed |
title |
no | Display name, up to 100 characters |
repository |
no | Source code: url, source (for example github), optional subfolder |
websiteUrl |
no | Home page or documentation |
icons |
no | Icons for user interfaces |
packages |
no | Packages: registryType, identifier, version, transport and settings |
remotes |
no | Remote endpoints: type and url, optional headers and variables |
_meta |
no | Additional metadata of the publisher |
A package entry requires registryType, identifier and transport. The type of the transport of a package is stdio, streamable-http or sse. A package can also describe how to start it: runtimeHint, runtimeArguments, packageArguments and environmentVariables, each variable with name, description, isRequired and isSecret.
Example
The example describes a server that is distributed as an npm package and is also available as a remote server:
{
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
"name": "io.github.example-user/weather",
"title": "Weather",
"description": "Current weather and forecasts for any city.",
"version": "1.2.0",
"repository": {
"url": "https://github.com/example-user/mcp-weather-server",
"source": "github"
},
"packages": [
{
"registryType": "npm",
"identifier": "@example-user/mcp-weather-server",
"version": "1.2.0",
"transport": {
"type": "stdio"
},
"environmentVariables": [
{
"name": "WEATHER_API_KEY",
"description": "API key of the weather data provider",
"isRequired": true,
"isSecret": true
}
]
}
],
"remotes": [
{
"type": "streamable-http",
"url": "https://weather.example.com/mcp"
}
]
}Publisher metadata
The _meta field uses reverse-DNS keys. The official registry keeps only the data under the key io.modelcontextprotocol.registry/publisher-provided and silently drops other keys. This data is limited to 4096 bytes of JSON; a larger block makes the publication fail.
The registry adds its own metadata in API responses, outside server.json, under the key io.modelcontextprotocol.registry/official: the status, the dates of publication and update, and the isLatest flag. Publishers cannot set these fields.
Namespaces
The name of a server consists of a namespace and the name of the server within it, separated by /. The namespace is written in reverse-DNS form: the parts of a domain in reverse order. The way the publisher authenticates determines which namespaces it can use.
| Authentication | Namespace | Example |
|---|---|---|
| GitHub (OAuth or OIDC) | io.github.<username>/* or io.github.<orgname>/* |
io.github.alice/weather-server |
| DNS | com.example/* and namespaces of subdomains, com.example.*/* |
com.example.api/analytics |
| HTTP | com.example/* only |
com.example/analytics |
GitHub authentication always grants the personal namespace. An organization namespace requires the role of Owner in that organization; ordinary membership is not enough. As of November 2026, the documentation notes that mcp-publisher login github cannot publish to an organization namespace, while login through OIDC or with a personal access token can.
A domain namespace is proved with DNS or HTTP. In the code of the registry, DNS verification grants the domain and all its subdomains, and HTTP verification only the domain itself. Domains github.io and its subdomains cannot be used with DNS or HTTP verification: the io.github.* namespaces are available only through GitHub authentication.
Ownership verification
Before publishing, the publisher logs in to the registry with mcp-publisher login and receives a token of the registry. The method of login proves control of the namespace.
- GitHub OAuth.
mcp-publisher login githubstarts the device flow: the user openshttps://github.com/login/deviceand enters the code printed in the terminal. With the--tokenoption, a personal access token is used instead. - GitHub OIDC.
mcp-publisher login github-oidcis used in GitHub Actions. The workflow needs theid-token: writepermission and no separate secret. - DNS. The publisher generates a key pair (Ed25519 or ECDSA P-384) and publishes the public key in a TXT record at the apex of the domain, not under a selector such as
_mcp-auth. The login command signs a timestamp with the private key. - HTTP. The same record,
v=MCPv1; k=ed25519; p=<public key>, is served as a file athttps://<domain>/.well-known/mcp-registry-auth. The registry fetches it from its own infrastructure, does not follow redirects and expects status 200 within 10 seconds.
DNS verification with an Ed25519 key, following the documentation of the registry:
MY_DOMAIN="example.com"
# Generate an Ed25519 key pair
openssl genpkey -algorithm Ed25519 -out key.pem
# Print the TXT record to add at the apex of the domain
PUBLIC_KEY="$(openssl pkey -in key.pem -pubout -outform DER | tail -c 32 | base64)"
echo "${MY_DOMAIN}. IN TXT \"v=MCPv1; k=ed25519; p=${PUBLIC_KEY}\""
# After the record has propagated, log in with the private key
PRIVATE_KEY="$(openssl pkey -in key.pem -noout -text | grep -A3 "priv:" | tail -n +2 | tr -d ' :\n')"
mcp-publisher login dns --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}"The private key can also stay in Google Cloud KMS or Azure Key Vault; mcp-publisher has login variants for both.
Package verification
The registry also checks that the package referenced in server.json belongs to the publisher of the name. Each package type has its own marker that must contain the server name exactly:
registryType |
Supported registry | Ownership marker |
|---|---|---|
npm |
registry.npmjs.org |
mcpName field in package.json |
pypi |
pypi.org |
String mcp-name: <server name> in the README, may be in an HTML comment |
nuget |
api.nuget.org |
String mcp-name: <server name> in the README, may be in an HTML comment |
cargo |
crates.io |
String mcp-name: <server name> as visible text in the README; crates.io removes HTML comments |
oci |
Docker Hub, GHCR, Quay.io, Google Artifact Registry, Azure Container Registry, Microsoft Container Registry | Annotation io.modelcontextprotocol.server.name of the image |
mcpb |
GitHub and GitLab releases | URL contains "mcp"; fileSha256 with the SHA-256 of the file is required |
For an npm package the marker looks like this:
{
"name": "@example-user/mcp-weather-server",
"version": "1.2.0",
"mcpName": "io.github.example-user/weather"
}For an OCI image the annotation is set with a LABEL instruction in the Dockerfile, for example LABEL io.modelcontextprotocol.server.name="io.github.example-user/weather". The registry does not check the hash of an MCPB file; according to the documentation, MCP clients check it before installation.
Only these public registries are accepted. Private package registries and mirrors are not supported by the official registry.
Remote servers
A remote server is described in the remotes array. Such an entry needs no packages, and package verification does not apply to it. The URL must be publicly accessible.
- Transport.
typeisstreamable-httporsse. The documentation recommends Streamable HTTP and describes SSE as deprecated, to be published only for existing clients. - URL variables. The URL can contain variables in curly braces, such as
https://{tenant_id}.analytics.example.com/mcp, with a description, a default value and a list of choices invariables. The user supplies the values when configuring the server. - Headers.
headerslists HTTP headers that the client must send, for example an API key, with the flagsisRequiredandisSecret.
The registry checks control of the namespace, not of the URL. Each remote URL can belong to only one server name: a publication with a URL already used by another server fails. packages and remotes can be combined, and the host application then chooses the way to install.
Publishing with mcp-publisher
mcp-publisher is installed as a pre-built binary from the releases of the modelcontextprotocol/registry repository or with Homebrew. Its commands:
| Command | What it does |
|---|---|
mcp-publisher init |
Creates a server.json template from the project files |
mcp-publisher validate |
Checks server.json against the schema and the rules of the registry without publishing |
mcp-publisher login <method> |
Logs in: github, github-oidc, dns, http; none only for a local registry |
mcp-publisher publish |
Sends server.json to the registry |
mcp-publisher status |
Changes the status of a version or of all versions: active, deprecated, deleted |
mcp-publisher logout |
Deletes the stored token |
A typical publication of an npm-based server consists of publishing the package and then its metadata:
# The package goes to npm first: the registry stores only metadata
npm publish --access public
# Create and check server.json
mcp-publisher init
mcp-publisher validate
# Log in with GitHub and publish
mcp-publisher login github
mcp-publisher publishDuring publication the registry validates the document, checks the namespace against the token and verifies the ownership of each package.
Publication can be automated with GitHub Actions. The documentation recommends OIDC, pinning a specific release of mcp-publisher and verifying its Sigstore signature before use. Running tests in such pipelines is described in the article Automated tests in CI.
Versions and statuses
Each publication has its own version, unique for the server. A published version cannot be changed: to correct metadata, a new version is published. The registry recommends semantic versioning but accepts any string, except strings that look like ranges, such as ^1.2.3, ~1.2.3, >=1.2.3 or 1.x.
The registry marks a version as the latest (isLatest) by comparing semantic versions. A version that cannot be parsed as a semantic version is always marked as the latest. To change only the metadata without a new package, it recommends a prerelease version such as 1.2.3-1; such a version sorts before 1.2.3 and does not become the latest if published after it.
The status of a version is the only field that changes after publication:
| Status | Meaning |
|---|---|
active |
The server is visible in default listings |
deprecated |
The server stays visible, with a message such as "Please upgrade to 2.0.0" |
deleted |
The server is hidden from default listings; the metadata remains available with include_deleted=true |
Metadata is never removed completely. A deleted server can be restored by setting the status back to active.
Registry API
The API of the official registry is open for reading without authentication. The main endpoints:
| Endpoint | What it returns |
|---|---|
GET /v0.1/servers |
List of servers, one record per version; parameters search, version, updated_since, include_deleted, limit, cursor |
GET /v0.1/servers/{serverName}/versions |
All versions of a server |
GET /v0.1/servers/{serverName}/versions/{version} |
One version; latest returns the latest one |
Server names in the path are URL-encoded: io.github.example-user%2Fweather. The list uses cursor pagination: the response contains metadata.nextCursor, which is passed as cursor in the next request. version=latest returns only the latest version of each server. updated_since with an RFC 3339 timestamp returns the servers changed since then and is used for incremental synchronization.
The documentation expects aggregators to read the registry on a regular but infrequent schedule, for example once per hour, and to keep the data in their own storage. The official registry gives no guarantees of availability or data durability.
Subregistries and private registries
The registry defines an OpenAPI specification of its API. A subregistry is an aggregator that implements this specification, so host applications can read it in the same way as the official registry. A subregistry can add its own metadata, such as ratings, download counts or scan results, under its own key in _meta, for example com.example.subregistry/custom.
Private registries of companies implement the same specification for their internal servers. In the specification, the publication endpoints are optional, and a registry can be read-only. A registry that requires authentication follows the MCP authorization specification and acts as an OAuth 2.1 resource server; the recommended scopes are mcp-registry:read and mcp-registry:write. The official registry itself uses its own JWT-based tokens for publication.
The code of the official registry is open, but according to its documentation it is not designed for self-hosting, and the maintainers do not support such use. An organization that forks it maintains and operates the fork on its own.
Moderation
The moderation policy of the official registry is permissive. The registry removes illegal content, malware, spam and servers that do not work. Spam includes, for example, the same server published under different names.
The registry does not remove low-quality or buggy servers, servers with security vulnerabilities, duplicates of other servers or servers with adult content. The policy states that the registry makes no guarantees about moderation and relies on package registries and subregistries for deeper checks. A removed server gets the status deleted, and its metadata remains available through the API; in extreme cases, such as unlawful metadata, it can be overwritten or erased. Abuse is reported to the package registry and in an issue of the registry repository whose title starts with "Abuse report:".
The registry, package registries and subregistries
The table compares the MCP Registry with the two kinds of systems it works with.
| Package registry | MCP Registry | Subregistry or marketplace | |
|---|---|---|---|
| What it stores | Code and binaries | Metadata: name, description, packages, endpoints | Copy of the metadata plus its own data |
| Who publishes | Package author | Server publisher, with verified namespace and package | Takes data from the MCP Registry |
| Curation | Own scanning and policies | Minimal: illegal content, malware, spam, broken servers | Ratings, scans, selection |
| Consumers | Package managers | Aggregators and subregistries | Host applications and users |
| Example | npm, PyPI, Docker Hub | registry.modelcontextprotocol.io |
GitHub MCP Registry, a private company registry |
The MCP Registry does not replace a package registry. A package is published first, in its own registry, and the MCP Registry refers to it. The ownership markers connect the two: the package names the server, and the server names the package.
A subregistry does not replace the MCP Registry either: it takes the data from it and applies its own selection. Testing a server before publication is described in the article Testing MCP servers, and regression checks between versions in the article Regression testing of an MCP server.
Terms
| Term | Meaning |
|---|---|
| MCP Registry | Official catalog of metadata about publicly available MCP servers at registry.modelcontextprotocol.io |
server.json |
JSON document that describes an MCP server: name, description, version, packages, remote endpoints |
| Server namespace | Part of the server name before /, in reverse-DNS form, tied to a GitHub account or a domain |
| Package ownership marker | Value in a package, such as mcpName or mcp-name:, that names the server and links the package to it |
mcp-publisher |
Official CLI for creating, validating and publishing server.json |
| Remote server entry | Element of remotes with the transport type and the public URL of a server |
| Subregistry | Aggregator that copies data from the MCP Registry, adds its own and implements the same API |
| Private registry | Registry of an organization that implements the registry API for internal servers |
Sources
- The MCP Registry, modelcontextprotocol.io(opens in a new tab)
- Quickstart: Publish an MCP Server to the MCP Registry(opens in a new tab)
- How to Authenticate When Publishing to the Official MCP Registry(opens in a new tab)
- MCP Registry Supported Package Types(opens in a new tab)
- Publishing Remote Servers(opens in a new tab)
- Versioning Published MCP Servers(opens in a new tab)
- How to Automate Publishing with GitHub Actions(opens in a new tab)
- MCP Registry Aggregators(opens in a new tab)
- The MCP Registry Moderation Policy(opens in a new tab)
- Frequently Asked Questions, MCP Registry(opens in a new tab)
- MCP Registry repository, GitHub(opens in a new tab)
- Server JSON Schema Changelog, GitHub(opens in a new tab)
- Official Registry server.json Requirements, GitHub(opens in a new tab)
- Publisher CLI Commands Reference, GitHub(opens in a new tab)
- Generic Registry API Specification, GitHub(opens in a new tab)
- Registry Authorization, GitHub(opens in a new tab)
- Introducing the MCP Registry, MCP blog, 8 September 2025(opens in a new tab)