wikis / Model Context Protocol (MCP) / wiki / entities / publishing-to-registry.md view as markdown report a mistake
Overview
Publishing to the MCP Registry means pushing a server.json describing your server, using the official mcp-publisher CLI. Because the registry only hosts metadata, you first publish the underlying package to its package registry (npm, PyPI, etc.), then authenticate (which determines your namespace), then publish the server.json. This page covers the operational how-to: install, authenticate, publish, automate in CI, and version.
Characteristics
- Two publishes, in order: package to npm/PyPI/etc. first, then
server.jsonto the MCP Registry. - Namespace is set by your auth method. GitHub auth → name must be
io.github.username/*(orio.github.orgname/*). Domain auth →com.example.*/*(reverse-DNS of your domain). - Name must match everywhere. The
server.jsonnamemust equal the package's ownership marker (mcpNameinpackage.jsonfor npm; anmcp-name: $SERVER_NAMEstring in the README for PyPI/NuGet). mcp-publishercommands:init(create aserver.jsontemplate),login(authenticate),logout(clear auth),publish(pushserver.json).
How to Use
Install mcp-publisher (pre-built binary or Homebrew):
# macOS/Linux
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/
brew install mcp-publisher
Publish an npm-based server (end to end):
# 1. Add the ownership marker to package.json: "mcpName": "io.github.my-username/weather"
# 2. Build and publish the package to npm first
npm install
npm run build
npm adduser # if not authenticated
npm publish --access public
# 3. Generate and edit server.json
mcp-publisher init
# 4. Authenticate (GitHub device flow) — sets your namespace
mcp-publisher login github
# 5. Publish the server metadata
mcp-publisher publish
mcp-publisher init produces a template like:
{
"$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 mcpName in package.json. Verify with curl "https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.my-username/weather".
Authentication methods
mcp-publisher login supports several methods; each maps to a namespace:
- GitHub (device OAuth):
mcp-publisher login github→io.github.username/*. - GitHub OIDC (for CI):
mcp-publisher login github-oidc(no dedicated secret needed). - GitHub PAT (for CI):
mcp-publisher login github --token $TOKEN(PAT needsread:organdread:userscopes). - DNS:
mcp-publisher login dns --domain example.com --private-key $KEY— proves domain ownership via av=MCPv1; k=ed25519; p=...TXT record (Ed25519 or ECDSA P-384; Google KMS / Azure Key Vault supported). - HTTP:
mcp-publisher login http --domain example.com --private-key $KEY— proves ownership via a/.well-known/mcp-registry-authfile hosting the samev=MCPv1;...record.
Publishing a remote (hosted) server
Use the remotes property instead of (or alongside) packages. A remote server MUST be publicly accessible at its URL:
{
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
"name": "com.example/acme-analytics",
"version": "2.0.0",
"remotes": [
{ "type": "streamable-http", "url": "https://analytics.example.com/mcp" }
]
}
type is "streamable-http" (recommended) or "sse"; a server can offer both at different URLs. remotes supports {curly_brace} URL template variables (with description, isRequired, choices, default, isSecret) for multi-tenant/regional endpoints, and a headers array to instruct clients to send specific HTTP headers (e.g. X-API-Key). remotes may coexist with packages so hosts can pick an install method. See local vs remote servers.
Automating with GitHub Actions
Create .github/workflows/publish-mcp.yml triggered on version tags. The recommended OIDC variant:
name: Publish to MCP Registry
on:
push:
tags: ["v*"] # Triggers on version tags like v1.0.0
jobs:
publish:
runs-on: ubuntu-latest
permissions:
id-token: write # Required for OIDC authentication
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Set up Node.js
uses: actions/setup-node@v5
with:
node-version: "lts/*"
- name: Install dependencies
run: npm ci
- name: Build package
run: npm run build --if-present
- name: Publish package to npm
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- 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
run: ./mcp-publisher login github-oidc
- name: Publish server to MCP Registry
run: ./mcp-publisher publish
Secrets by method: OIDC — none (needs id-token: write); PAT — MCP_GITHUB_TOKEN (a PAT with read:org, read:user), authenticate with ./mcp-publisher login github --token ${{ secrets.MCP_GITHUB_TOKEN }}; DNS — MCP_PRIVATE_KEY (Ed25519 private key). Also add NPM_TOKEN for the npm publish. Trigger by pushing a tag:
git tag v1.0.0
git push origin v1.0.0
Versioning
The version string in server.json is required, MUST be unique per publication, and is immutable once published. Semantic versioning is recommended (any format is supported; unparseable versions are always marked "latest"). Version ranges are prohibited (^1.2.3, ~1.2.3, >=1.2.3, 1.x, 1.2.*, 1 - 2, etc.). Best practices: align the server version with the underlying package version (local servers) or the remote API version (remote servers); for registry-only metadata updates that don't change the package, use a semantic prerelease like 1.2.3-1 (note: prereleases sort before the matching release, so publishing one after the release won't be marked "latest").
Related Entities
- mcp registry — what you are publishing to (metadata store, package types, aggregators)
- typescript sdk · python sdk — the servers you package and publish
- versioning — the protocol/version-awareness concept
- authorization — OAuth for the served remote server (distinct from publish-time auth)
- local vs remote servers — choosing
packagesvs.remotesinserver.json
