createmcps.com

Guide

How to publish an MCP server to the official registry

Last verified: 10 August 2026 · spec 2026-07-28 · ruleset 1.4.2

Five steps: install mcp-publisher, add an ownership marker to your package, generate server.json, authenticate for the namespace you want, publish. Two of those five are where publishes actually fail — the ownership marker and the namespace match — so they get the most room below.

The whole flow, in one block

If you already know the steps
# 1. Install
brew install mcp-publisher

# 2. In package.json — must match server.json's "name"
#    "mcpName": "io.github.you/weather"
npm publish

# 3. Generate and edit server.json
mcp-publisher init

# 4. Authenticate for your namespace
mcp-publisher login github          # io.github.you/*
# or: mcp-publisher login dns --domain example.com --private-key …

# 5. Publish
mcp-publisher publish
The two steps that fail: Step 2 — the mcpName in your package must match server.json's name exactly, and the package must already be published where the registry can read it. Step 4 — the namespace you authenticate for must contain the name you are publishing. Everything else is mechanical.

1. Install mcp-publisher

Homebrew
brew install mcp-publisher
macOS / Linux, without Homebrew
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/
Windows (PowerShell)
$arch = if ([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture -eq "Arm64") { "arm64" } else { "amd64" }
Invoke-WebRequest -Uri "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_windows_$arch.tar.gz" -OutFile "mcp-publisher.tar.gz"
tar xf mcp-publisher.tar.gz mcp-publisher.exe
rm mcp-publisher.tar.gz
Confirm
mcp-publisher --help

2. Prove you own the package

This is the step people skip, and it is the one that makes a publish fail with an ownership error that reads like an authentication problem. The registry has to confirm that whoever is registering io.github.you/weather also controls the npm package that entry points at — otherwise anyone could publish a registry entry aimed at someone else's code.

The proof is a marker inside the package itself:

npm — package.json
{
  "name": "@my-username/mcp-weather-server",
  "mcpName": "io.github.my-username/weather"
}

For PyPI and NuGet, the equivalent marker goes in the package README as mcp-name: <server-name>, or the same string inside an HTML comment.

The exact rule: The name property in server.json must match the mcpName property in package.json. Not similar — identical. And the package must already be published, because the registry reads the marker from the published artifact rather than from your working directory.
So the order matters
# 1. Add mcpName to package.json
# 2. npm publish            ← the marker becomes readable
# 3. mcp-publisher publish  ← the registry can now verify it

# Publishing to the MCP registry before npm fails:
# the registry fetches your package and finds no marker.

Only official package registries are accepted, at fixed URLs — npm at registry.npmjs.org, PyPI at pypi.org, NuGet at api.nuget.org/v3/index.json, Cargo at crates.io, plus specific container registries. Private registries are explicitly disallowed, for the same reason: the registry cannot verify a marker it cannot read.

Our recommendation, not a spec quote: a remote-only server sidesteps this entirely. If your server.json declares remotes rather than packages, there is no package to own and no marker to add — the endpoint URL and your namespace are the whole claim.

3. Generate and edit server.json

MCP-REG-001
Scaffold it
mcp-publisher init

Then fill it in. A minimal, complete file for an npm-packaged server:

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

$schema, name, description and version are required, and at least one of packages or remotes must be present. Every field is covered in the server.json reference.

Check it before you publish
mcp-publisher validate
The MCP Registry recommends semantic versioning, but supports any version string format.
MCP-REG-003 MAY · spec 2026-07-28

4. Authenticate for the namespace you want

MCP-REG-002

The registry grants namespaces, not blanket write access. Which command you run determines which names you may publish:

CommandNamespaceUse when
login githubio.github.name/*Publishing by hand
login github-oidcio.github.name/*GitHub Actions — no secret needed
login dnscom.example.*/*Your own domain
login httpcom.example.*/*Own the domain, not the DNS
Publishers must verify ownership of their namespace through GitHub, DNS, or HTTP challenges, preventing arbitrary spam submissions.
MCP-REG-002 MAY · spec 2026-07-28
Organization namespaces need Owner role: Publishing under io.github.orgname/* requires Owner role in that organization. Ordinary membership is not sufficient — the registry checks your role, so that anyone who merely belongs to an org cannot publish or overwrite servers under the org's name.

For a custom domain, the full walkthrough is verify a DNS namespace. If login fails, every known cause is here.

5. Publish, then verify it landed

MCP-REG-004
Publish
mcp-publisher publish
Confirm from the outside
curl "https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.my-username/weather"

One more thing the registry checks that is easy to miss: if you declared a remote endpoint, it has to actually be there.

A remote server MUST be publicly accessible at its specified URL.
MCP-REG-004 MAY · spec 2026-07-28
Our recommendation, not a spec quote: publish the package and the registry entry in the same release step, not in separate manual runs. The version in server.json, the version in package.json, and the tag you cut should move together — a registry entry pointing at a package version that was never published is a broken install for anyone who finds you.

Updating, deprecating and removing

Publishing a new version is the same command with a bumped version. Changing a server's lifecycle state is a separate subcommand:

Lifecycle
# New version — bump "version" in server.json first
mcp-publisher publish

# Mark deprecated, with a note for anyone who finds it
mcp-publisher status --status deprecated \
  --message "Superseded by io.github.you/weather-v2" \
  io.github.you/weather

# Remove
mcp-publisher status --status deleted --all-versions --yes io.github.you/weather
Our recommendation, not a spec quote: prefer deprecated with a message over deleted. A deleted entry leaves anyone who already depends on you with nothing to follow; a deprecated one with a pointer is the difference between a migration and a dead end.

What we check about a published server

Publishing successfully means the registry accepted your metadata. It says nothing about whether the server behind it conforms to the protocol. These four registry rules are what we check from the outside:

RuleWhat we checkLevelIf it fails
MCP-REG-001server.json present and valid against the registry schemaMAYwarn
MCP-REG-002Namespace ownership is verifiable (GitHub or DNS TXT)MAYwarn
MCP-REG-003version is present and valid semverMAYwarn
MCP-REG-004Declared remote endpoint matches the probed URLMAYwarn

All four carry verbatim quotes from registry documentation.

Check the server, not just the listing

A published entry is discoverable. Whether it works for the clients that find it is a different question — run the endpoint through the validator before you announce it.

Validate a server →

Frequently asked

How do I publish an MCP server to the official registry?

Install the mcp-publisher CLI, add an ownership marker to your package (mcpName in package.json for npm), run mcp-publisher init to generate server.json, authenticate with mcp-publisher login github or login dns for a custom domain, then run mcp-publisher publish. The whole flow is five commands once the markers are in place.

What is mcpName in package.json?

An ownership marker proving the same person controls both the npm package and the registry namespace. The mcpName value in package.json must match the name property in server.json exactly — that pairing is what stops someone publishing a registry entry that points at a package they do not own.

Do I need to publish to npm before the MCP registry?

If your server.json declares an npm package, yes — the registry verifies the ownership marker by reading the published package. Publish to npm first with mcpName already in package.json, then publish to the MCP registry. A remote-only server declaring remotes rather than packages does not need a package registry at all.

Which package registries does the MCP registry accept?

Only official ones, at fixed URLs: npm at registry.npmjs.org, PyPI at pypi.org, NuGet at api.nuget.org/v3/index.json, and Cargo at crates.io, plus specific container registries. Private registries are explicitly disallowed, because the registry cannot verify ownership on a host it cannot read.

Can I publish under my organization's namespace?

Yes, but publishing under io.github.orgname/* requires Owner role in that organization — ordinary membership is not enough. The registry checks your membership role so that anyone who merely belongs to an org cannot publish or overwrite servers under the org's name.

How do I update or unpublish a server?

Publish a new version by bumping the version field in server.json and running mcp-publisher publish again. To change a server's lifecycle state, use mcp-publisher status --status with active, deprecated or deleted, optionally with --message and --all-versions.

Sources

Verified on 10 August 2026 against: