A server.json file is refused by the official MCP registry for two different kinds of reason. Some fields fail the published JSON Schema: a description that is too long, a version written as a range, a name with the wrong shape. Others pass the schema and then fail a check the registry runs at publish time, which asks whether the person publishing really controls the name and the package. The first kind can be caught with a validator. The second kind needs a look at the package itself, and it is where most of the confusion lives. This guide goes through both, field by field, so a file can be checked before the publish command is run.
The maintainers call the registry a preview and warn that breaking changes or data resets may happen (package types page). Everything below reflects the documentation and schema as read on 28 October 2026; this guide did not publish anything to the registry to test it.
The schema address
Near the top of the file, a schema address is expected. The registry documentation shows addresses on its static schema host, with a dated folder in the middle, and its current examples use the folder dated 2025-12-11 (package types page). Older tutorials carry earlier dates. A file that names an old folder may validate against a shape the registry no longer wants, so copy the address from the current documentation rather than from a blog post, this one included.
The name decides how you must sign in
The name has a reverse-domain namespace, a slash, and then a short name for the server itself. The namespace is not a free choice. The authentication page ties it to the login method: with GitHub sign-in the name must start with io.github. followed by a user or organization login, and with domain-based sign-in it must start with the domain written backwards (authentication page).
Two details cause refusals here.
- An organization namespace is only granted to an owner of that organization on GitHub. The documentation says ordinary membership is not enough, and that the interactive GitHub login currently cannot read organization membership, so only the personal namespace is granted. A personal access token that is allowed to read organization roles is the documented way around it (authentication page).
- Domain sign-in has two forms. The DNS form needs a TXT record placed directly on the bare domain; the documentation warns that a record under a selector-style subdomain is not seen and ends in a generic signature error. The HTTP form needs the proof served from the well-known location
/.well-known/mcp-registry-auth, as plain text without redirects (authentication page).
Do not reuse an npm package name as the server name. A scoped npm name has an at-sign and no reverse-domain shape, so putting it in the name field fails the schema pattern.
The description limit
In the schema the description is capped at a hundred characters, not words, and it is meant to describe what the server can do rather than how it is built. A paragraph copied from a README is the usual cause of a refusal. Shorten it to one plain sentence that names the capability. A separate optional title field exists for a display name, so the name does not need to carry that job.
The version must be exact and new
The versioning page requires a version string in every file and requires it to be different for each publication. Once a version is published, neither it nor its metadata can be changed (versioning page). The page lists range forms as prohibited: caret and tilde prefixes, comparison operators, and wildcard segments. The schema also rejects the literal word latest as a package version since the version field of the schema forbids that value.
Semantic versions are recommended but not required. The page warns that a published version that does not parse as a semantic version is always marked as the latest one, even when it would sort earlier (versioning page). That makes a mixed history confusing, so pick one format and stay with it.
When just the listing text changes and the package is untouched, the same page suggests a semantic prerelease suffix on the server version, for example a package at version 1.2.3 listed as server version 1.2.3-1. The package entry keeps the exact version of the package (versioning page).
The proof inside each package
Here the schema is satisfied and the publish still fails, because the registry opens the published package and looks for the server name. The package types page describes one check per registry, and the check lives in the package, not in server.json (package types page).
| Package type | Where the server name must appear |
|---|---|
| npm | an mcpName property in package.json |
| PyPI | an mcp-name: line in the README that becomes the package description |
| NuGet | the same mcp-name: line in the package README |
| Cargo | the same line, but as visible text |
| OCI image | a label named io.modelcontextprotocol.server.name on the image |
| MCPB bundle | the download address has to include the text mcp somewhere, and server.json carries a SHA-256 hash of the file (docs) |
Several traps follow from that table. For a bundle, the registry itself does not validate the hash, but clients do before installing, so a wrong hash breaks installs later instead of the publish (package types page).
The value must match the server name exactly. A copy that differs by one character is a different name, and the check fails.
Cargo differs from PyPI and NuGet. The documentation says crates.io removes HTML comments when it renders a README, so the hidden comment form that works for the other two never reaches the validator, and the line must be ordinary visible text; a bullet under a links heading is the suggested spot (package types page).
The mcp-name: token needs a boundary after the name. A sentence-ending full stop glued to the name stops the match, so keep the line on its own or inside a comment (package types page).
Only public registries are accepted: the npm public registry, the official PyPI index, the official NuGet index and crates.io. A private mirror will not do. For images, six registries are supported, including Docker Hub and Quay (package types page).
The proof also has to exist before the publish. Because versions cannot be edited afterwards, a package published without the proof line means releasing a new package version, not patching the old one. This follows from the immutability rule above, and it is the reason to check the package first.
Secrets are inputs, not values
The registry's own example for an API key uses an input description with a flag marking it secret, and gives no value (server.json examples). The registry is public. Whatever is written in a value field is readable by anyone, so a key pasted there is leaked at publish time, whether or not the file validates. This is the one point in the guide that is general practice rather than a documented refusal: the schema does not stop a value from being added.
The extension block
The optional _meta object is described in the schema as extension metadata under reverse-domain keys, with a publisher-provided key for the publisher's own data (server.json examples). Put custom data under that key rather than inventing new top-level fields; the schema is the reference for what the registry expects.
A check that can fail
Check the file against the published JSON Schema first, with any JSON Schema validator. Then do the part no schema validator does. Open the package as it exists on its public registry, not the folder on disk, and look for the proof from the table above with the exact server name. Change one character in the name inside server.json and confirm the check you are relying on would object. If a change like that goes unnoticed, the check was not looking at the thing you thought it was.
When this does not apply
Only the official registry is in scope here. Other catalogs and client-specific lists have their own rules and may ask for different fields. Because the registry is still a preview, a field, a supported package registry or a login method can change; the pages cited above hold the current rules. This guide did not run a publish and did not test any refusal message, so the descriptions of what fails come from documentation, not from observed errors. Hosted servers, which use a remotes entry instead of a package, have their own conditions that this guide only touches on, and private or internal registries follow whatever rules their operator sets.
Choosing a name and a package proof is the same kind of decision as wiring a server into a client, and the walk-through in adding an MCP server to Claude Code covers the client side of that picture.
Writing the file with help
For the steps above, the mcp-registry-server-json skill takes a description of a server and writes the file: it picks a namespace that fits how the publisher signs in, trims the description, writes a separate entry for every package type with its exact version, adds secrets as inputs without values, and tells you where the ownership proof must go in the package. It can also answer a short question about why a publish was refused.
Read this post as Markdown: /blog/mcp-registry-server-json-fields.md · Atom feed.
