registry/docs/reference/server-json/official-registry-requirements.md
Radoslav Dimitrov a25f166b4b
docs: remove stale complete.md and fix remaining stale references (#1522)
Deletes `complete.md` and cleans up the last references to the
pre-restructuring docs layout.

## Why `complete.md` goes

It is 2,944 lines (31% of all documentation lines in the repo)
self-described as *"all documentation from the docs/ directory compiled
into a single markdown file"*. But:

- **No generator exists.** No make target, no workflow, no script. `grep
-rn "complete\.md"` finds **zero** references anywhere in the repo.
- **It mirrors a docs tree that no longer exists** — `docs/guides/`,
`docs/explanations/`, `docs/concepts/`.
- **All 72 of its relative links are broken**, and six of the basenames
it references exist nowhere in the repo.
- **~14 current docs are missing from it entirely** (`about.mdx`,
`authentication.mdx`, `package-types.mdx`, both CHANGELOGs, ...).
- **Its `server.json` examples pin the superseded `2025-07-09` schema**
(current is `2025-12-11`).

The strongest argument is its own last commit. [PR
#1278](https://github.com/modelcontextprotocol/registry/pull/1278)
closed [issue
#767](https://github.com/modelcontextprotocol/registry/issues/767) — a
real user-reported CLI flag bug — by editing **only `complete.md`**. A
genuine fix landed in a file nobody reads, while the docs a reader would
actually find stayed wrong.

A generated file with no generator degrades into a trap: authoritative
enough that people edit it, with nothing validating it. If the
LLM-consumption use case is worth keeping, the right shape is a `make
docs-bundle` target plus a `check-docs-bundle` CI job mirroring the
existing `check-schema` pattern — happy to do that instead if you would
rather keep it.

## Remaining stale references

| File | Was | Now |
|---|---|---|
| `contributing/add-package-registry.md` | Add
`docs/guides/publishing/publish-[yourregistry].md`, update
`docs/guides/publishing/README.md` | Add a section to
`package-types.mdx`. These were the **last live references** to the
obsolete `docs/guides/` layout outside `complete.md` |
| `administration/maintainer-onboarding.md` | Add them to "Current key
maintainers" in README | "Registry Working Group" — the section that
actually exists |
| `reference/README.md` | Omitted 6 reference docs | Adds
`api/extensions.md`, `api/openapi.yaml`, `api/CHANGELOG.md`,
`server-json/CHANGELOG.md`, `server-json/CONTRIBUTING.md`,
`server-json/draft/` |
| `docs/README.md` | Index omitted `community-projects.md` | Listed |
| `reference/api/generic-registry-api.md` | `PUT .../versions/{version}`
"not implemented by official registry" | It **is** implemented
([edit.go:36](https://github.com/modelcontextprotocol/registry/blob/main/internal/api/handlers/v0/edit.go#L36)),
and `official-registry-api.md` already lists it as an admin endpoint |
| `reference/api/official-registry-api.md` | Omitted 3 registered routes
| Adds `POST /v0.1/validate` (which `mcp-publisher validate` calls),
`GET /v0.1/ping`, `GET /v0.1/version`, and notes these plus `/health`
are absent from `openapi.yaml` |
| `server-json/official-registry-requirements.md` | Per-registry
verification → `quickstart.mdx` | → `package-types.mdx`, which has an
Ownership Verification section per registry |
| `design/design-principles.md` | Sections 1,2,3,4,**6**,7 | Renumbered
1–6 |
| `contributing/releasing.md` | Steps 1,2,3,**5**,6 | Renumbered 1–5 |

I left the sibling `DELETE` claim in `generic-registry-api.md` alone —
it is accurate, no DELETE route is registered (the `http.MethodDelete`
hit in `server.go` is CORS config, not a route).

## Staleness banners rather than rewrites

**`design/tech-architecture.md`** is substantially fictional. Rather
than a vague "may be outdated", the banner lists what I verified is
wrong:

- Describes Helm-chart deployment; the registry is a plain `Deployment`
([registry.go:65](https://github.com/modelcontextprotocol/registry/blob/main/deploy/pkg/k8s/registry.go#L65)).
Helm is used only for third-party components.
- Diagram shows a `StatefulSet` on port **27017** — MongoDB's port. The
DB is PostgreSQL on 5432 via the CloudNativePG operator.
- Documents a `verify-domain` challenge flow (`POST /verify-domain`,
`/verify-domain/check`, server-issued tokens) that **was never built**.
- Documents `/admin/*` routes that are not registered.
- Says the DB stores user auth state and DNS verification records; only
`servers` and `server_extensions` tables exist.

**`design/roadmap.md`** still says "Phase 1: MVP/Go-Live (Current
Focus)" ~11 months after launch. I added a dated note rather than
inventing a current phase — that is a working-group call.

## Verification

All **101** remaining relative links across every `.md`/`.mdx` in the
repo resolve. The only two non-resolving matches are the intentional
`[project-name](link)` template placeholder and the moderation link
fixed in #1518.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 16:32:37 +03:00

5.9 KiB

Official Registry Server.json Requirements

This document describes the additional requirements and validation rules that apply when publishing to the official MCP Registry at registry.modelcontextprotocol.io.

For step-by-step publishing instructions, see the publishing guide.

Overview

While the generic server.json format defines the base specification, the official registry enforces additional validation to ensure:

  • Namespace authentication - Servers are published under appropriate namespaces
  • Package ownership verification - Publishers actually control referenced packages
  • Restricted registry base urls - Packages are from trusted public registries
  • _meta namespace restrictions - Restricted to publisher key only

Namespace Authentication

Publishers must prove ownership of their namespace. For example to publish to com.example/server, the publisher must prove they own the example.com domain.

See the publishing guide for authentication details for GitHub and domain namespaces.

Package Ownership Verification

All packages must include metadata proving the publisher owns them. This prevents impersonation and ensures authenticity (see more reasoning in #96).

For detailed verification requirements for each registry type, see package types, which has an "Ownership Verification" section per registry.

Restricted Registry Base URLs

Only trusted public registries are supported. Private registries and alternative mirrors are not allowed.

Supported registries:

  • NPM: https://registry.npmjs.org only
  • PyPI: https://pypi.org only
  • NuGet: https://api.nuget.org/v3/index.json only
  • Cargo: https://crates.io only
  • Docker/OCI:
    • Docker Hub (docker.io)
    • GitHub Container Registry (ghcr.io)
    • Quay.io (quay.io)
    • Google Artifact Registry (*.pkg.dev)
    • Azure Container Registry (*.azurecr.io)
    • Microsoft Container Registry (mcr.microsoft.com)
  • MCPB: https://github.com releases and https://gitlab.com releases only

_meta Namespace Restrictions

The _meta field in server.json allows publishers to include custom metadata alongside their server definitions.

Publisher-Provided Metadata

When publishing to the official registry, only data under the specific key io.modelcontextprotocol.registry/publisher-provided will be preserved. Any other keys in the _meta object are silently dropped during publishing and will not be stored or returned by the registry.

Example:

{
  "_meta": {
    "io.modelcontextprotocol.registry/publisher-provided": {
      "tool": "ci-publisher",
      "version": "1.0.0",
      "custom_data": "your data here"
    },
    "some.other.key": {
      // This will be dropped and not preserved
    }
  }
}

Size limit: The publisher-provided extension is limited to 4KB (4096 bytes) of JSON. If the marshaled JSON exceeds this limit, publishing will fail with an error indicating the actual size.

When your publisher-provided metadata gets large or comes from multiple sources (e.g. GitHub-specific hints alongside your own CI tool's metadata), grouping keys under reverse-DNS subkeys is a useful convention to avoid accidental collisions:

{
  "_meta": {
    "io.modelcontextprotocol.registry/publisher-provided": {
      "com.github": {
        "serverDisplayName": "My Server"
      },
      "io.example.ci-publisher": {
        "tool": "ci-publisher",
        "version": "1.0.0"
      }
    }
  }
}

This is not enforced; flat keys work fine for simple cases, and existing examples in this repo use the flat form. Use the namespaced form when organizing metadata from more than one source.

Registry API Metadata vs server.json Metadata

The _meta field in server.json is different from the _meta field returned in registry API responses:

  • In server.json: The _meta field contains publisher-provided custom metadata under io.modelcontextprotocol.registry/publisher-provided
  • In API responses: The _meta field is returned as a separate property at the response level (not inside server.json) and contains registry-managed metadata like:
    • status: Server lifecycle status (active, deprecated, deleted)
    • publishedAt: When the server was first published
    • updatedAt: When the server was last updated
    • isLatest: Whether this is the latest version

Example: What you publish (server.json)

{
  "name": "io.github.example/my-server",
  "version": "1.0.0",
  "description": "My MCP server",
  // ... other fields ...
  "_meta": {
    "io.modelcontextprotocol.registry/publisher-provided": {
      "tool": "ci-publisher",
      "version": "2.0.0"
    }
  }
}

Example: What the registry API returns

{
  "server": {
    "name": "io.github.example/my-server",
    "version": "1.0.0",
    "description": "My MCP server",
    // ... other fields ...
    "_meta": {
      "io.modelcontextprotocol.registry/publisher-provided": {
        "tool": "ci-publisher",
        "version": "2.0.0"
      }
    }
  },
  "_meta": {
    // Registry-managed metadata at response level
    "status": "active",
    "publishedAt": "2024-01-15T10:30:00Z",
    "updatedAt": "2024-01-15T10:30:00Z",
    "isLatest": true
  }
}

Notice how the registry API response has two _meta fields:

  1. Inside the server object: Your publisher-provided metadata (preserved from server.json)
  2. At the response level: Registry-managed metadata (automatically added by the registry)

Registry-managed metadata cannot be set or overridden by publishers. See the API documentation for the complete response structure.