Reference
What goes in a server.json file for MCP
Last verified: 10 August 2026 · spec 2026-07-28 · ruleset 1.4.2
server.json is the metadata file that describes an MCP server to the registry. Four fields are required — $schema, name, description and version — plus at least one of packages or remotes. Everything else is optional.A minimal, complete file
{
"$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.",
"version": "1.0.1",
"repository": {
"url": "https://github.com/my-username/mcp-weather-server",
"source": "github"
},
"packages": [
{
"registryType": "npm",
"identifier": "@my-username/mcp-weather-server",
"version": "1.0.1",
"transport": { "type": "stdio" }
}
]
}mcp-publisher initEvery top-level field
| Field | Required | Type | What it is |
|---|---|---|---|
| $schema | Yes | string | URL of the schema version this file targets |
| name | Yes | string | Reverse-DNS namespace identifier, e.g. io.github.you/weather |
| description | Yes | string | What the server does |
| version | Yes | string | Version number; semver recommended |
| title | No | string | Human-readable display name |
| repository | No | object | Source repository details |
| websiteUrl | No | string | Documentation URL |
| packages | No | array | Installable package definitions |
| remotes | No | array | Hosted endpoint definitions |
| _meta | No | object | Publisher metadata, reverse-DNS namespaced |
packages says “install this and run it locally”, remotes says “connect to this endpoint I host”. A server offering both may declare both.Naming rules — the field most likely to reject your publish
name is reverse-DNS, namespace first, then a slash, then the server name:
io.github.my-username/weather ✓ GitHub namespace
io.github.my-org/weather ✓ needs Owner role in the org
com.example/weather ✓ verified domain example.com
com.example.tools/weather ✓ subdomain form
weather ✗ no namespace
my-username/weather ✗ not reverse-DNS
com.example/Weather Server ✗ spacesTwo separate things must line up, and they fail with different errors:
- The namespace must be one you authenticated for.
mcp-publisher login githubgrantsio.github.you/*;login dns --domain example.comgrantscom.example.*/*. See verifying a DNS namespace. - If you declare an npm package,
namemust exactly match itsmcpName. That pairing is the package-ownership proof.
// package.json // server.json
{ {
"name": "@you/mcp-weather", "name": "io.github.you/weather",
"mcpName": "io.github.you/weather" …
} }
└──────── must be identical ────────┘“Publishers must verify ownership of their namespace through GitHub, DNS, or HTTP challenges, preventing arbitrary spam submissions.”
packages[] — for servers users install
{
"registryType": "npm", // npm | pypi | cargo | nuget | oci | mcpb
"registryBaseUrl": "https://registry.npmjs.org",
"identifier": "@you/mcp-weather",
"version": "1.0.1",
"runtimeHint": "npx",
"fileSha256": "…", // mcpb only
"transport": { "type": "stdio" },
"packageArguments": [],
"runtimeArguments": [],
"environmentVariables": []
}registryBaseUrl must be an official registry — registry.npmjs.org, pypi.org, api.nuget.org/v3/index.json or crates.io. Private registries are disallowed, because ownership cannot be verified on a host the registry cannot read.
"environmentVariables": [
{
"name": "WEATHER_API_KEY",
"description": "API key for the upstream weather provider",
"isRequired": true,
"isSecret": true
}
],
"packageArguments": [
{
"type": "named",
"name": "--units",
"description": "Measurement system",
"choices": ["metric", "imperial"],
"default": "metric",
"isRequired": false
}
]isSecret: true. Clients use that flag to decide whether to mask a value in a UI and keep it out of logs — a missing flag is how an API key ends up in a screenshot.remotes[] — for servers you host
"remotes": [
{
"type": "streamable-http", // streamable-http | sse
"url": "https://mcp.example.com/mcp",
"headers": [
{
"name": "Authorization",
"description": "Bearer token issued by example.com",
"isRequired": true,
"isSecret": true
}
]
}
]The URL supports {variable} templating against declared arguments, environment variables or remote-specific variables — useful for a per-tenant subdomain.
“A remote server MUST be publicly accessible at its specified URL.”
streamable-http over sse for anything new. The legacy HTTP+SSE transport is formally deprecated under MCP's lifecycle policy — declaring it in a fresh listing publishes a server on a transport with a countdown on it.repository
"repository": {
"url": "https://github.com/you/mcp-weather-server",
"source": "github",
"id": "…", // optional UUID
"subfolder": "servers/weather" // for a monorepo
}Optional, but worth filling in: it is the link anyone evaluating your server will follow first, and subfolder is the only way to point at the right directory in a monorepo.
_meta — and what survives publishing
Custom metadata is allowed only under _meta, in a reverse-DNS namespaced key. For the official registry, exactly one key survives:
"_meta": {
"io.modelcontextprotocol.registry/publisher-provided": {
"tool": "my-release-script",
"version": "2.1.0",
"build_info": { "commit": "a1b2c3d" }
}
}io.modelcontextprotocol.registry/publisher-provided, and that block is capped at 4KB; exceeding it fails the publish with the actual size in the message.Validating it
mcp-publisher validate
# or against a specific file
mcp-publisher validate ./server.json“Validates `server.json` against schema”
We check the same thing from the outside as MCP-REG-001, alongside MCP-REG-003 for version format and MCP-REG-004 for whether a declared remote endpoint is actually reachable at its stated URL.
Validate the server, not just the file
A valid server.json means your listing is well-formed. Whether the server it describes conforms to the 2026-07-28 protocol is a separate question.
Frequently asked
What goes in a server.json file for MCP?
Four required fields — $schema, name, description and version — plus at least one of packages or remotes. Optional fields are title, repository, websiteUrl and _meta. The name must be a reverse-DNS identifier such as io.github.username/server-name, and it must sit inside a namespace you have authenticated for.
Is packages or remotes required in server.json?
At least one of them must be present. Use packages for a server users install and run locally, listing the registry type, identifier, version and transport. Use remotes for a server you host, listing the endpoint type and URL. A server can declare both if it offers each option.
What is the correct $schema URL for server.json?
https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json is the current schema version. Getting this wrong is a quiet failure — validation runs against whatever you point at, so a stale or misspelled URL can pass locally and be rejected at publish time.
How should I name an MCP server?
In reverse-DNS form, namespace first: io.github.username/server-name for a GitHub namespace, or com.example/server-name for a verified domain. The namespace half must be one you have authenticated for, and if your server.json declares an npm package, the name must exactly match the mcpName field in that package's package.json.
Can I put custom fields in server.json?
Only under _meta, and only in a reverse-DNS namespaced key. For the official registry, just io.modelcontextprotocol.registry/publisher-provided survives publishing — other keys are silently dropped, so anything you rely on must live under that key. That block is also capped at 4KB.
Which package registries can server.json reference?
Only official ones at fixed URLs: npm at registry.npmjs.org, PyPI at pypi.org, NuGet at api.nuget.org/v3/index.json and Cargo at crates.io, plus specific container registries. Private registries are disallowed because the registry cannot verify package ownership on a host it cannot read.
Sources
Field tables verified on 10 August 2026 against:
- registry/docs/reference/server-json/generic-server-json.md — the field-by-field reference
- official-registry-requirements.md — the allowed registry list,
_metahandling and the 4KB cap