How to publish an MCP server to the official MCP Registry

LLM Mart · Sep 21, 2026 · 2 views 194 listing impressions
How to publish an MCP server to the official MCP Registry

Publishing to the official MCP Registry means submitting metadata, not code. You publish your package to npm, PyPI, or a container registry first; then you verify ownership of a namespace, describe the server in a server.json file, and submit an immutable version with the mcp-publisher CLI.

The registry is currently in preview: breaking changes or data resets may occur before general availability. Publish to it, but do not treat it as the only place your server is discoverable.

What the registry is, and is not

The MCP Registry is a centralised metadata repository for publicly accessible MCP servers, backed by contributors including Anthropic, GitHub, PulseMCP, and Microsoft. It provides a single publishing point, namespace management through DNS and GitHub verification, a REST API for clients and aggregators, and standardised installation information.

Three boundaries are worth understanding before you start.

It stores metadata, not artifacts. Package registries — npm, PyPI, Docker Hub — host the code. The registry hosts the record that maps "weather v1.2.0" to npm:weather-mcp. Your package has to exist before its metadata can.

It does not scan your code. Security scanning is delegated to the underlying package registries and to downstream aggregators. A registry entry is evidence of namespace ownership, not of safety. That is worth saying out loud to your own users, and it is why installing anything still calls for vetting the server yourself.

It does not host private servers. A server qualifies if its installation method is publicly available or the server itself is publicly reachable. Servers on private networks or private package registries do not belong there; host your own registry instead.

It is also not meant to be consumed directly by host applications. The expected pattern is that downstream aggregators and marketplaces pull from the registry API on a regular but infrequent basis and add curation, ratings, and richer metadata on top.

Before you publish

You need four things in place:

  1. A working server. Tested against its own tool list, not just launched once. If you are still building, start with how to build an MCP server in TypeScript.
  2. A published package, or a publicly reachable URL for a remote server.
  3. A namespace you can prove you own — a GitHub account or a domain.
  4. A version string you will never reuse. Published versions are immutable.

Step 1: Add verification metadata to the package

The registry verifies that a server's underlying package matches its metadata. For npm packages, that means adding an mcpName property to package.json:

{
  "name": "@my-username/mcp-weather-server",
  "version": "1.0.1",
  "mcpName": "io.github.my-username/weather",
  "repository": {
    "type": "git",
    "url": "https://github.com/my-username/mcp-weather-server.git"
  }
}

The value of mcpName becomes your server's name in the registry, and it must match the namespace you authenticate with. With GitHub authentication it must start with io.github.<your-username>/.

Other package types have their own equivalent verification step; check the package-types guide for the registry you are publishing to.

Step 2: Publish the package first

Because the registry only stores metadata, the artifact has to exist before the record can point at it.

npm install
npm run build
npm adduser          # if not already authenticated
npm publish --access public

Confirm the package is live at its public URL before continuing. A registry submission that points at a package that does not exist yet fails validation rather than waiting for it.

Step 3: Install mcp-publisher

# macOS / Linux, via Homebrew
brew install mcp-publisher

# Or a pre-built binary
curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" \
  | tar xz mcp-publisher && sudo mv mcp-publisher /usr/local/bin/

mcp-publisher --help should list four commands: init, login, logout, and publish.

Step 4: Create server.json

mcp-publisher init generates a template with fields derived from your project.

{
  "$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 in server.json must match the mcpName in package.json. That pair is the link the registry checks.

Delete template fields you do not need. The generated file includes an environmentVariables example; leaving a placeholder YOUR_API_KEY entry in a server that needs no credential is a small thing that makes your listing look careless and makes installation confusing.

Where a server does need configuration, describe it properly — each variable takes a description, isRequired, format, and isSecret. Marking a credential isSecret is how a host knows not to display or log it.

Step 5: Authenticate the namespace

mcp-publisher login github

This starts a device flow: visit the printed URL, enter the code, authorise, and return to the terminal.

DNS authentication is the alternative and the one to use for a custom domain prefix such as com.example/analytics. It proves control of the domain rather than a GitHub account, and it is what makes an organisation's servers recognisably theirs.

This namespace system is the registry's actual trust mechanism: reverse-DNS names tie a server to a verified GitHub account or domain, so only the legitimate owner can publish under that prefix.

Step 6: Publish

mcp-publisher publish

Verify through the API rather than trusting the success message:

curl "https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.my-username/weather"

Versioning rules that will bite you

Every publication needs a unique version string, and once published, the version and its metadata cannot be changed. There is no edit. There is only a new version.

Semantic versioning is recommended but not required. The registry parses versions as semver for sorting and marks the newest appropriately; if parsing fails, that version is always marked "latest."

Example Type Guidance
1.0.0 semantic version Recommended
2.1.3-alpha, 1.0.0-beta.1 semantic prerelease Recommended
2025.11.25 semantic date Recommended
2025.06.18 non-semantic date Allowed, with caution
v1.0 prefixed Allowed
^1.2.3, 1.x, >=1.2.3 version range Prohibited

Three traps follow from those rules.

Mixing schemes promotes the wrong release. If you use semver and then publish something that is not valid semver, the new version is marked "latest" even when it would sort earlier.

Prerelease versions sort before their release. Publishing 1.2.3-1 after 1.2.3 means the prerelease is not marked latest — which is either what you wanted or a silent no-op.

Metadata-only updates need a version anyway. If you expect to republish without changing the package — to fix a description, add an environment variable, correct a URL — use prerelease versions such as 1.2.3-1 for those, and keep the package version field pointing at the real package release.

Align versions deliberately: for local servers, match the server version to the package version; for remote servers with an API version, match the server version to the API version; with multiple packages, use the server version as the overall release number.

Publishing a remote server

A remote server is published through remotes instead of packages, and must be publicly accessible at its URL:

{
  "name": "com.example/acme-analytics",
  "version": "2.0.0",
  "remotes": [
    { "type": "streamable-http", "url": "https://analytics.example.com/mcp" }
  ]
}

Use streamable-http. The SSE transport is deprecated; publish an "sse" remote only to support existing clients, and plan its removal. A server can expose both at different URLs during a migration.

Two remote-only features are worth knowing:

URL template variables support multi-tenant deployments. A {tenant_id} or {region} placeholder with a variables block — including description, isRequired, choices, and default — lets one entry describe many endpoints, and the host prompts the user to fill it in.

Declared headers tell clients which HTTP headers to send, with isRequired and isSecret flags. Use this for API-key authentication; for OAuth, see MCP authentication explained.

remotes and packages can coexist in one entry, letting the host application offer the user a choice between a hosted endpoint and a local install.

Troubleshooting

Error What to do
"Registry validation failed for package" The package is missing its verification metadata — check mcpName in package.json and that it matches name in server.json.
"Invalid or expired Registry JWT token" Re-run mcp-publisher login github.
"You do not have permission to publish this server" Your namespace does not match your authentication method. With GitHub auth, the name must start with io.github.<your-username>/.

After publishing

Automate it. Publishing by hand from a laptop means the registry drifts from your releases, and drift in an immutable record is not recoverable — only appendable. Wire mcp-publisher into the same CI job that publishes the package, so the two always move together.

Then treat the registry as one distribution channel rather than the destination. It is deliberately unopinionated: no ratings, no curation, no review. The richer discovery layer is downstream, in marketplaces and catalogues that add the context the registry leaves out.

Next step: Publish verified MCP metadata, then submit the reviewed server to LLM Mart for richer discovery and trust signals — the registry proves who you are, the catalogue explains what the server is for.

Sources

0 0 0 0 Sign in to react

Comments (0)

Sign in to join the conversation.

No comments yet.