ManyCP is not affiliated with, endorsed by or sponsored by any directory, Docker, OpenAI, Anthropic or GitHub named here. Names are used to say which service a fact is about.
Publishing to the official MCP Registry takes a proof inside your package, a server.json, and three commands. The registry stores metadata only, and points at a package you published elsewhere[8]. It says it is in preview, so breaking changes or data resets may occur[8]. If you only need this one listing, you can do all of it by hand: see when you don't need ManyCP.
Put the proof inside the package
The registry checks that the package really belongs to the name in server.json. The marker has to be inside the version you publish[3].
Package
Proof, with this guide's sample name
npm
"mcpName": "io.github.example-user/weather-mcp" in package.json
PyPI
mcp-name: io.github.example-user/weather-mcp in the README, which PyPI shows as the description
NuGet
mcp-name: io.github.example-user/weather-mcp in the package README
Cargo
mcp-name: io.github.example-user/weather-mcp as visible text in the README (crates.io removes HTML comments)
Docker / OCI
LABEL io.modelcontextprotocol.server.name="io.github.example-user/weather-mcp" in the Dockerfile
Remote server
No package, so no marker. The URL has to be publicly reachable[4].
With GitHub login the name must start with io.github.<your-username>/. A domain you own gives a name that starts with that domain, reversed[2].
The steps
Publish the package first, so the proof is live: npm publish --access public[1].
Install the tool: brew install mcp-publisher, or the release download on the registry's GitHub page[1].
Write server.json. mcp-publisher init makes a template with TODO: placeholders[10]. The next section has a finished one.
Log in: mcp-publisher login github. It prints a code to enter at github.com/login/device[1].
Check it: mcp-publisher validate[10]. Then publish: mcp-publisher publish.
Terminal
mcp-publisher login github
mcp-publisher validate
mcp-publisher publish
# Is it live? (this search form is the quickstart's own check)
curl "https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.example-user/weather-mcp"
This file was built by buildServerJson, the function ManyCP publishes with, for a sample npm server. Posted to the registry's validation endpoint on it returned {"valid":true,"issues":[]}[11].
A remote entry uses remotes with a streamable-http or sse type and a public URL[4]. This one also returned valid on .
Publish from GitHub Actions
The registry's own workflow logs in with GitHub OIDC, so there is no stored secret, and it needs id-token: write[6]. This is the registry-only variant ManyCP puts in a pull request body: it leaves publishing the package to your own release job, waits for the version to appear on npm, and sets the version from the tag.
.github/workflows/publish-mcp.yml
# Publishes this server to the official MCP Registry when you push a tag like v1.2.3.
# Your own release pipeline publishes the package. This only tells the registry about it, with GitHub OIDC (no stored secret).
name: Publish to MCP Registry
on:
push:
tags: ["v*"]
jobs:
publish:
runs-on: ubuntu-latest
permissions:
id-token: write # required for `mcp-publisher login github-oidc`
contents: read
steps:
- name: Checkout
uses: actions/checkout@v5
# The package must be on npm before the registry can check it. Your own release job publishes it.
- name: Wait for the package on npm
run: |
VERSION="${GITHUB_REF_NAME#v}"
for i in $(seq 1 30); do
curl -sfo /dev/null "https://registry.npmjs.org/@example-user%2Fweather-mcp/$VERSION" && exit 0
sleep 10
done
echo "Version $VERSION is not on npm yet." && exit 1
# Keep server.json and the git tag in lockstep (registry versions never change once published).
- name: Sync version from tag
run: |
VERSION="${GITHUB_REF_NAME#v}"
jq --arg v "$VERSION" '.version = $v | (.packages[]? | select(.registryType=="npm") | .version) = $v' \
server.json > server.tmp && mv server.tmp server.json
- name: Install mcp-publisher
run: |
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
- name: Authenticate to MCP Registry (GitHub OIDC)
run: ./mcp-publisher login github-oidc
- name: Validate server.json
run: ./mcp-publisher validate
- name: Publish server to MCP Registry
run: ./mcp-publisher publish
Errors and fixes
The texts below are the registry's own, with this guide's sample names in them. The ones that start with expected, version must and invalid version were returned by the registry's validator on .
Error text
Cause
Fix
Source
NPM package '@example-user/weather-mcp' is missing required 'mcpName' field. Add this to your package.json: "mcpName": "io.github.example-user/weather-mcp"
The npm version in server.json has no mcpName. The registry reads the published copy of that version, not your repo.
Add mcpName to package.json, publish a new npm version, and put that version in server.json.
NPM package '@example-user/weather-mcp' exists, but version '1.0.0' was not found (status: 404). A newly published release can take a moment to appear on the registry. Wait and retry, or publish version '1.0.0' before registering it
The package version is not on npm yet, or has only just been published.
Check the version on npm, wait a minute and publish again.
PyPI package 'weather-mcp' ownership validation failed. The server name 'io.github.example-user/weather-mcp' must appear as 'mcp-name: io.github.example-user/weather-mcp' in the package README
The README that PyPI shows for this version has no mcp-name line.
Add the line mcp-name: io.github.example-user/weather-mcp to the README and publish a new PyPI version.
PyPI package 'weather-mcp' ownership validation failed: found 'mcp-name: io.github.example-user/weather-mcp' in the README, but it is immediately followed by "." rather than a boundary. The token must be followed by a space, newline, an HTML tag, or a comment close ('-->') — put it on its own line and republish
Punctuation is glued to the end of the name.
Put the mcp-name line on its own line and publish a new version.
OCI image 'docker.io/example-user/weather-mcp:1.0.0' is missing required annotation. Add this to your Dockerfile: LABEL io.modelcontextprotocol.server.name="io.github.example-user/weather-mcp"
The image has no ownership label.
Add the LABEL line, rebuild, push a new tag and use that tag in the identifier.
What ManyCP adds: ManyCP builds the server.json, checks it with the registry, and publishes with your GitHub sign-in. If your package is missing the mcpName proof, it opens a pull request on your own repo to add it. It does not publish your npm or PyPI package for you. A new package version, with the proof in it, is yours to release. For the rest of the list, see all 26 directories compared, or run the free checker first.