How to publish an MCP server to the official registry

by Sujay ChoubeyOct 23, 202617 min read
MCP

TL;DR: Publishing to the official MCP Registry centers on one file, server.json, which you publish with the mcp-publisher CLI. If you also ship a Claude Desktop extension, that uses a separate manifest.json with its own schema. Most failed publishes come from namespace and package-ownership errors, not schema failures. Post-publish maintenance (versioning, deprecation, uptime monitoring, schema drift) starts the day your server goes live and consumes more engineering time than the initial build. For teams whose core product is not the MCP server itself, managed infrastructure like Composio removes the build burden entirely by replacing months of in-house OAuth, credential, and policy work with access to 1,500+ pre-built app connectors, 50,000+ agent-ready tools, and an infrastructure layer that handles over 1 billion tool calls.

Your team can build an MCP server in a week. Publishing it without failed publishes and maintaining it without burning sprint capacity is a different problem. Most guides treat registry submission as the finish line, but submission is the easiest step. The maintenance lifecycle consumes more engineering hours than the initial build.

Teams that publish cleanly treat publishing as a lifecycle. They validate server.json before publishing, prove they own the package they list, version semantically, monitor uptime after go-live, and prepare a recovery procedure before they need it. This guide covers the full workflow, including 15 production readiness requirements, a field reference for both files, CI validation snippet, a recovery procedure for broken releases, and the build-vs-buy decision.

Quick start: MCP server publishing workflow

Step

Action

Key Command or Requirement

1

Create server.json

mcp-publisher init

2

Add ownership proof to your package

mcpName in package.json (npm) or mcp-name: in the README (PyPI, NuGet)

3

Publish the package itself

npm publish, PyPI upload, etc. (the registry checks it exists)

4

Validate

mcp-publisher validate

5

Authenticate and publish

mcp-publisher login github, then mcp-publisher publish

6

Version, monitor, and maintain

Unique version per publish; mcp-publisher status to deprecate

Production readiness requirements

Before publishing, verify these 15 requirements. Only the first group is enforced by the registry: it checks your server.json schema, your namespace, and your package ownership. The rest are not registry gates, but clients, aggregators, and enterprise security reviewers expect them. If you also distribute a Claude Desktop extension, its manifest.json is validated separately with the mcpb CLI and is never submitted to the registry.

Registry checks (publish fails without these):

  1. server.json validates against the current schema

  2. name uses a namespace you control, such as io.github.<user-or-org>/<server> or a domain you verify

  3. Package contains the ownership marker that matches name

  4. The package version listed in server.json is already published to npm, PyPI, or the matching registry

  5. Each version is unique and is not a range; semantic versioning recommended

  6. description is 100 characters or fewer

  7. Remote servers expose a public URL, preferably over streamable-http

Security (MCP spec and best practice):

  1. HTTP servers that support authorization follow the MCP authorization spec (OAuth 2.1)

  2. Tokens are validated as issued for your server; no token passthrough

  3. Origin header validated on HTTP servers to prevent DNS rebinding

  4. Hosted credentials kept in a secrets manager, with a documented rotation schedule

Documentation and operations:

  1. README with installation, configuration, troubleshooting, and security sections

  2. LICENSE file with a standard SPDX license

  3. SECURITY.md with a reporting contact and response timeline

  4. Uptime monitoring with alerts for downtime and elevated error rates

Readiness audit: Catching publish errors early

Before publishing, audit three areas: server.json validity and namespace, package ownership, and credential security. Documentation matters for adoption and enterprise review, but the registry itself does not review it.

Validate your server.json

The registry's own CLI generates and validates server.json. Install it on macOS or Linux with Homebrew (brew install mcp-publisher) or the prebuilt 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/

<p>mcp-publisher init       # creates a server.json template
mcp-publisher validate   # checks ./server.json against the registry</p>

mcp-publisher validate sends the file to the registry's validation endpoint, so it needs network access but no login. For an offline check, the zero-dependency Python validator from the mcp-manifest-validator repository also validates server.json. Running validation locally before publishing catches errors before they reach the registry.

If you also ship a Claude Desktop extension

MCP Bundles (.mcpb files, formerly Desktop Extensions) use a different file, manifest.json. The MCPB CLI validates it:

npm install -g @anthropic-ai/mcpb
mcpb validate manifest.json

You can also validate a directory containing a manifest.json file with mcpb validate ./my-extension. The MCPB CLI documentation confirms the command validates the manifest against the schema. mcpb validate does not accept server.json; it fails with “Unrecognized or unsupported manifest version.”

Audit server.json schema compliance

Your server.json must include name, description, and version, plus either a packages entry (for npm, PyPI, NuGet, OCI, and other packages) or a remotes entry (for hosted servers). The name field is a reverse-DNS name with exactly one slash, such as io.github.your-org/weather, not a plain slug. description is limited to 100 characters, and version must be unique for every publish. The registry quickstart shows complete examples.

Prove you own the package

The registry fetches your package and checks that it points back to your server name. For npm, add mcpName to package.json:

{
  "name": "@your-org/weather-mcp",
  "mcpName": "io.github.your-org/weather"
}

For PyPI and NuGet, put mcp-name: io.github.your-org/weather on its own line in the package README (an HTML comment works). Then publish that package version before you publish to the registry. Servers listed only under remotes skip this check. See Package Types for OCI images and other formats.

Secure your MCP server credentials

The MCP specification explicitly forbids token passthrough. Your server must only accept access tokens issued for it and must validate them before processing requests. This prevents a compromised client from using your server as a proxy to attack other services. The MCP specification classifies servers as OAuth Resource Servers.

Authorization is optional in MCP. When an HTTP-based server supports it, the specification says it should follow the MCP authorization flow, which is built on OAuth 2.1. Local STDIO servers should instead read credentials from the environment. For hosted servers, keep credentials in a secrets manager rather than in source code or plain configuration files, and document a rotation schedule in your runbook.

Audit documentation for adoption and enterprise review

The official MCP registry does not review submissions. Its moderation policy only removes spam, malicious, or illegal servers, and leaves quality checks to downstream aggregators and subregistries. Documentation still decides whether people adopt your server: clients, subregistries, and enterprise security teams look for READMEs, usage examples, installation guides, and troubleshooting steps.

Active maintenance signals also matter. A repository with no recent commit activity makes users and security reviewers question whether you still maintain the server.

Fields for a valid server.json

A valid server.json is the minimum requirement for registry acceptance. Understanding the difference between required and optional fields determines whether your server is discoverable and trustworthy.

Required server.json fields

  1. name: Reverse-DNS server name with exactly one slash (for example io.github.your-org/weather), 3–200 characters.

  2. description: Human-readable summary, 1–100 characters.

  3. version: Version of this release. Must be unique per publish; semantic versioning recommended; ranges such as ^1.2.3 are rejected.

  4. packages or remotes: At least one way to run the server: a package (npm, PyPI, NuGet, OCI, MCPB, and others) or a remote URL.

Optional server.json fields

  • $schema: URL of the schema version the file targets. Recommended so validators know which version to apply.

  • title: Human-friendly display name.

  • repository: Source repository (url and source, for example "github").

  • websiteUrl: Homepage or documentation URL.

  • icons: Icons for clients that display them.

  • _meta: Extension metadata in reverse-DNS namespaced keys.

Field-by-field server.json reference

Use this reference to verify your server.json before validation.

Field

Required

Type

Validation Rule

$schema

Recommended

string

Schema URL, e.g. https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json

name

Yes

string

Reverse-DNS, exactly one /, 3–200 characters

description

Yes

string

1–100 characters

version

Yes

string

Unique per publish; semver recommended; no ranges

packages

One of packages/remotes

array

registryType, identifier, version, transport

remotes

One of packages/remotes

array

type (streamable-http or sse) and public url

title

No

string

Display name

repository

No

object

url and source

websiteUrl

No

string

URL

Desktop extension manifest.json fields

If you also ship an MCP Bundle, its manifest.json requires manifest_version, name, version, description, author (with author.name), and server. Useful optional fields include display_name, tools (an array), keywords, license, repository, privacy_policies, compatibility, and user_config. The MCPB manifest specification lists every field.

Fixing common publish errors

Error

Fix

“Registry validation failed for package”

Add the ownership marker (mcpName or mcp-name:) and make sure that package version is published.

“You do not have permission to publish this server”

Your login doesn't match the name namespace. With GitHub login, the name must start with io.github.<your-username>/, or an org you own.

“Invalid or expired Registry JWT token”

Run mcp-publisher login github again.

Validation error on a field

Fix the field named in the message and run mcp-publisher validate again.

Security scanners flag servers that depend on deprecated packages, such as reference MCP servers deprecated on npm whose archived repositories state "NO SECURITY GUARANTEES ARE PROVIDED," as documented in the GuardMCP repository. Update or replace deprecated dependencies before you publish.

If you are deprecating a previous version, include replacement guidance and migration steps in your README. Deprecation messages without migration paths leave users stranded and signal poor maintenance commitment.

Optimizing server.json for discoverability

A valid server.json gets you past the schema validator. A well-described one helps people find and trust your server.

Configure server metadata

The registry's API search parameter is a case-insensitive substring match on server names, and aggregators display your title and description. Pick a name that says what the server connects to (io.github.your-org/salesforce beats io.github.your-org/utils), and use the 100-character description to name the main capabilities.

The registry schema has no license field. Put a LICENSE file with a standard SPDX license such as MIT or Apache-2.0 in your repository, and link it from repository. If you ship an MCP Bundle, also set the license field in manifest.json. Enterprise clients filter by license during procurement.

Map server capabilities and actions

Your transport configuration determines how clients connect. For production deployments, streamable-http is the current standard for HTTP-based transports; SSE is deprecated and worth publishing only for existing clients. The MCP authorization specification, since the June 2025 revision and in the current 2026-07-28 revision, is built on OAuth 2.1. Authorization is optional for MCP implementations; when an HTTP-based server supports it, the spec says it should conform to that flow.

If your server supports multiple transports, document the preferred one in your README.

Validate identity and access protocols

Whatever authentication you use, HTTP servers must validate the Origin header on all incoming connections to prevent DNS rebinding; the Streamable HTTP transport spec requires it. Document your actual authentication method in your README so clients know what to expect.

For servers handling sensitive data, document your credential storage approach in your README or security documentation. Enterprise reviewers look for statements about credential isolation, encryption at rest, and audit logging capabilities.

Adopt semantic versioning for releases

The registry recommends semantic versioning and uses it to work out which version is latest. Non-semantic version strings are accepted but may not sort predictably, and version ranges are rejected. The rules are strict: MAJOR for incompatible API changes, MINOR for backward-compatible functionality additions, and PATCH for backward-compatible bug fixes, as documented in the Semantic Versioning specification.

Catch schema errors before publishing your server

Schema validation is the first line of defense against failed publishes. Run it locally, in CI, and before every publish.

Run the official schema validator

For server.json, mcp-publisher is the canonical validation tool:

mcp-publisher validate server.json

For an offline check, the zero-dependency PayloadHQ validator also reads server.json:

python3 validate.py server.json

You can cross-check the version field against your pyproject.toml or package.json with:

python3 validate.py server.json --repo-dir /path/to/repo

Catch publish errors before you publish

Schema validation catches syntax errors. These checks catch the errors that only show up at publish time.

Before publishing, verify:

  • Namespace access: Can your login publish under the name prefix? GitHub login always grants io.github.<your-username>/; an organization namespace requires you to be an Owner of that org. Custom domains use DNS or HTTP authentication.

  • Package ownership: Does the published package contain mcpName or mcp-name: matching name?

  • Version: Is this version new? Published versions can't be overwritten.

Reducing publish failures via scripts

Automate validation in your CI pipeline. This GitHub Actions snippet validates server.json on every pull request:

name: Validate server.json
on: [pull_request]
jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install mcp-publisher
        run: curl -L "<a href="https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_linux_amd64.tar.gz">https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_linux_amd64.tar.gz</a>" | tar xz mcp-publisher
      - name: Validate server.json
        run: ./mcp-publisher validate server.json

If you also ship an MCP Bundle, add a step that runs npm install -g @anthropic-ai/mcpb and mcpb validate manifest.json.

Streamlining the final submission process

Submission mechanics are straightforward. The complexity lies in preparation and follow-up.

Publishing with mcp-publisher

Log in, then publish:

mcp-publisher login github
mcp-publisher publish

After publishing, verify your server appears in the registry:

curl "<a href="https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.yourname/your-server">https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.yourname/your-server</a>"

Preparing your server's metadata

Before publishing, make sure the package version inside server.json matches a version already published to npm, PyPI, or your package registry. The registry doesn't read your git tags, but keeping the tag, package version, and server.json version aligned prevents confusion; the Cluefinch workflow explicitly verifies version consistency before publishing.

Your README should include:

  • Installation instructions

  • Configuration examples

  • Troubleshooting section

  • Security considerations

  • License information

There is no approval wait

The registry has no manual approval step: once mcp-publisher publish passes the schema, namespace, and ownership checks, your server is listed. Aggregators and subregistries that copy from the registry may take longer to show it. For automated releases, use GitHub OIDC. Publishing from GitHub Actions with mcp-publisher login github-oidc grants the repository owner's namespace (io.github.<repository-owner>/), with no stored secret, as noted in the MainWP MCP pull request.

Preventing common publish failures

  • Namespace mismatch: The name prefix doesn't match how you logged in.

  • Missing ownership marker: The package lacks mcpName or mcp-name:, or the value differs from name.

  • Package not published yet: Publish the package version before the registry entry.

  • Reused version: Every publish needs a new version string.

Release, versioning, and deprecation

Publishing is not the finish line. It is the start of a maintenance obligation that lasts as long as your server is listed.

Adhering to semantic versioning specs

Post-publish versioning follows the same semantic versioning rules, but mistakes have user-facing consequences. When incrementing versions, follow the reset rules: MAJOR increment resets MINOR and PATCH to zero; MINOR increment resets PATCH to zero. The Semantic Versioning specification is explicit on this point.

Automating your registry deployment

Automate publishing to reduce human error. A GitHub Actions workflow with manual trigger allows controlled releases:

name: Publish to MCP Registry
on:
  workflow_dispatch:
    inputs:
      tag:
        description: 'Release tag to publish'
        required: true
jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      id-token: write   # required for GitHub OIDC login
      contents: read
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ inputs.tag }}
      - name: Validate version consistency
        env:
          TAG_VERSION: ${{ inputs.tag }}
        run: |
          MANIFEST_VERSION=$(jq -r '.version' server.json)
          if [ "$TAG_VERSION" != "v$MANIFEST_VERSION" ]; then
            echo "Version mismatch: tag=$TAG_VERSION, server.json=$MANIFEST_VERSION"
            exit 1
          fi
      - name: Install mcp-publisher
        run: curl -L "<a href="https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_linux_amd64.tar.gz">https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_linux_amd64.tar.gz</a>" | tar xz mcp-publisher
      - name: Validate server.json
        run: ./mcp-publisher validate server.json
      - name: Log in to the MCP Registry
        run: ./mcp-publisher login github-oidc
      - name: Publish to registry
        run: ./mcp-publisher publish

This workflow, adapted from the Cluefinch example and the registry's GitHub Actions guide, ensures tag and server.json version alignment before publishing. The tag is passed through an environment variable rather than inline, which avoids shell injection from the input. If the workflow also publishes your npm or PyPI package, run that step before the registry publish.

How to gracefully deprecate old servers

Deprecation is a user experience problem, not just a metadata change. The AWS MCP servers follow a formal deprecation notice process that serves as a model:

  1. Display a deprecation warning at runtime and in the README.

  2. Publish no further releases for the deprecated server.

  3. Include a replacement migration guide with capability differences.

The AWS MCP discussions document this approach. In the registry itself, mark versions as deprecated with mcp-publisher:

mcp-publisher status --status deprecated --message "Please upgrade to 2.0.0" <br />  io.github.your-org/your-server 1.0.0

Add --all-versions to deprecate every version at once. You can also mark the underlying package as deprecated through npm, which flags specific versions or the entire package as outdated.

Preventing failed publishes

Prevent failed publishes by:

  • Testing validation locally before publishing

  • Reviewing recent registry GitHub issues for known publishing problems

  • Re-running mcp-publisher login when your token expires

  • Documenting security measures proactively

Build vs. buy: When managed infrastructure beats self-hosting

Self-hosting an MCP server gives full control over the codebase and avoids vendor lock-in, but every capability you need (OAuth flows, audit logging, policy enforcement) becomes your responsibility to build, maintain, and secure. Composio's managed infrastructure provides the same capabilities without the build cost. The comparison is not about features; it is about engineering hours and maintenance burden.

Capability

Self-Hosted MCP Server

Composio Managed Infrastructure

Credential storage

Build and secure your own vault

AES-256 encrypted, isolated from LLM context

OAuth management

Implement and maintain per-provider flows

Managed OAuth with automatic token refresh

Policy enforcement

Tool-list filtering and prompt instructions

Policy-as-code in the request path

Audit logging

Build logging infrastructure

Centralized audit log including denied calls

Maintenance burden

Your team's ongoing responsibility

Composio's operational responsibility

Time to production

Build time determined by OAuth, credential, and policy scope

Configuration-based onboarding against pre-built connectors

Certifications

Pursue independently

SOC 2 Type II, ISO/IEC 27001:2022 included

Composio's MCP gateway provides the managed infrastructure layer: credential isolation, policy-as-code enforcement, and centralized audit logging. The platform holds SOC 2 Type II and ISO/IEC 27001:2022 certifications, with pre-filled compliance documentation available through the trust center.

Before you commit budget to a build-vs-buy decision, you can test the managed path directly: Composio's free tier includes 100,000 tool calls per month, so your team can test its first integration without a sales call.

If you are evaluating managed infrastructure for your MCP server deployment, book a call to walk through your security requirements before the next enterprise review.

FAQs

How long does it take for my server to appear in the registry?

It's immediate. The registry has no manual review: once mcp-publisher publish passes the schema, namespace, and ownership checks, the server is listed. Aggregators that copy from the registry may take longer to pick it up.

Can I update my server after publishing?

Yes, you can publish updates using semantic versioning. Each new version requires validation and a new, unique version string, published through the same workflow as the initial publish.

What happens if my server fails validation?

Validation failures return specific error messages indicating which field or requirement failed. Fix the issue and publish again.

Do I need to maintain backward compatibility?

Yes, within MAJOR versions. MINOR and PATCH versions must be backward compatible. MAJOR version increments signal breaking changes and require user migration.

Can I update my server after publishing?

Yes, you can publish updates using semantic versioning. Each new version requires validation and submission through the same workflow as the initial publish.

What happens if my server fails validation?

Validation failures return specific error messages indicating which field or requirement failed. Fix the issue and resubmit.

How do I roll back a broken version?

The official registry specification does not document a formal rollback mechanism. Deprecate the broken version and publish a fixed version with an incremented PATCH number, as detailed in the "Handling failed registry deployments" section above.

Do I need to maintain backward compatibility?

Yes, within MAJOR versions. MINOR and PATCH versions must be backward compatible. MAJOR version increments signal breaking changes and require user migration.

Key terms glossary

server.json: The JSON file the MCP Registry accepts, declaring a server's name, version, description, and where to find its package or remote endpoint.

manifest.json: The manifest inside an MCP Bundle (Claude Desktop extension), validated with the MCPB CLI. It is not submitted to the registry.

MCPB CLI: The official command-line tool for validating and packaging MCP Bundle manifests (manifest.json).

mcp-publisher: The MCP Registry's command-line tool for creating, validating, publishing, and deprecating server.json entries.

Semantic Versioning: A versioning scheme (MAJOR.MINOR.PATCH) where MAJOR indicates breaking changes, MINOR indicates new features, and PATCH indicates bug fixes.

OAuth 2.1: The authorization framework the MCP authorization specification is built on, recommended for HTTP-accessible servers that support authorization, classifying them as OAuth Resource Servers. Authorization is optional for MCP implementations.

Token passthrough: Forwarding a client token directly to a downstream API; explicitly forbidden by the MCP specification.

Policy-as-code: Access restrictions enforced in the request path before the model is involved, rather than through prompt instructions.

Credential isolation: Architectural separation of authentication tokens from the LLM context and application code.

Audit logging: Recording every tool call with user, tool, action, and outcome, including denied calls for compliance evidence.

Deprecation: Marking a server version as obsolete with guidance for users to migrate to a replacement.

Ownership verification: The registry's automated check that your package (via mcpName or mcp-name:) and your login both match the server's namespace before it accepts a publish.

Get started

Your agents can
do more

Connect your agents to 1,500+ apps. Start for free, no credit card needed.

Are you an AI agent? See setup options

Share