TL;DR: Publishing to the official MCP Registry centers on one file,
server.json, which you publish with themcp-publisherCLI. If you also ship a Claude Desktop extension, that uses a separatemanifest.jsonwith 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 |
|
2 | Add ownership proof to your package |
|
3 | Publish the package itself |
|
4 | Validate |
|
5 | Authenticate and publish |
|
6 | Version, monitor, and maintain | Unique version per publish; |
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):
server.jsonvalidates against the current schemanameuses a namespace you control, such asio.github.<user-or-org>/<server>or a domain you verifyPackage contains the ownership marker that matches
nameThe package version listed in
server.jsonis already published to npm, PyPI, or the matching registryEach
versionis unique and is not a range; semantic versioning recommendeddescriptionis 100 characters or fewerRemote servers expose a public URL, preferably over
streamable-http
Security (MCP spec and best practice):
HTTP servers that support authorization follow the MCP authorization spec (OAuth 2.1)
Tokens are validated as issued for your server; no token passthrough
Originheader validated on HTTP servers to prevent DNS rebindingHosted credentials kept in a secrets manager, with a documented rotation schedule
Documentation and operations:
README with installation, configuration, troubleshooting, and security sections
LICENSE file with a standard SPDX license
SECURITY.mdwith a reporting contact and response timelineUptime 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.jsonYou 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
name: Reverse-DNS server name with exactly one slash (for example
io.github.your-org/weather), 3–200 characters.description: Human-readable summary, 1–100 characters.
version: Version of this release. Must be unique per publish; semantic versioning recommended; ranges such as
^1.2.3are rejected.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 (
urlandsource, 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 |
|---|---|---|---|
| Recommended | string | Schema URL, e.g. |
| Yes | string | Reverse-DNS, exactly one |
| Yes | string | 1–100 characters |
| Yes | string | Unique per publish; semver recommended; no ranges |
| One of packages/remotes | array |
|
| One of packages/remotes | array |
|
| No | string | Display name |
| No | object |
|
| 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 ( |
“You do not have permission to publish this server” | Your login doesn't match the |
“Invalid or expired Registry JWT token” | Run |
Validation error on a field | Fix the field named in the message and run |
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.jsonFor an offline check, the zero-dependency PayloadHQ validator also reads server.json:
python3 validate.py server.jsonYou can cross-check the version field against your pyproject.toml or package.json with:
python3 validate.py server.json --repo-dir /path/to/repoCatch 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
nameprefix? GitHub login always grantsio.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
mcpNameormcp-name:matchingname?Version: Is this
versionnew? 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.jsonIf 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 publishAfter 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
nameprefix doesn't match how you logged in.Missing ownership marker: The package lacks
mcpNameormcp-name:, or the value differs fromname.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 publishThis 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:
Display a deprecation warning at runtime and in the README.
Publish no further releases for the deprecated server.
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.0Add --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 loginwhen your token expiresDocumenting 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.
