registry/docs/reference/server-json/official-registry-requirements.md
Joel Verhagen 80cb03ff7a
Fix NuGet.org base URL default, improve API integration (#813)
<!-- Provide a brief summary of your changes -->

## Motivation and Context
<!-- Why is this change needed? What problem does it solve? -->

This resolves
https://github.com/modelcontextprotocol/registry/issues/738. There are
two issues with the NuGet.org integration today:

1. Change the `registryBaseUrl` for NuGet to
`https://api.nuget.org/v3/index.json`. `https://api.nuget.org` is not
the entry point for our service and is not specific enough.
`https://api.nuget.org/v3/index.json` is the value that users put into
their tooling to connect to our public registry (it is installed by
default in .NET client tooling).
2. Use the service index API to get the URL pattern for package READMEs.
- This URL is not guaranteed to be stable. The service index URL
(`registryBaseUrl`) is, however.

Additionally, I polished up the error messages so various error modes
are clearer. When packages are published to NuGet.org, they take 2-5
minutes to become available due to validations we run.

Some possible states that are now handled and described more helpfully
are:
- Version exists, but the package has no README
- The ID exists, but the version does not
- The ID does not exist at all

There are only 3 authors (me, another MSFT person, and a community
member) explicitly setting the old value. The DB migration will fix the
issue. A future publish will fail with the validation error but the
error message is clear.

`registry type and base URL do not match: 'https://api.nuget.org' is not
valid for registry type 'nuget'. Expected:
https://api.nuget.org/v3/index.json`

## How Has This Been Tested?
<!-- Have you tested this in a real application? Which scenarios were
tested? -->

- Unit tests, new and existing
- Manual test locally
- Set old data in the DB, `docker compose up`, confirm the migration
updates the specific package

## Breaking Changes

Yes, but minimal impact expected, unless there is a dependency on the
specific `https://api.nuget.org` value downstream. I am not aware of any
such dependency, and the new value is more proper and specific.

## Types of changes
<!-- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [x] Breaking change (fix or feature that would cause existing
functionality to change)
- [x] Documentation update

## Checklist
<!-- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [x] I have read the [MCP
Documentation](https://modelcontextprotocol.io)
- [x] My code follows the repository's style guidelines
- [x] New and existing tests pass locally
- [x] I have added appropriate error handling
- [x] I have added or updated documentation as needed

## Additional context
<!-- Add any other context, implementation notes, or design decisions
-->

For more information about the NuGet API see:
https://learn.microsoft.com/en-us/nuget/api/overview#service-index (I
wrote most of the docs).
2025-12-09 07:19:38 -08:00

5 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 the publishing guide.

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
  • Docker/OCI:
    • Docker Hub (docker.io)
    • GitHub Container Registry (ghcr.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.

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.