### Administration/Admin Operations # Admin Operations This is a brief guide for admins and moderators managing content on the registry. All actions should be taken in line with the [moderation policy](../modelcontextprotocol-io/moderation-policy.mdx). ## Prerequisites - Admin account with @modelcontextprotocol.io email - If you are a maintainer and would like an account, ask in the Discord - `gcloud` CLI installed and configured - `curl` and `jq` installed - `kubectl` installed with `gke-gcloud-auth-plugin` (for database access) ## Authentication ```bash # Run this, then run the export command it outputs ./tools/admin/auth.sh ``` ## Edit a Specific Server Version Use this when you need to modify details of a specific version (e.g., fix description, update status, modify packages). ### Step 1: Download Specific Version ```bash export SERVER_NAME="" # e.g., "com.example/my-server" export VERSION="" # e.g., "1.0.0" (optional, defaults to latest) # URL encode the server name (replace / with %2F) ENCODED_SERVER_NAME=$(echo "$SERVER_NAME" | sed 's|/|%2F|g') # Get specific version curl -s "https://registry.modelcontextprotocol.io/v0/servers/${ENCODED_SERVER_NAME}/versions/${VERSION}" > server.json # Or get the latest version (use the special version "latest") curl -s "https://registry.modelcontextprotocol.io/v0/servers/${ENCODED_SERVER_NAME}/versions/latest" > server.json ``` ### Step 2: Make Changes Open `server.json` and edit the specific version details. You cannot change the server name or version number. ### Step 3: Update Version ```bash # Update specific version (requires the full server.json body) curl -X PUT "https://registry.modelcontextprotocol.io/v0/servers/${ENCODED_SERVER_NAME}/versions/${VERSION}" \ -H "Authorization: Bearer ${REGISTRY_TOKEN}" \ -H "Content-Type: application/json" \ -d "$(cat server.json)" ``` To change **only** the status of a version, use the status endpoint instead β€” it does not require the full server configuration: ```bash curl -X PATCH "https://registry.modelcontextprotocol.io/v0/servers/${ENCODED_SERVER_NAME}/versions/${VERSION}/status" \ -H "Authorization: Bearer ${REGISTRY_TOKEN}" \ -H "Content-Type: application/json" \ -d '{"status": "deprecated", "statusMessage": "Superseded by v2"}' ``` ## Edit an Entire Server (All Versions) ### Status Changes Across All Versions A status change applies to every version of a server in a single request. The response reports how many versions were updated in `updatedCount`. ```bash export SERVER_NAME="" # e.g., "com.example/my-server" ENCODED_SERVER_NAME=$(echo "$SERVER_NAME" | sed 's|/|%2F|g') curl -X PATCH "https://registry.modelcontextprotocol.io/v0/servers/${ENCODED_SERVER_NAME}/status" \ -H "Authorization: Bearer ${REGISTRY_TOKEN}" \ -H "Content-Type: application/json" \ -d '{"status": "deleted", "statusMessage": "Removed per moderation policy"}' ``` ### Content Changes Across All Versions Content edits (e.g. scrubbing sensitive text from descriptions) have no bulk endpoint and must be applied per version using the edit endpoint, which takes the full server configuration. #### Step 1: List All Versions ```bash export SERVER_NAME="" # e.g., "com.example/my-server" ENCODED_SERVER_NAME=$(echo "$SERVER_NAME" | sed 's|/|%2F|g') curl -s "https://registry.modelcontextprotocol.io/v0/servers/${ENCODED_SERVER_NAME}/versions" > all_versions.json ``` #### Step 2: Extract Versions ```bash # Extract all versions from the server jq -r '.servers[].server.version' all_versions.json > versions.txt ``` #### Step 3: Apply Changes to Each Version ```bash while read VERSION; do echo "Processing version: $VERSION" # Download the version, edit it, then send the full body back curl -s "https://registry.modelcontextprotocol.io/v0/servers/${ENCODED_SERVER_NAME}/versions/${VERSION}" > version.json # Apply your changes to version.json here, then: curl -X PUT "https://registry.modelcontextprotocol.io/v0/servers/${ENCODED_SERVER_NAME}/versions/${VERSION}" \ -H "Authorization: Bearer ${REGISTRY_TOKEN}" \ -H "Content-Type: application/json" \ -d "$(cat version.json)" done < versions.txt # Clean up temporary files rm -f versions.txt all_versions.json version.json ``` ## Quick Operations ### Get Latest Version of a Server ```bash export SERVER_NAME="" # e.g., "com.example/my-server" ENCODED_SERVER_NAME=$(echo "$SERVER_NAME" | sed 's|/|%2F|g') curl -s "https://registry.modelcontextprotocol.io/v0/servers/${ENCODED_SERVER_NAME}/versions/latest" > latest_version.json export VERSION=$(jq -r '.server.version' latest_version.json) echo "Latest version: $VERSION" ``` ### Takedown a Specific Version ```bash export SERVER_NAME="" # e.g., "com.example/my-server" export VERSION="" # e.g., "1.0.0" export REGISTRY_TOKEN="" REGISTRY_TOKEN="$REGISTRY_TOKEN" SERVER_NAME="$SERVER_NAME" VERSION="$VERSION" ./tools/admin/takedown.sh ``` ### Takedown All Versions of a Server `ALL_VERSIONS=true` marks every version as deleted in a single request. The script requires either `VERSION` or `ALL_VERSIONS` to be set explicitly, so a forgotten `VERSION` cannot take down a whole server by accident. ```bash export SERVER_NAME="" # e.g., "com.example/my-server" export REGISTRY_TOKEN="" REGISTRY_TOKEN="$REGISTRY_TOKEN" SERVER_NAME="$SERVER_NAME" ALL_VERSIONS=true ./tools/admin/takedown.sh ``` ### Takedown the Latest Version Only ```bash export SERVER_NAME="" # e.g., "com.example/my-server" export REGISTRY_TOKEN="" ENCODED_SERVER_NAME=$(echo "$SERVER_NAME" | sed 's|/|%2F|g') # Resolve the latest version, then take down that specific version VERSION=$(curl -s "https://registry.modelcontextprotocol.io/v0/servers/${ENCODED_SERVER_NAME}/versions/latest" | jq -r '.server.version') REGISTRY_TOKEN="$REGISTRY_TOKEN" SERVER_NAME="$SERVER_NAME" VERSION="$VERSION" ./tools/admin/takedown.sh ``` ### Record a Reason with a Takedown ```bash REGISTRY_TOKEN="$REGISTRY_TOKEN" SERVER_NAME="$SERVER_NAME" ALL_VERSIONS=true \ STATUS_MESSAGE="Removed per moderation policy" ./tools/admin/takedown.sh ``` ## Connecting to the Production Database For debugging or data analysis, you can connect directly to the production PostgreSQL database. Use caution and prefer read-only access. ### Prerequisites Install the GKE auth plugin if you haven't already: ```bash gcloud components install gke-gcloud-auth-plugin ``` ### Connect ```bash # Get cluster credentials gcloud container clusters get-credentials mcp-registry-prod --zone us-central1-b --project mcp-registry-prod # Get the database password kubectl get secret registry-pg-app -o jsonpath='{.data.password}' | base64 -d # Port-forward and connect (enter the password from above) kubectl port-forward svc/registry-pg-rw 15432:5432 & sleep 2 psql -h localhost -p 15432 -U app -d app ``` ### Read-Only Access To prevent accidental writes, set your session to read-only after connecting: ```sql SET default_transaction_read_only = on; ``` Any write attempts will fail with an error until you disconnect. ## Notes - **Version-specific changes**: Only affect that particular version - **Server-wide status changes**: `PATCH /v0/servers/{serverName}/status` updates every version in one request - **Server-wide content changes**: Have no bulk endpoint and must be applied to each version individually - **Status vs. edit**: Use `PATCH .../status` to change status alone; the `PUT` edit endpoint requires the full server configuration - **Content scrubbing**: Use the version-specific edit workflow to scrub sensitive content - **Server name**: Cannot be changed in any version (it's the immutable identifier) --- ### Administration/Maintainer Onboarding # Registry Maintainer Onboarding This guide covers onboarding new maintainers to the MCP Registry project. ## Checklist When onboarding a new maintainer, complete the following steps: ### 1. Access Repository (GitHub, Google Workspace) - [ ] Open a PR on the [modelcontextprotocol/access](https://github.com/modelcontextprotocol/access) repository ### 2. MAINTAINERS.md - [ ] Add them to the `MAINTAINERS.md` file in [modelcontextprotocol/modelcontextprotocol](https://github.com/modelcontextprotocol/modelcontextprotocol) ### 3. README.md - [ ] Add them to the "Registry Working Group" section in [README.md](../../README.md) ### 4. Discord - [ ] Invite them to the MCP Discord server - [ ] Ask a Community Moderator or Core Maintainer to add them to the appropriate roles --- ### Contributing/Add Package Registry # Adding a new package registry The MCP Registry project is a **metaregistry**, meaning that it hosts metadata for MCP servers but does not host the code for the servers directly. For local MCP servers, the MCP Registry has pointers in the `packages` node of the [`server.json`](../reference/server-json/generic-server-json.md) schema that refer to packages in supported package managers. The list of supported package managers for hosting MCP servers is defined by the `properties.packages[N].properties.registryType` string enum in the [`server.json` schema](../reference/server-json/draft/server.schema.json). For example, this could be "npm" (for npmjs.com packages) or "pypi" (for PyPI packages). For remote MCP servers, the package registry is not relevant. The MCP client consumes the server via a URL instead of by downloading and running a package. In other words, this document only applies to local MCP servers. For the sake of illustration, this document will use npm (the Node.js package manager) as an example at each step. ## Prerequisites The package registry must meet the following requirements: 1. The package registry supports packaging and executing CLI apps. Local MCP servers use the [stdio transport](https://modelcontextprotocol.io/docs/concepts/transports#standard-input%2Foutput-stdio). - npm CLI tools typically express their CLI commands in the [`bin` property of the package.json](https://docs.npmjs.com/cli/v11/configuring-npm/package-json#bin) 1. The package registry (or associated client tooling) has a widely accepted **single-shot** CLI command. - npm's `npx` tool executes CLI commands using a [documented execution heuristic](https://docs.npmjs.com/cli/v11/commands/npx#description) - For example, the MCP client can map the `server.json` metadata to an `npx` CLI execution, with args and environment variables populated via user input. 1. The package registry supports anonymous package downloads. This allows the MCP client software to use the metadata found in the MCP registry to discover, download, and execute package-based local MCP servers with minimal user intervention. - `npx` by default connects to the public npmjs.com registry, allowing simple consumption of public npm packages. 1. The package registry should support a validation mechanism to verify ownership of the server name. This prevents misattribution and ensures that only the actual package owner can reference their packages in server registrations. For example: - npm requires an `mcpName` field in `package.json` that matches the server name being registered - PyPI requires a `mcp-name:` line in the package README/description - Each registry type must implement a validation mechanism accessible via public API ## Steps These steps may evolve as additional validations or details are discovered and mandated. 1. [Create a feature request issue](https://github.com/modelcontextprotocol/registry/issues/new?template=feature_request.md) on the MCP Registry repository to begin the discussion about adding the package registry. - Example for NuGet: https://github.com/modelcontextprotocol/registry/issues/126 1. Open a PR with the following changes: - Update the [`server.json` schema](../reference/server-json/draft/server.schema.json) - Add your package registry name to the `registryType` example array. - Add your package registry base url to the `registryBaseUrl` example array. - Add the single-shot CLI command name to the `runtimeHint` example value array. - Update the [`openapi.yaml`](../reference/api/openapi.yaml) - Add your package registry name to the `registryType` enum value array. - Add your package registry base url to the `registryBaseUrl` enum value array. - Add the single-shot CLI command name to the `runtimeHint` example value array. - Add a sample, minimal `server.json` to the [`server.json` examples](../reference/server-json/generic-server-json.md). - Implement a registry validator: - Create a new validator file: `internal/validators/registries/yourregistry.go`, following the pattern of existing validators. Examples: - **npm**: Checks for an `mcpName` field in `package.json` that matches the server name - **PyPI**: Searches for `mcp-name: server-name` format in the package README content - **NuGet**: Looks for `mcp-name: server-name` format in the package README file - **Docker/OCI**: Validates a Docker image label `io.modelcontextprotocol.server.name` in the image manifest - Add corresponding unit tests: `internal/validators/registries/yourregistry_test.go` - Register your validator in `internal/validators/validators.go` - Update the publishing documentation: - Add a section for your registry to `docs/modelcontextprotocol-io/package-types.mdx`, following the pattern of the existing registries (npm, PyPI, NuGet, ...) - Include instructions on how to prepare packages for your registry, and an `### Ownership Verification` subsection describing the validation your registry requires --- ### Contributing/Releasing # Release Guide ## Creating a Release 1. **Go to GitHub**: Navigate to https://github.com/modelcontextprotocol/registry/releases 2. **Click "Draft a new release"** 3. **Choose a tag**: Click "Choose a tag" and type a new semantic version that follows the last one available (e.g., `v1.0.0`) 4. **Generate notes**: Click "Generate release notes" to auto-populate the name and description 5. **Publish**: Click "Publish release" The release workflow will automatically: - Build binaries for 6 platforms (Linux, macOS, Windows Γ— amd64, arm64) - Create and push Docker images with `:latest` and `:X.Y.Z` tags (note: no 'v' prefix) - Attach all artifacts to the GitHub release - Generate checksums and signatures ## After Release - Docker images will be available at: - `ghcr.io/modelcontextprotocol/registry:latest` - Latest stable release - `ghcr.io/modelcontextprotocol/registry:X.Y.Z` - Specific release version (note: no 'v' prefix) - Binaries can be downloaded from the GitHub release page ## Deploying to Production Releases do not automatically deploy to production. To deploy a release: 1. Update `mcp-registry:imageTag` in `deploy/Pulumi.gcpProd.yaml` to the desired version (e.g., `1.2.3` - note: no 'v' prefix) 2. Commit and push the change to the `main` branch (either through a PR or by pushing directly to main) 3. The [deploy-production.yml](../../.github/workflows/deploy-production.yml) workflow will automatically trigger and deploy the specified version See the [deployment documentation](../../deploy/README.md) for more details. ## Staging Staging auto-deploys from `main` via [deploy-staging.yml](../../.github/workflows/deploy-staging.yml). It always runs the latest `main` branch code. ## Rollback To rollback production, update `deploy/Pulumi.gcpProd.yaml` to the previous version and push. **Note:** Rollbacks may not work as expected if the release included database migrations, since migrations are not automatically reversed. ## Docker Image Tags The registry publishes different Docker image tags for different use cases: - **`:latest`** - Latest stable release (updated only on releases) - **`:X.Y.Z`** - Specific release versions (e.g., `:1.0.0` - note: no 'v' prefix) - **`:main`** - Rolling tag updated on every push to main branch (continuous deployment) - **`:main-YYYYMMDD-sha`** - Specific development builds from main branch **Note:** Git release tags include the 'v' prefix (e.g., `v1.0.0`), but Docker image tags follow the standard Docker convention and do not include the 'v' prefix (e.g., `1.0.0`). ## Versioning We use semantic versioning (SemVer): - `v1.0.0` - Major release with breaking changes - `v1.1.0` - Minor release with new features - `v1.0.1` - Patch release with bug fixes --- ### Design/Design Principles # MCP Registry Design Principles These are the core constraints that guide the design of the MCP Registry. They are not exhaustive, but they are the most important principles that we will use to evaluate design decisions. ## 1. Single Source of Truth The registry serves as the authoritative metadata repository for publicly-available MCP servers, both locally-run and remote, open source and closed source. Server creators publish once, and all consumers (MCP clients, aggregators, etc.) reference the same canonical data. ## 2. Minimal Operational Burden - Design for low maintenance and operational overhead - Delegate complexity to existing services where possible (GitHub for auth, npm/PyPI/NuGet for packages) - Avoid features that require constant human intervention or moderation - Build for reasonable downtime tolerance (24h acceptable) by having consumers cache data for their end-users ## 3. Vendor Neutrality - No preferential treatment for specific servers or organizations - No built-in ranking, curation, or quality judgments - Let consumers (MCP clients, aggregators) make their own curation decisions ## 4. Meets Industry Security Standards - Leverage existing package registries (npm, PyPI, NuGet, Docker Hub, etc.) for source code distribution, obviating the need to reinvent source code security - Use mechanisms like DNS verification, OAuth to provide base layer of authentication and trust - Implement rate limiting, field validation, and blacklisting to prevent abuse ## 5. Reusable, Extensible Shapes; Not Infrastructure - API shapes (OpenAPI, server.json) designed for reuse - Enable private/internal registries using same formats - Don't mandate infrastructure reuse - focus on interface compatibility ## 6. Progressive Enhancement - Start with MVP that provides immediate value - Build foundation that supports future features - Don't over-engineer for hypothetical needs - Each milestone should be independently valuable --- ### Design/Ecosystem Vision # Ecosystem Vision How the MCP Registry fits into the broader ecosystem and our vision for the future. ## The Registry Ecosystem The MCP registry provides MCP clients with a list of MCP servers, like an app store for MCP servers. (In the future it might do more, like also hosting a list of clients). There are two parts to the registry project: 1. 🟦 **The MCP registry spec**: An [API specification](../reference/api/) that allows anyone to implement a registry. 2. πŸŸ₯ **The Official MCP registry**: A hosted registry following the MCP registry spec at [`registry.modelcontextprotocol.io`](https://registry.modelcontextprotocol.io). This serves as the **authoritative repository** for publicly-available MCP servers. Server creators publish once, and all consumers (MCP clients, aggregators, marketplaces) reference the same canonical data. This is owned by the MCP open-source community, backed by major trusted contributors to the MCP ecosystem such as Anthropic, GitHub, PulseMCP and Microsoft. The registry is built around the [`server.json`](../reference/server-json/) format - a standardized way to describe MCP servers that works across discovery, initialization, and packaging scenarios. In time, we expect the ecosystem to look a bit like this: Note that MCP registries are _metaregistries_. They host metadata about packages, but not the package code or binaries. Instead, they reference other package registries (like NPM, PyPi or Docker) for this. Additionally, we expect clients pull from _subregistries_. These subregistries add value to the registry ecosystem by providing curation, or extending it with additional metadata. The Official MCP registry expects a lot of API requests from ETL jobs from these subregistries. ## Registry vs Package Registry Key distinction: MCP Registries are **metaregistries**. - **Package registries** (npm, PyPI, Docker Hub) host actual code/binaries - **The MCP Registry** hosts metadata pointing to those packages ``` MCP Registry: "weather-server v1.2.0 is at npm:weather-mcp" NPM Registry: [actual weather-mcp package code] ``` ## Official vs Community Registries **Official MCP Registry** (`registry.modelcontextprotocol.io`): - Canonical source for publicly-available servers - Community-owned, backed by trusted contributors - Focuses on discoverability and basic metadata **Subregistries** (Smithery, PulseMCP, etc.): - Add value through curation, ratings, enhanced metadata - ETL from official registry + additional annotations - Serve specific communities or use cases ## How Servers Are Represented Each server entry contains: - **Identity**: Unique name (`io.github.user/server-name`) - **Packages**: Where to download it (`npm`, `pypi`, `docker`, etc.) - **Runtime**: How to execute it (args, env vars) - **Metadata**: Description, capabilities, version This is stored in a standardized `server.json` format that works across discovery, installation, and execution. --- ### Design/Proposed Enhanced Validation # Enhanced Server Validation Design NOTE: This document describes a proposed direction for improving validation of server.json data in the Official Registry. This work is in progress (including open PRs and discussions) in a collaborative process and may change significantly or be abandoned. ## Overview This document outlines the design for implementing comprehensive server validation in the MCP Registry, due to the following concerns: - Currently, the MCP Registry project publishes a server.json schema but does not validate servers against it, allowing non-compliant servers to be published. - There is existing ad-hoc validation that covers some schema compliance, but not all (there are logical errors not identifiable by schema validation and that are not covered by the existing ad hoc validation). - Many servers that do pass validation do not represent best-practices for published servers. This design implements a three-tier validation system: **Schema Validation**, **Semantic Validation**, and **Linter Validation**. ## Current State ### Problems with Current Validation - **No schema validation**: Servers are published without validating against the published schema (and many violate it) - **Incomplete validation**: Ad hoc validation covers only some schema constraints (many published servers have additional logical errors) - **Best Practices not indicated**: Many servers that would pass schema and semantic validation do not represent best practices - **Fail-fast behavior**: Legacy `ValidateServerJSON()` stopped at first error (now replaced with exhaustive validation) - **No path information**: Errors don't specify where in JSON the problem occurs ## Three-Tier Validation System ### Schema Validation (Primary) - **Validates against published schema**: Ensures servers comply with the official server.json schema - **Exhaustive coverage**: Catches all structural and format violations defined in the schema - **Detailed error references**: Shows exact schema rule locations with specific constraint and full path to constraint ### Semantic Validation (Secondary) - **Business logic validation**: Validates only constraints not expressible in JSON Schema - **Registry validation**: Enforce validitiy of registry references (as current) - **Logical Errors**: Enforce logical consistency: format, choices, variable usage, etc ### Linter Validation (Tertiary) - **Best practice recommendations**: Security concerns, style guidelines, naming conventions - **Non-blocking**: Warnings and suggestions, not errors - **Quality improvements**: Helps developers create better servers - **Educational**: Teaches best practices for MCP server development ## Implementation Approach The enhanced validation will be implemented in stages to minimize risk and allow for review and experimentation: ### **Stage 1: Schema Validation and Exhaustive Validation Results (Current)** - Convert existing validators to use and track context and to return exhaustive results - Add `mcp-publisher validate` command that performs exhaustive validation with full schema validation - Implement schema validation with configurable policy for non-current schemas - Schema version validation consolidated in `schema.go` with policy support (Allow/Warn/Error) - `mcp-publisher publish` command validates schema version (rejects empty and non-current schemas) but does not perform full schema validation - API `/v0/publish` endpoint uses `ValidatePublishRequest` which validates schema version and semantic validation, but not full schema validation - **All callers migrated**: All code now uses `ValidateServerJSON()` with `ValidationOptions` directly; legacy wrapper removed - **ValidationResult.FirstError()**: Backward compatibility maintained via `FirstError()` method for code expecting error return type - This allows experimentation and validation of the new model (including schema validation) without impacting production API ### **Future Stages** - Enable full schema validation in `mcp-publisher publish` command (currently only validates schema version) - Enable full schema validation in the `/v0/publish` API endpoint (currently only validates schema version via `ValidatePublishRequest`) - Add `/v0/validate` API endpoint for programmatic validation without publishing (see Validate API Endpoint section below) - Enhance production code to use full validation results: Update `importer.go` and `validate-examples/main.go` to log all issues instead of just first error - Build out comprehensive semantic and linter validation rules (with tests) - Remove redundant manual validators that duplicate schema constraints - Consider migrating tests to check all validation issues instead of just first error (where appropriate) ## Proposed Design ### Design Goals 1. **Exhaustive Feedback**: Collect all validation issues in a single pass, not just the first error 2. **Precise Location**: Provide exact JSON paths for every validation issue 3. **Structured Output**: Return machine-readable validation results with consistent format 4. **Backward Compatibility**: Use `ValidationResult.FirstError()` for code expecting error return type 5. **Extensible**: Support different validation types (json, schema, semantic, linter) and severity levels ### Core Types ``` /* Detailed source-code truncated for AI context efficiency. */ ``` ### Validation Types The `Type` field categorizes validation issues by their source: - **`ValidationIssueTypeJSON`**: JSON parsing errors (malformed JSON syntax) - **`ValidationIssueTypeSchema`**: JSON Schema validation errors (structural/format violations) - **`ValidationIssueTypeSemantic`**: Logical validation errors not enforceable in schema (business rules) - **`ValidationIssueTypeLinter`**: Best practice recommendations, security concerns, style guidelines The `Severity` field indicates the impact level: - **`ValidationIssueSeverityError`**: Critical issues that must be fixed - **`ValidationIssueSeverityWarning`**: Issues that should be addressed - **`ValidationIssueSeverityInfo`**: Suggestions and recommendations The `Reference` field provides context about what triggered the validation issue: - **Schema validation**: Contains the resolved schema path with `$ref` resolution (e.g., `"#/definitions/SseTransport/properties/url/format from: [#/definitions/ServerDetail]/properties/packages/items/[#/definitions/Package]/properties/transport/properties/url/format"`) - **Semantic validation**: Contains rule names for business logic (e.g., `"invalid-server-name"`, `"missing-transport-url"`) - **Linter validation**: Contains rule names for best practices (e.g., `"descriptive-naming"`, `"security-recommendation"`) - **JSON validation**: Contains error type identifiers (e.g., `"json-syntax-error"`, `"invalid-json-format"`) ### ValidationContext The `ValidationContext` tracks the current JSON path during validation, allowing validators to report issues with precise location information. This is essential for providing users with exact paths to problematic fields. #### **Purpose** - **Path tracking**: Builds JSON paths like `"packages[0].transport.url"` as validation traverses nested structures - **Precise error location**: Users can see exactly where validation issues occur - **Immutable building**: Each method returns a new context, preventing accidental mutations #### **Usage Example** ```go // Navigate to packages array, first item, transport field pkgCtx := ctx.Field("packages").Index(0).Field("transport") // Validate transport - any issues will be reported at "packages[0].transport" ``` ### Backward Compatibility Strategy The design maintains perfect backward compatibility by leveraging Go's existing error handling patterns: #### **Error Message Preservation** - **Current validators** use `fmt.Errorf("%w: %s", ErrInvalidRepositoryURL, obj.URL)` - **New validators** use `NewValidationIssueFromError()` which extracts `err.Error()` - **Result**: Identical error messages, ensuring all existing tests pass #### **Constructor Pattern** Following Go conventions used throughout the project: ```go // Standard constructor for manual field setting issue := NewValidationIssue(ValidationIssueTypeLinter, "name", "message", ValidationIssueSeverityWarning, "rule-name") // Constructor that preserves existing error formatting issue := NewValidationIssueFromError(ValidationIssueTypeSemantic, "path", err, "rule-name") ``` #### **Error Interface Compatibility** For code that needs an error return type, use `ValidationResult.FirstError()`: ```go result := ValidateServerJSON(serverJSON, ValidationSchemaVersionAndSemantic) if err := result.FirstError(); err != nil { return err // Returns first error-level issue as error } ``` This maintains compatibility with existing error handling code while providing access to all validation issues. ### New Validation Architecture #### **All Validators Use Context and Return ValidationResult** All existing validators are converted to use `ValidationContext` for precise error location tracking and return `ValidationResult` for comprehensive error collection: ```go func ValidateServerJSON(serverJSON *apiv0.ServerJSON, opts ValidationOptions) *ValidationResult { result := &ValidationResult{Valid: true, Issues: []ValidationIssue{}} // Schema validation based on options if opts.ValidateSchemaVersion || opts.ValidateSchema { schemaResult := validateServerJSONSchema(serverJSON, opts.ValidateSchema, opts.NonCurrentSchemaPolicy) result.Merge(schemaResult) } // Semantic validation (if requested) if opts.ValidateSemantic { // Validate server name - using existing error logic if _, err := parseServerName(*serverJSON); err != nil { issue := NewValidationIssueFromError( ValidationIssueTypeSemantic, "name", err, "invalid-server-name", ) result.AddIssue(issue) } // Validate repository with context if repoResult := validateRepository(&ValidationContext{}, &serverJSON.Repository); !repoResult.Valid { result.Merge(repoResult) } // ... more semantic validation ... } return result } ``` For backward compatibility with code that expects an error return type, `ValidationResult.FirstError()` can be used: ```go result := ValidateServerJSON(serverJSON, ValidationSchemaVersionAndSemantic) if err := result.FirstError(); err != nil { return err } ``` ## Schema Validation The project uses `github.com/santhosh-tekuri/jsonschema/v5` for schema validation with an embedded schema approach. The schema is embedded at compile time using Go's `//go:embed` directive, eliminating the need for file system access and ensuring the schema is always available. ### Schema-First Validation Strategy The enhanced validation system adopts a **schema-first approach** where JSON Schema validation serves as the primary and first validator. This strategy addresses the current duplication between manual/semantic validators and schema constraints. #### **Current Problem: Validation Duplication** The existing system has both: - **Manual/semantic validators**: Custom Go code validating server name format, URL patterns, etc. - **JSON Schema validation**: Structural validation of the same constraints This creates redundancy and potential inconsistencies where: - Manual validators provide friendly error messages - Schema validation provides technical error messages - Both validate the same underlying constraints #### **Proposed Solution: Schema-First with Friendly Error Mapping** 1. **Schema validation runs first** and catches all structural/format issues 2. **Manual validators are eliminated** for constraints already specified in the schema 3. **Schema error messages are mapped to friendly messages** using deterministic schema rule references (if needed) ### Embedded Schema Benefits #### **No File System Dependencies** - **Embedded at compile time**: Schema is included in the binary using `//go:embed schema/*.json` - **No external files**: Eliminates dependency on schema files being present at runtime - **Portable**: Binary contains everything needed for validation #### **Version Consistency** - **Schema version tracking**: `model.CurrentSchemaURL` provides compile-time constant for current schema version - **Version validation**: Schema version validation consolidated in `schema.go` with policy support (Allow/Warn/Error) - **Empty schema handling**: Empty/missing schema fields always generate errors during validation - **Compile-time validation**: Schema is validated when the binary is built - **No version drift**: Schema version is locked to the binary version #### **Performance Benefits** - **No I/O operations**: Schema is already in memory - **Faster startup**: No need to read schema files - **Reduced complexity**: No file path resolution or error handling for missing files ### Rich Error Information The `jsonschema.ValidationError` provides: - **InstanceLocation**: JSON Pointer format (RFC 6901) path to the invalid field (e.g., `"/packages/0/transport/url"`) - **Error**: Detailed error message from schema - **KeywordLocation**: Schema path with $ref segments (e.g., `"/$ref/properties/transport/$ref/properties/url/format"`) - **AbsoluteKeywordLocation**: Resolved schema path (e.g., `"file:///server.schema.json#/definitions/SseTransport/properties/url/format"`) **Path Format Conversion**: JSON Pointer format paths from `InstanceLocation` are converted to bracket notation format to match semantic validation paths. The conversion transforms JSON Pointer paths like `"/packages/0/transport/url"` into bracket notation like `"packages[0].transport.url"`. This ensures consistent path formatting across all validation types (schema, semantic, and linter). #### **Current Error Reference Format** Schema validation errors now include detailed reference information: ``` Reference: #/definitions/Repository/properties/url/format from: [#/definitions/ServerDetail]/properties/repository/[#/definitions/Repository]/properties/url/format ``` This format provides: - **Absolute location**: `#/definitions/Repository/properties/url/format` - the final resolved schema location - **Resolved path**: Shows the complete path with `$ref` segments replaced by their resolved values in square brackets - **Full context**: Users can see exactly which schema rule triggered the error and how it was reached #### **Error Message Quality** The current schema validation errors are generally quite readable: ``` [error] repository.url (schema) '' has invalid format 'uri' Reference: #/definitions/Repository/properties/url/format from: [#/definitions/ServerDetail]/properties/repository/[#/definitions/Repository]/properties/url/format ``` #### **Future Error Message Enhancement** If we encounter situations where schema validation errors need to be more user-friendly, we have full access to: - **`KeywordLocation`**: The schema path to the validating rule - **`AbsoluteKeywordLocation`**: The absolute schema location after `$ref` resolution - **`InstanceLocation`**: The JSON Pointer format path (e.g., `"/packages/0/transport/url"`) which is converted to bracket notation (e.g., `"packages[0].transport.url"`) for consistency with semantic validation - **`Message`**: The original schema validation error message - **Complete reference stack**: The entire resolved path showing how the error was reached This allows us to build better, more descriptive error messages if needed, while maintaining the current high-quality error references. ### Integration with ValidateServerJSON ``` /* Detailed source-code truncated for AI context efficiency. */ ``` ### Schema Version Validation Schema version validation is consolidated in `validateServerJSONSchema()` (now private) in `schema.go`: - **Empty schema check**: Always performed when schema validation is requested, always generates an error - **Schema file existence check**: Always performed when schema validation is requested - verifies the schema file exists in embedded schemas, even when not performing full validation - **Schema version policy**: Controls how non-current schemas are handled (via `ValidationOptions.NonCurrentSchemaPolicy`): - `SchemaVersionPolicyAllow`: Non-current schemas are allowed with no warning - `SchemaVersionPolicyWarn`: Non-current schemas are allowed but generate a warning - `SchemaVersionPolicyError`: Non-current schemas are rejected with an error - **Full schema validation**: Only performed if `performValidation` is `true` The `mcp-publisher publish` command validates schema version (rejects empty, non-existent, and non-current schemas) but does not perform full schema validation. The `mcp-publisher validate` command performs full schema validation with `SchemaVersionPolicyWarn` (warns about non-current schemas but doesn't error). ### Request Validation Functions Two consolidated validation functions in `validators` package handle publish and update requests: - **`ValidatePublishRequest()`**: Validates publisher extensions, server JSON structure (via `ValidateServerJSON`), and registry ownership (if enabled) - **`ValidateUpdateRequest()`**: Validates server JSON structure (via `ValidateServerJSON`) and registry ownership (if enabled), with option to skip registry validation for deleted servers Both functions use `ValidateServerJSON()` with `ValidationSchemaVersionAndSemantic` and `FirstError()` for backward-compatible error handling. Registry ownership validation is extracted into a shared `validateRegistryOwnership()` helper function. ### Testing with Draft or Custom Schemas The validation system supports testing against draft schemas or custom schema versions by embedding them in the validators package. #### Setup Steps 1. **Copy the schema file**: Copy your schema file (e.g., `docs/reference/server-json/draft/server.schema.json`) to `internal/validators/schemas/{version}.json` - Example: Copy to `internal/validators/schemas/draft.json` for draft schema testing - Ensure the schema file's `$id` field matches: `https://static.modelcontextprotocol.io/schemas/{version}/server.schema.json` - For draft schema, the `$id` should be: `https://raw.githubusercontent.com/modelcontextprotocol/registry/main/docs/reference/server-json/draft/server.schema.json` 2. **Rebuild**: Recompile the Go binary to embed the new schema file (schemas are embedded at compile time) 3. **Use in server.json**: Reference the schema version in your `server.json` file: ```json { "$schema": "https://static.modelcontextprotocol.io/schemas/draft/server.schema.json", ... } ``` #### Schema Version Identifier Rules Schema version identifiers can contain: - **Letters**: A-Z, a-z - **Digits**: 0-9 - **Special characters**: Hyphen (-), underscore (_), tilde (~), period (.) Examples of valid identifiers: `2025-10-17`, `draft`, `test-v1.0`, `custom_schema~1.2.3` #### Non-Current Schema Policy When testing with draft or custom schemas, they will be treated as **non-current** schemas (since they don't match `model.CurrentSchemaURL`), which triggers the `NonCurrentSchemaPolicy` behavior: - **`SchemaVersionPolicyAllow`**: Draft schemas are allowed with no warning - **`SchemaVersionPolicyWarn`**: Draft schemas are allowed but generate a warning (default for `ValidationAll` and `ValidationSchemaVersionAndSemantic`) - **`SchemaVersionPolicyError`**: Draft schemas are rejected with an error (default for `ValidationSchemaVersionOnly`) #### Treating Draft as Current Schema To test with a draft schema as if it were the current schema (no warnings/errors about non-current version): 1. Temporarily update `model.CurrentSchemaVersion` in `pkg/model/constants.go`: ```go const ( CurrentSchemaVersion = "draft" // Temporarily set for testing CurrentSchemaURL = "https://static.modelcontextprotocol.io/schemas/" + CurrentSchemaVersion + "/server.schema.json" ) ``` 2. Rebuild and test 3. **Important**: Revert the change before committing - `model.CurrentSchemaVersion` should always point to the latest official schema version #### Example: Testing with Draft Schema ```bash # 1. Copy draft schema cp docs/reference/server-json/draft/server.schema.json internal/validators/schemas/draft.json # 2. Verify the $id field in draft.json is correct # Should be: "https://raw.githubusercontent.com/modelcontextprotocol/registry/main/docs/reference/server-json/draft/server.schema.json" # 3. Rebuild go build ./... # 4. Use in server.json # Set "$schema": "https://static.modelcontextprotocol.io/schemas/draft/server.schema.json" # 5. Validate mcp-publisher validate server.json ``` **Note**: The draft schema will be validated successfully, but you may see a warning about it not being the current schema version unless you temporarily update `model.CurrentSchemaVersion` as described above. ### Discriminated Union Error Consolidation The schema uses `anyOf` for discriminated unions (transport, argument, remote), which causes noisy error messages when validation fails. When a transport/argument/remote doesn't match its specified type, `anyOf` validation tries all variants and reports errors for each one that doesn't match. **Problem Example**: If you have an "sse" transport with no url, you get errors for all transport types: 1. [error] packages[0].transport.type (schema) value must be "stdio" Reference: #/definitions/StdioTransport/properties/type/enum 2. [error] packages[0].transport (schema) missing required fields: 'url' Reference: #/definitions/StreamableHttpTransport/required 3. [error] packages[0].transport.type (schema) value must be "streamable-http" Reference: #/definitions/StreamableHttpTransport/properties/type/enum 4. [error] packages[0].transport (schema) missing required fields: 'url' Reference: #/definitions/SseTransport/required **Solution Strategy**: Since we cannot modify the schema (it's managed in the static repository), we'll detect and consolidate these `anyOf` error patterns in the validation error processing code (`addDetailedErrors` in `schema.go`). **Detection Strategy**: - Identify groups of errors at the same JSON path (e.g., `packages[0].transport`) - Detect pattern of multiple "type must be X" errors or multiple "missing required fields" errors from different schema definitions - Extract the actual `type` value from the JSON being validated - Filter out errors from non-matching transport/argument/remote definitions - Consolidate remaining errors into a single, actionable error message **Implementation Approach**: - Add logic in `addDetailedErrors()` or a post-processing function to detect `anyOf` error clusters - Group errors by instance location and analyze error patterns - Identify the intended type from the JSON data - Filter/consolidate errors to only show relevant issues for the actual type specified - Preserve all other validation errors unchanged This approach allows us to provide clearer error messages without modifying the schema, and can be applied to transport, argument, and remote validation. **Future Enhancement**: If the schema is updated to use `if/then/else` discriminated unions in the future, this consolidation logic can be removed, but it provides immediate value without requiring schema changes. ## Implementation Status ### βœ… Completed Features #### **Core Validation System** - [x] **ValidationIssue and ValidationResult types**: Complete with all required fields - [x] **ValidationContext**: Immutable context building for JSON path tracking - [x] **Constructor functions**: `NewValidationIssue()` and `NewValidationIssueFromError()` with consistent parameter naming - [x] **Helper methods**: Context building, result merging, and path construction #### **Schema Validation Integration** - [x] **JSON Schema validation**: Using existing `jsonschema/v5` library - [x] **Error conversion**: Schema errors converted to `ValidationIssue` format - [x] **$ref resolution**: Sophisticated resolution showing complete schema path with resolved references - [x] **Comprehensive testing**: Full test coverage for schema validation scenarios - [x] **Embedded schema**: Schema embedded at compile time using `//go:embed` directive - [x] **Path format normalization**: JSON Pointer paths converted to bracket notation to match semantic validation format (e.g., `/packages/0/transport` β†’ `packages[0].transport`) #### **Enhanced Error References** - [x] **Resolved schema paths**: Shows complete path with `$ref` segments replaced by resolved values - [x] **Incremental resolution**: Each `$ref` resolved in context of previous resolution - [x] **Human-readable format**: Clear indication of schema rule location and resolution chain - [x] **Consistent output**: All schema errors use the same reference format #### **Testing and Quality** - [x] **Unit tests**: Comprehensive test coverage for all new functionality - [x] **Integration tests**: End-to-end validation testing - [x] **Backward compatibility**: Existing validation continues to work #### **Caller Migration** - [x] **Function rename**: `ValidateServerJSONExhaustive` renamed to `ValidateServerJSON` (now takes `ValidationOptions` parameter) - [x] **Legacy wrapper removed**: Old `ValidateServerJSON()` wrapper that returned `error` removed - [x] **All callers migrated**: All production code and tests now use `ValidateServerJSON()` with `ValidationOptions` directly - [x] **FirstError() helper**: `ValidationResult.FirstError()` method added for backward compatibility with error return types - [x] **Request validators consolidated**: `ValidatePublishRequest` and `ValidateUpdateRequest` moved to validators package with shared `validateRegistryOwnership` helper ### πŸ”„ In Progress #### **Schema-First Validation Strategy** - [x] **Schema validation integration**: `ValidateServerJSON()` runs schema validation first - [x] **CLI integration**: Schema validation enabled in `mcp-publisher validate` command - [x] **Schema version validation**: Consolidated in `schema.go` with policy support (Allow/Warn/Error) - [x] **Schema file existence check**: Schema version validation verifies schema file exists in embedded schemas - [x] **Publish command schema checks**: `mcp-publisher publish` validates schema version (rejects empty, non-existent, and non-current schemas) - [x] **API endpoint validation**: `/v0/publish` uses `ValidatePublishRequest` which validates schema version and semantic validation - [ ] **Full schema validation in publish**: Enable full schema validation in `mcp-publisher publish` command - [ ] **Full schema validation in API**: Enable full schema validation in `/v0/publish` API endpoint - [ ] **Discriminated union error consolidation**: Detect and filter/consolidate noisy `anyOf` errors for transport, argument, and remote validation to show only relevant errors for the actual type - [ ] **Error message mapping**: Map technical schema errors to user-friendly messages (if needed) - [ ] **Validator migration**: Move from manual validators to schema-first approach ### πŸ“‹ Pending #### **Migration Strategy** - [ ] **Phase 1: Identify Schema Coverage**: Audit existing manual validators against schema constraints - [ ] **Phase 2: Implement Error Mapping (Optional)**: Create mapping function for schema error messages (only if current messages are insufficient) - [ ] **Phase 3: Error Consolidation**: Implement logic to detect and consolidate noisy `anyOf` errors from discriminated unions (transport, argument, remote) - [ ] **Phase 4: Enable Schema-First Validation**: Update tests to expect schema validation errors instead of semantic errors; Enable schema validation in publish API - [ ] **Phase 5: Clean Up Redundant Validators**: Remove manual validators that duplicate schema constraints - [ ] **Phase 6: Add Enhanced Semantic and Linter Rules**: Review and implement specific rules from [MCP Registry Validator linter guidelines](https://github.com/TeamSparkAI/ToolCatalog/blob/main/packages/mcp-registry-validator/linter.md) #### **Command Integration** - [x] **CLI updates**: `mcp-publisher validate` command uses detailed validation with full schema validation - [x] **Publish command**: `mcp-publisher publish` validates schema version (rejects empty, non-existent, and non-current schemas) - [x] **Shared validation logic**: Both commands use `runValidationAndPrintIssues` to eliminate duplication - [x] **Caller migration**: All callers migrated to use `ValidateServerJSON()` with `ValidationOptions` directly - [x] **Request validation consolidation**: `ValidatePublishRequest` and `ValidateUpdateRequest` consolidated in validators package - [ ] **Enhanced error reporting**: Update production code (importer, validate-examples tool) to log all issues instead of just first error - [ ] **Output formatting**: Add JSON output format options - [ ] **Filtering options**: Add severity and type filtering #### **Validate API Endpoint** - [ ] **POST /v0/validate endpoint**: API endpoint for validating server.json without publishing #### **Documentation and Polish** - [ ] **API documentation**: Update API documentation with new validation types ### 🎯 Key Achievements 1. **Comprehensive Error Collection**: All validation issues collected in single pass 2. **Precise Error Location**: Exact JSON paths for every validation issue 3. **Schema Integration**: Full JSON Schema validation with detailed error references 4. **Backward Compatibility**: Existing validation continues to work unchanged 5. **Type Safety**: Constrained types prevent invalid validation issue creation 6. **Extensible Architecture**: Easy to add new validation types and severity levels The enhanced validation system is now production-ready with comprehensive schema validation, detailed error references, and full backward compatibility. ## Example Usage ### JSON Output Format ```json { "valid": false, "issues": [ { "type": "json", "path": "", "message": "invalid JSON syntax at line 5, column 12", "severity": "error", "reference": "json-syntax-error" }, { "type": "semantic", "path": "name", "message": "server name must be in format 'dns-namespace/name'", "severity": "error", "reference": "invalid-server-name" }, { "type": "semantic", "path": "packages[0].transport.url", "message": "url is required for streamable-http transport type", "severity": "error", "reference": "missing-transport-url" }, { "type": "schema", "path": "packages[1].environmentVariables[0].name", "message": "string does not match required pattern", "severity": "error", "reference": "#/definitions/EnvironmentVariable/properties/name/pattern from: [#/definitions/ServerDetail]/properties/packages/items/[#/definitions/Package]/properties/environmentVariables/items/[#/definitions/EnvironmentVariable]/properties/name/pattern" }, { "type": "linter", "path": "packages[1].description", "message": "consider adding a more descriptive package description", "severity": "warning", "reference": "descriptive-package-description" } ] } ``` **Note**: The JSON output still uses string values for `type` and `severity` fields for JSON serialization compatibility, but the Go code uses the typed constants for type safety. ### CLI Usage ```bash # Basic validation mcp-publisher validate server.json # JSON output format mcp-publisher validate --format json server.json # Filter by severity mcp-publisher validate --severity error server.json # Include schema validation mcp-publisher validate --schema server.json ``` ## Benefits and Achievements ### βœ… Comprehensive Feedback - **Exhaustive error collection**: See all validation issues at once, not just the first error - **Better developer experience**: No need to fix errors one by one - **Precise error location**: JSON paths show exactly where issues occur in large JSON files - **Structured output**: JSON format for tooling integration and machine-readable error information ### βœ… Schema-First Validation - **Primary validator**: Schema validation catches all structural and format violations defined in the schema - **Semantic validation only for gaps**: Covers business logic that cannot be expressed in JSON Schema - **Standards compliance**: Ensures server.json follows the official schema - **Detailed error messages**: Exact JSON paths and resolved schema references ### βœ… Backward Compatibility - **Backward compatibility**: Use `ValidationResult.FirstError()` for code expecting error return type - **Error interface compatibility**: Leverages Go's error interface and existing error constants - **Constructor pattern**: Follows established project conventions - **No breaking changes**: All error handling code remains functional ### βœ… Extensible Architecture - **Easy to add new validation types**: Schema, semantic, linter validation - **Easy to add new severity levels**: Error, warning, info - **Easy to add filtering and formatting options**: By type, severity, path pattern - **Type safety**: Constrained types prevent invalid validation issue creation ### βœ… Schema-First Strategy Benefits - **Eliminates duplication**: Single source of truth for structural constraints - **Better error messages**: Schema validation provides precise JSON paths with deterministic mapping - **Maintainability**: Schema changes automatically update validation - **Standards compliance**: Ensures validation matches official schema exactly ## Technical Design ### Architecture Overview The enhanced validation system uses a **schema-first approach** with comprehensive error collection and precise location tracking. The system is designed for maximum backward compatibility while providing extensive new capabilities. #### **Error Interface Compatibility** - **Leverages existing error constants**: `ErrInvalidRepositoryURL`, `ErrVersionLooksLikeRange`, etc. - **Preserves error wrapping**: Uses `fmt.Errorf("%w: %s", err, context)` pattern - **Maintains error.Is() compatibility**: Existing error checking continues to work - **No breaking changes**: All error handling code remains functional #### **Constructor Pattern** Following established Go conventions in the project: - **`NewValidationIssue()`**: Standard constructor following `NewXxx()` pattern - **`NewValidationIssueFromError()`**: Specialized constructor for error conversion - **Consistent with project**: Matches patterns used in `NewConfig()`, `NewServer()`, etc. - **Type safety**: Compile-time validation of required fields #### **Context Passing Architecture** - **Immutable context building**: `ctx.Field("name").Index(0)` pattern - **Clean composition**: Validators focus on validation, not path building - **Reusable validators**: Same validator can be called with different contexts - **No global state**: Thread-safe validation with explicit context #### **Type Safety with Constrained Values** Following Go best practices used throughout the project: - **Typed string constants**: `ValidationIssueType`, `ValidationIssueSeverity` prevent invalid values - **Compile-time validation**: IDE autocomplete and error checking - **JSON compatibility**: Still serializes as strings for API compatibility - **Refactoring safety**: Rename constants without breaking code - **Consistent with project**: Matches patterns used in `Status`, `Format`, `ArgumentType` ### Performance Considerations - **Slightly slower than fail-fast validation**: Acceptable trade-off for better user experience - **Memory usage increases with error collection**: Manageable for typical server.json files - **Schema validation performance**: Embedded schema eliminates I/O operations ### Testing Strategy - **Unit tests**: Each validator with context - **Integration tests**: End-to-end validation testing - **Backward compatibility tests**: Ensure existing code continues to work - **Performance benchmarks**: Validate acceptable performance characteristics --- ## Appendix: Future Enhancements ### Additional Validation Types - **Linter rules**: Best practices and style guidelines - **Warning level**: Non-critical issues - **Info level**: Suggestions and improvements ### Advanced Features - **Error filtering**: By type, severity, path pattern - **Output formatting**: Human-readable, JSON, XML - **Configuration**: Custom validation rules - **IDE integration**: Real-time validation feedback ### Tooling Integration - **WASM package**: Browser-based validation - **VS Code extension**: Real-time validation - **CI/CD integration**: Automated validation in pipelines - **API endpoint**: Validation as a service (see Validate API Endpoint section below) ## Validate API Endpoint ### Overview A REST API endpoint (`POST /v0/validate`) that validates `server.json` files without publishing them to the registry. This endpoint provides programmatic access to the same validation logic used by the CLI commands, returning structured validation results in JSON format. ### Use Cases - **CI/CD Pipelines**: Validate server.json files before attempting to publish - **Editor/IDE Integrations**: Real-time validation feedback in development tools - **Web UIs**: Validate files in browser-based interfaces - **Pre-publish Checks**: Validate before authentication/publishing workflow - **Validation as a Service**: Allow external tools to validate server.json format ### Implementation #### Endpoint Specification **Endpoint**: `POST /v0/validate` **Authentication**: None required (read-only operation) **Content-Type**: `application/json` #### Request Request body should be a valid `ServerJSON` object: ```json { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json", "name": "io.example/server", "version": "1.0.0", ... } ``` #### Response Returns a `ValidationResult` in JSON format: ```json { "valid": false, "issues": [ { "type": "schema", "path": "packages[0].transport.url", "message": "missing required field: 'url'", "severity": "error", "reference": "#/definitions/SseTransport/required" }, { "type": "semantic", "path": "name", "message": "server name must be in format 'dns-namespace/name'", "severity": "error", "reference": "invalid-server-name" } ] } ``` **HTTP Status Codes**: - `200 OK`: Validation completed successfully (regardless of whether valid or invalid) - `400 Bad Request`: Malformed JSON or invalid request format Note: A `200 OK` status does not mean the server.json is valid - check the `valid` field in the response body. #### Implementation Details **Location**: `internal/api/handlers/v0/validate.go` **Handler Function**: - Accepts `ServerJSON` in request body - Calls `validators.ValidateServerJSON(serverJSON, validators.ValidationAll)` - Returns `ValidationResult` as JSON response - Uses Huma framework (same as publish endpoint) for request/response handling **Key Differences from Publish Endpoint**: - No authentication required (read-only) - Does not save to database - Returns structured validation results instead of published server response - Returns warnings, not just errors (useful for comprehensive feedback) **Reuses Existing Infrastructure**: - Same validation functions as CLI commands - Same `ValidationResult` type - Same issue types and severity levels - Consistent validation behavior across CLI and API ### Testing Strategy #### Unit Tests Test handler function with mocked dependencies: - Valid server.json β†’ `valid: true, issues: []` - Invalid server.json β†’ `valid: false` with specific issues - Schema errors β†’ issues with `type: "schema"` - Semantic errors β†’ issues with `type: "semantic"` - Empty schema β†’ `schema-field-required` issue - Non-current schema β†’ `schema-version-deprecated` issue - Multiple issues β†’ all issues returned in response - Malformed JSON β†’ proper error handling #### Integration Tests Follow patterns from `publish_integration_test.go`: - Start test server - Send HTTP POST requests with various `server.json` payloads - Assert response JSON matches expected `ValidationResult` structure - Verify HTTP status codes (200 for valid requests, 400 for malformed) - Test both valid and invalid inputs - Reuse test fixtures from `validation_detailed_test.go` #### Test Infrastructure - Reuse existing test server setup - Use same patterns as `test_endpoints.sh` for manual testing - Leverage existing validation test cases ### Future Enhancements - **Query Parameters**: Optional parameters to filter by issue type or severity - **Partial Validation**: Validate specific sections (e.g., only schema, only semantic) - **Format Options**: Request different output formats (detailed vs. summary) - **Batch Validation**: Validate multiple server.json files in one request --- ### Design/Roadmap # MCP Registry Roadmap This is a high-level roadmap for the MCP Registry. It is subject to change and not exhaustive, but it outlines the general thinking of the sequencing and scope of our work in this repository. This roadmap may occasionally drift out of date. Please review [Issues](https://github.com/modelcontextprotocol/registry/issues) (and corresponding Labels) for the most current work in progress. ## Current Status > [!NOTE] > **The phase labelling below is out of date as of 2026-08-10.** The registry launched in preview on > 2025-09-08 and the v0.1 API entered a freeze on 2025-10-24, so "Go-Live" has already happened. The > phases are retained for historical context. See > [Issues](https://github.com/modelcontextprotocol/registry/issues) for current work. The initial version of the MCP Registry is actively being developed. The initial focus is on delivering a REST API to which server creators can publish, and aggregator/marketplace consumers can ETL. ## Phase 1: MVP/Go-Live See the [go-live blocker issues](https://github.com/modelcontextprotocol/registry/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22go-live%20blocker%22). ## Backlog (Future Work, may be moved to out of scope) - [ ] UI implementation - [ ] Store and surface other data besides servers (e.g. [clients](https://modelcontextprotocol.io/clients), resources) - [ ] Download count tracking - [ ] Internationalization (i18n) ## Out of Scope (Not Planned) - **Source code hosting**: The registry will never host actual server code - **Quality rankings**: No built-in server quality assessments or rankings - **Curation**: No editorial decisions about which servers are "better" - **Unified runtime**: Not solving how servers are executed - **Server hosting**: The registry does not provide hosting for servers - **Search engine**: The registry will not provide a commercial grade search engine for servers - **Server tags or categories**: Not supported, to reduce moderation burden - **Server rankings**: The registry will not rank servers by subjective measures of quality --- ### Design/Tech Architecture # MCP Registry Technical Architecture > [!WARNING] > **This document has drifted from the shipped system and is kept as a design record.** > For the architecture as deployed, see [deploy/README.md](../../deploy/README.md); for the API as > implemented, see the [API reference](../reference/api/official-registry-api.md). > > Known inaccuracies below, as of 2026-08-10: > - The registry is not deployed via a Helm chart. It is a plain Kubernetes `Deployment` created by > Pulumi; Helm is used only for third-party components (ingress-nginx, cert-manager, > cloudnative-pg, k8up, monitoring). > - The database diagram shows a `StatefulSet` on port `27017` (MongoDB). The database is PostgreSQL > on `5432`, provisioned by the CloudNativePG operator. > - The DNS verification sequence (`mcp verify-domain`, `POST /verify-domain`, > `POST /verify-domain/check`, server-issued challenge tokens) describes a design that was never > built. The shipped flow signs a timestamp with the operator's key and posts it to > `POST /v0/auth/dns`, with the public key published in a `v=MCPv1; k=...; p=...` TXT record. > - No `/admin/*` routes exist. Admin actions use the regular server endpoints with an > admin-permissioned token. > - The CLI is `mcp-publisher`, not `mcp`, and publishing posts to `/v0/publish`. > - The database stores servers and server extensions only; it holds no user authentication state > and no DNS verification records. This document describes the technical architecture of the MCP Registry, including system components, deployment strategies, and data flows. ## System Overview The MCP Registry is designed as a lightweight metadata service that bridges MCP server creators with consumers (MCP clients and aggregators). ## Core Components ### REST API (Go) The main application server implemented in Go, providing: - Public read endpoints for server discovery - Authenticated write endpoints for server publication - GitHub OAuth integration (extensible to other providers) - DNS verification system (optional for custom namespaces) ### Database (PostgreSQL) Primary data store for: - Versioned server metadata (server.json contents) - User authentication state - DNS verification records ### CDN Layer Critical for scalability: - Caches all public read endpoints - Reduces load on origin servers - Enables global distribution - Designed for daily consumer polling patterns ### CLI Tool Developer interface for: - Server publication workflow - GitHub OAuth flow - DNS verification ## Deployment Architecture ### Kubernetes Deployment (Helm) The registry is designed to run on Kubernetes using Helm charts: ```mermaid graph TB subgraph "Kubernetes Cluster" subgraph "Namespace: mcp-registry" subgraph "Registry Service" LB[Load Balancer
:80] RS[Registry Service
:8080] RP1[Registry Pod 1] RP2[Registry Pod 2] RP3[Registry Pod N] end subgraph "Database Service" DBS[DB Service
:27017] SS[StatefulSet] PV[Persistent Volume] end subgraph "Secrets" GHS[GitHub OAuth Secret] end end end LB --> RS RS --> RP1 RS --> RP2 RS --> RP3 RP1 --> DBS RP2 --> DBS RP3 --> DBS DBS --> SS SS --> PV RP1 -.-> GHS RP2 -.-> GHS RP3 -.-> GHS ``` ## Data Flow Patterns ### 1. Server Publication Flow ```mermaid sequenceDiagram participant Dev as Developer participant CLI as CLI Tool participant API as Registry API participant DB as Database participant GH as GitHub participant DNS as DNS Provider Dev->>CLI: mcp publish server.json CLI->>CLI: Validate server.json CLI->>GH: OAuth flow GH-->>CLI: Access token CLI->>API: POST /servers API->>GH: Verify token API->>DNS: Verify domain (if applicable) API->>DB: Store metadata API-->>CLI: Success CLI-->>Dev: Published! ``` ### 2. Consumer Discovery Flow ```mermaid sequenceDiagram participant Client as MCP Client Host App participant INT as Intermediary
(Marketplace/Aggregator) participant CDN as CDN Cache participant API as Registry API participant DB as Database Note over INT,CDN: Daily ETL Process INT->>CDN: GET /servers alt Cache Hit CDN-->>INT: Cached response else Cache Miss CDN->>API: GET /servers API->>DB: Query servers DB-->>API: Server list API-->>CDN: Response + cache headers CDN-->>INT: Response end INT->>INT: Process & enhance data INT->>INT: Store in local cache Note over Client,INT: Real-time Client Access Client->>INT: Request server list INT-->>Client: Curated/enhanced data ``` ### 3. DNS Verification Flow ```mermaid sequenceDiagram participant User as User participant CLI as CLI Tool participant API as Registry API participant DNS as DNS Provider participant DB as Database User->>CLI: mcp verify-domain example.com CLI->>API: POST /verify-domain API->>API: Generate verification token API->>DB: Store pending verification API-->>CLI: TXT record: mcp-verify=abc123 CLI-->>User: Add TXT record to DNS User->>DNS: Configure TXT record User->>CLI: Confirm added CLI->>API: POST /verify-domain/check API->>DNS: Query TXT records DNS-->>API: TXT records API->>API: Validate token API->>DB: Store verification API-->>CLI: Domain verified CLI-->>User: Success! ``` ### 4. Admin OIDC Authentication Flow For registry administration, users with @modelcontextprotocol.io Google Cloud Identity accounts can authenticate using OIDC: ```mermaid sequenceDiagram participant Admin as Admin User participant CLI as Admin CLI participant GCP as Google Cloud Identity participant API as Registry API Admin->>CLI: Request admin token CLI->>GCP: gcloud auth print-identity-token --audiences=mcp-registry GCP-->>CLI: ID Token (with hd: "modelcontextprotocol.io") CLI->>API: POST /v0.1/auth/oidc {"oidc_token": "eyJ..."} API->>GCP: Verify token signature (JWKS) API->>API: Validate claims (issuer, audience, hd) API->>API: Grant admin permissions (edit: *, publish: *) API-->>CLI: Registry JWT Token CLI->>API: POST /admin/* (with Registry JWT) API->>API: Validate JWT + permissions API-->>CLI: Admin operation success ``` **Usage:** ```bash # Get Google Cloud Identity token ID_TOKEN=$(gcloud auth print-identity-token) # Exchange for Registry JWT token REGISTRY_TOKEN=$(curl -X POST /v0.1/auth/oidc \ -H "Content-Type: application/json" \ -d '{"oidc_token": "'$ID_TOKEN'"}' | jq -r .registry_token) # Use for admin operations curl -H "Authorization: Bearer $REGISTRY_TOKEN" /v0.1/... ``` --- ### Modelcontextprotocol Io/About --- title: The MCP Registry sidebarTitle: About --- The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). The MCP Registry is the official centralized metadata repository for publicly accessible MCP servers, backed by major trusted contributors to the MCP ecosystem such as Anthropic, GitHub, PulseMCP, and Microsoft. The MCP Registry provides: - A single place for server creators to publish metadata about their servers - Namespace management through DNS verification - A REST API for MCP clients and aggregators to discover available servers - Standardized installation and configuration information Server metadata is stored in a standardized [`server.json` format](https://github.com/modelcontextprotocol/registry/blob/main/docs/reference/server-json/draft/server.schema.json), which contains: - The server's unique name (e.g., `io.github.user/server-name`) - Where to locate the server (e.g., npm package name, remote server URL) - Execution instructions (e.g., command-line args, env vars) - Other discovery data (e.g., description, server capabilities) ## The MCP Registry Ecosystem The MCP Registry is part of an ecosystem that looks something like: ### Relationship with Package Registries Package registries β€” such as npm, PyPI, and Docker Hub β€” host packages with code and binaries. The MCP Registry hosts metadata that points to those packages. For example, a `weather-mcp` package could be hosted on npm, and metadata in the MCP Registry could map the "weather v1.2.0" server to `npm:weather-mcp`. The [Package Types guide](./package-types) lists the supported package types and registries. More package registries may be supported in the future based on community demand. If you are interested in building support for a package registry, please [open an issue](https://github.com/modelcontextprotocol/registry). ### Relationship with Server Developers The MCP Registry supports both open-source and closed-source servers. Server developers can publish their server's metadata to the registry as long as the server's installation method is publicly available (e.g., an npm package or a Docker image on a public registry) _or_ the server itself is publicly accessible (e.g., a remote server that is not restricted to private networks). The MCP Registry **does not** support private servers. Private servers are those that are only accessible to a narrow set of users. For example, servers published on a private network (like `mcp.acme-corp.internal`) or on private package registries (e.g. `npx -y @acme/mcp --registry https://artifactory.acme-corp.internal/npm`). If you want to publish private servers, we recommend that you host your own private MCP registry and add them there. ### Relationship with Downstream Aggregators The MCP Registry is intended to be consumed primarily by downstream aggregators, such as MCP server marketplaces. The metadata hosted by the MCP Registry is deliberately unopinionated. Downstream aggregators can provide curation or additional metadata such as community ratings. We expect that downstream aggregators will use the MCP Registry API to pull new metadata on a regular but infrequent basis (for example, once per hour). See the [MCP Registry Aggregators guide](./registry-aggregators) for more information. ### Relationship with Other MCP Registries In addition to a public REST API, the MCP Registry defines an [OpenAPI spec](https://github.com/modelcontextprotocol/registry/blob/main/docs/reference/api/openapi.yaml) that other MCP registries can implement in order to provide a standardized interface for MCP host applications. We expect that many downstream aggregators will implement this interface. Private MCP registries can implement it as well to benefit from existing host application support. Note that the official MCP Registry codebase is **not** designed for self-hosting, and the registry maintainers cannot provide support for this use case. If you choose to fork it, you would need to maintain and operate it independently. ### Relationship with MCP Host Applications The MCP Registry is not intended to be directly consumed by host applications. Instead, host applications should consume other MCP registries, such as downstream marketplaces, via a REST API conforming to the official MCP Registry's OpenAPI spec. ## Trust and Security ### Verifying Server Authenticity The MCP Registry uses namespace authentication to ensure that servers come from their claimed sources. Server names follow a reverse DNS format (like `io.github.username/server` or `com.example/server`) that ties them to verified GitHub accounts or domains. This namespace system ensures that only the legitimate owner of a GitHub account or domain can publish servers under that namespace, providing trust and accountability in the ecosystem. For details on authentication methods, see the [Authentication guide](./authentication). ### Security Scanning The MCP Registry delegates security scanning to: - **Underlying package registries** β€” npm, PyPI, Docker Hub, and other package registries perform their own security scanning and vulnerability detection. - **Downstream aggregators** β€” MCP Registry aggregators and marketplaces can implement additional security checks, ratings, or curation. The MCP Registry focuses on namespace authentication and metadata hosting, while relying on the broader ecosystem for security scanning of actual server code. ### Spam Prevention The MCP Registry uses multiple mechanisms to prevent spam: - **Namespace authentication requirements** β€” Publishers must verify ownership of their namespace through GitHub, DNS, or HTTP challenges, preventing arbitrary spam submissions. - **Character limits and validation** β€” Free-form fields have strict character limits and regex validation to prevent abuse. - **Manual takedown** β€” The registry maintainers can manually remove spam or malicious servers. See the [Moderation Policy](./moderation-policy) for details on what content is removed. Future spam prevention measures under consideration include stricter rate limiting, AI-based spam detection, and community reporting capabilities. --- ### Modelcontextprotocol Io/Authentication --- title: How to Authenticate When Publishing to the Official MCP Registry sidebarTitle: Authentication --- The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). You must authenticate before publishing to the official MCP Registry. The MCP Registry supports different authentication methods. Which authentication method you choose determines the namespace of your server's name. If you choose GitHub-based authentication, your server's name in `server.json` **MUST** be of the form `io.github.username/*` (or `io.github.orgname/*`). For example, `io.github.alice/weather-server`. If you choose domain-based authentication, your server's name in `server.json` **MUST** be of the form `com.example.*/*`, where `com.example` is the reverse-DNS form of your domain name. For example, `io.modelcontextprotocol/everything`. | Authentication | Name Format | Example Name | | -------------- | ----------------------------------------------- | ------------------------------------ | | GitHub-based | `io.github.username/*` or `io.github.orgname/*` | `io.github.alice/weather-server` | | domain-based | `com.example.*/*` | `io.modelcontextprotocol/everything` | ## GitHub Authentication GitHub authentication uses an OAuth flow initiated by the `mcp-publisher` CLI tool. To perform GitHub authentication, navigate to your server project directory and run: ```bash mcp-publisher login github ``` You should see output like: ```text Output Logging in with github... To authenticate, please: 1. Go to: https://github.com/login/device 2. Enter code: ABCD-1234 3. Authorize this application Waiting for authorization... ``` Visit the link, follow the prompts, and enter the authorization code that was printed in the terminal (e.g., `ABCD-1234` in the above output). Once complete, go back to the terminal, and you should see output like: ```text Output Successfully authenticated! βœ“ Successfully logged in ``` ### Personal vs. organization namespaces GitHub authentication always grants your personal namespace, `io.github./*`. To publish under an **organization** namespace (`io.github./*`), you must be an **Owner** of that organization. Ordinary org membership is no longer sufficient: the registry checks your membership role and only grants the org namespace to admins. This prevents anyone who merely belongs to an org from publishing β€” or overwriting β€” servers under the org's name. If you authenticate with a Personal Access Token (for example in CI), the token must let the registry read your organization role. A token that can't will still publish to your personal namespace, but org publishing will be silently unavailable. The exact requirement depends on the token type: - **Classic PAT**: grant the `read:org` scope. - **Fine-grained PAT**: grant the **Organization permissions β†’ Members β†’ Read-only** permission (the fine-grained equivalent of `read:org`). Without it, GitHub returns no organization membership for the token and you'll get your personal namespace only. Note that a fine-grained PAT is bound to a single resource owner, so it can only see the organization it was created for. Either way the token needs **no** repository scopes β€” the registry never reads or writes your code. ## DNS Authentication DNS authentication is a domain-based authentication method that relies on a DNS TXT record. The TXT record must be placed on the **apex** of your domain (e.g. `example.com`), **not** under a selector like `_mcp-auth.example.com` or `_mcp-registry.example.com`. MCP DNS auth follows SPF-style placement (apex), not DKIM-style (selector). If you put the record under a selector, the registry will not see it and authentication will fail with a generic signature error. If you rotate keys, also remember to remove the previous TXT record from the apex β€” a stale record left behind will be tried first and cause verification to fail. To perform DNS authentication using the `mcp-publisher` CLI tool, run the following commands in your server project directory to generate a TXT record based on a public/private key pair: The Ed25519 codepath requires **OpenSSL 3.0 or later**. macOS ships with LibreSSL by default (the system `openssl` binary), which does not implement Ed25519 in `genpkey` and fails with `Algorithm Ed25519 not found`. On macOS, install OpenSSL 3 (`brew install openssl@3`) and invoke it explicitly β€” for example, replace `openssl` with `/opt/homebrew/opt/openssl@3/bin/openssl` (Apple Silicon) or `/usr/local/opt/openssl@3/bin/openssl` (Intel) in the commands below. The ECDSA P-384 codepath works on LibreSSL. ```bash Ed25519 MY_DOMAIN="example.com" # Generate public/private key pair using Ed25519 openssl genpkey -algorithm Ed25519 -out key.pem # Generate TXT record PUBLIC_KEY="$(openssl pkey -in key.pem -pubout -outform DER | tail -c 32 | base64)" echo "${MY_DOMAIN}. IN TXT \"v=MCPv1; k=ed25519; p=${PUBLIC_KEY}\"" ``` ```bash ECDSA P-384 MY_DOMAIN="example.com" # Generate public/private key pair using ECDSA P-384 openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:secp384r1 -out key.pem # Generate TXT record PUBLIC_KEY="$(openssl ec -in key.pem -text -noout -conv_form compressed | grep -A4 "pub:" | tail -n +2 | tr -d ' :\n' | xxd -r -p | base64)" echo "${MY_DOMAIN}. IN TXT \"v=MCPv1; k=ecdsap384; p=${PUBLIC_KEY}\"" ``` ```bash Google KMS MY_DOMAIN="example.com" MY_PROJECT="myproject" MY_KEYRING="mykeyring" MY_KEY_NAME="mykey" # Log in using gcloud CLI (https://cloud.google.com/sdk/docs/install) gcloud auth login # Set default project gcloud config set project "${MY_PROJECT}" # Create a keyring in your project gcloud kms keyrings create "${MY_KEYRING}" --location global # Create an Ed25519 signing key gcloud kms keys create "${MY_KEY_NAME}" --default-algorithm=ec-sign-ed25519 --purpose=asymmetric-signing --keyring="${MY_KEYRING}" --location=global # Enable Application Default Credentials (ADC) so the publisher tool can sign gcloud auth application-default login # Attempt login to show the public key mcp-publisher login dns google-kms --domain="${MY_DOMAIN}" --resource="projects/${MY_PROJECT}/locations/global/keyRings/${MY_KEYRING}/cryptoKeys/${MY_KEY_NAME}/cryptoKeyVersions/1" # Copy the "Expected proof record": # ${MY_DOMAIN}. IN TXT "v=MCPv1; k=ed25519; p=${PUBLIC_KEY}" ``` ```bash Azure Key Vault MY_DOMAIN="example.com" MY_SUBSCRIPTION="subscription name or ID" MY_RESOURCE_GROUP="MyResourceGroup" MY_KEY_VAULT="MyKeyVault" MY_KEY_NAME="MyKey" # Log in using Azure CLI (https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) az login # Set default subscription az account set --subscription "${MY_SUBSCRIPTION}" # Create a resource group az group create --location westus --resource-group "${MY_RESOURCE_GROUP}" # Create a key vault az keyvault create --name "${MY_KEY_VAULT}" --location westus --resource-group "${MY_RESOURCE_GROUP}" # Create an ECDSA P-384 signing key az keyvault key create --name "${MY_KEY_NAME}" --vault-name "${MY_KEY_VAULT}" --curve P-384 # Attempt login to show the public key mcp-publisher login dns azure-key-vault --domain="${MY_DOMAIN}" --vault "${MY_KEY_VAULT}" --key "${MY_KEY_NAME}" # Copy the "Expected proof record": # ${MY_DOMAIN}. IN TXT "v=MCPv1; k=ecdsap384; p=${PUBLIC_KEY}" ``` Then add the TXT record using your DNS provider's control panel. It may take several minutes for the TXT record to propagate. After the TXT record has propagated, log in using the `mcp-publisher login` command: ```bash Ed25519 MY_DOMAIN="example.com" PRIVATE_KEY="$(openssl pkey -in key.pem -noout -text | grep -A3 "priv:" | tail -n +2 | tr -d ' :\n')" mcp-publisher login dns --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}" ``` ```bash ECDSA P-384 MY_DOMAIN="example.com" PRIVATE_KEY="$(openssl ec -in key.pem -noout -text | grep -A4 "priv:" | tail -n +2 | tr -d ' :\n')" mcp-publisher login dns --algorithm ecdsap384 --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}" ``` ```bash Google KMS MY_DOMAIN="example.com" MY_PROJECT="myproject" MY_KEYRING="mykeyring" MY_KEY_NAME="mykey" mcp-publisher login dns google-kms --domain="${MY_DOMAIN}" --resource="projects/${MY_PROJECT}/locations/global/keyRings/${MY_KEYRING}/cryptoKeys/${MY_KEY_NAME}/cryptoKeyVersions/1" ``` ```bash Azure Key Vault MY_DOMAIN="example.com" MY_KEY_VAULT="MyKeyVault" MY_KEY_NAME="MyKey" mcp-publisher login dns azure-key-vault --domain="${MY_DOMAIN}" --vault "${MY_KEY_VAULT}" --key "${MY_KEY_NAME}" ``` ## HTTP Authentication HTTP authentication is a domain-based authentication method that relies on a `/.well-known/mcp-registry-auth` file hosted on your domain. For example, `https://example.com/.well-known/mcp-registry-auth`. To perform HTTP authentication using the `mcp-publisher` CLI tool, run the following commands in your server project directory to generate an `mcp-registry-auth` file based on a public/private key pair: As with DNS authentication, the Ed25519 codepath requires **OpenSSL 3.0 or later**. macOS's system LibreSSL does not support Ed25519 in `genpkey`. See the note in the [DNS Authentication](#dns-authentication) section for the macOS workaround. ```bash Ed25519 # Generate public/private key pair using Ed25519 openssl genpkey -algorithm Ed25519 -out key.pem # Generate mcp-registry-auth file PUBLIC_KEY="$(openssl pkey -in key.pem -pubout -outform DER | tail -c 32 | base64)" echo "v=MCPv1; k=ed25519; p=${PUBLIC_KEY}" > mcp-registry-auth ``` ```bash ECDSA P-384 # Generate public/private key pair using ECDSA P-384 openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:secp384r1 -out key.pem # Generate mcp-registry-auth file PUBLIC_KEY="$(openssl ec -in key.pem -text -noout -conv_form compressed | grep -A4 "pub:" | tail -n +2 | tr -d ' :\n' | xxd -r -p | base64)" echo "v=MCPv1; k=ecdsap384; p=${PUBLIC_KEY}" > mcp-registry-auth ``` ```bash Google KMS MY_DOMAIN="example.com" MY_PROJECT="myproject" MY_KEYRING="mykeyring" MY_KEY_NAME="mykey" # Log in using gcloud CLI (https://cloud.google.com/sdk/docs/install) gcloud auth login # Set default project gcloud config set project "${MY_PROJECT}" # Create a keyring in your project gcloud kms keyrings create "${MY_KEYRING}" --location global # Create an Ed25519 signing key gcloud kms keys create "${MY_KEY_NAME}" --default-algorithm=ec-sign-ed25519 --purpose=asymmetric-signing --keyring="${MY_KEYRING}" --location=global # Enable Application Default Credentials (ADC) so the publisher tool can sign gcloud auth application-default login # Attempt login to show the public key mcp-publisher login http google-kms --domain="${MY_DOMAIN}" --resource="projects/${MY_PROJECT}/locations/global/keyRings/${MY_KEYRING}/cryptoKeys/${MY_KEY_NAME}/cryptoKeyVersions/1" # Copy the "Expected proof record" to `./mcp-registry-auth`: # v=MCPv1; k=ed25519; p=${PUBLIC_KEY} ``` ```bash Azure Key Vault MY_DOMAIN="example.com" MY_SUBSCRIPTION="subscription name or ID" MY_RESOURCE_GROUP="MyResourceGroup" MY_KEY_VAULT="MyKeyVault" MY_KEY_NAME="MyKey" # Log in using Azure CLI (https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) az login # Set default subscription az account set --subscription "${MY_SUBSCRIPTION}" # Create a resource group az group create --location westus --resource-group "${MY_RESOURCE_GROUP}" # Create a key vault az keyvault create --name "${MY_KEY_VAULT}" --location westus --resource-group "${MY_RESOURCE_GROUP}" # Create an ECDSA P-384 signing key az keyvault key create --name "${MY_KEY_NAME}" --vault-name "${MY_KEY_VAULT}" --curve P-384 # Attempt login to show the public key mcp-publisher login http azure-key-vault --domain="${MY_DOMAIN}" --vault "${MY_KEY_VAULT}" --key "${MY_KEY_NAME}" # Copy the "Expected proof record" to `./mcp-registry-auth`: # v=MCPv1; k=ecdsap384; p=${PUBLIC_KEY} ``` Then host the `mcp-registry-auth` file at `/.well-known/mcp-registry-auth` on your domain. After the file is hosted, log in using the `mcp-publisher login` command: ```bash Ed25519 MY_DOMAIN="example.com" PRIVATE_KEY="$(openssl pkey -in key.pem -noout -text | grep -A3 "priv:" | tail -n +2 | tr -d ' :\n')" mcp-publisher login http --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}" ``` ```bash ECDSA P-384 MY_DOMAIN="example.com" PRIVATE_KEY="$(openssl ec -in key.pem -noout -text | grep -A4 "priv:" | tail -n +2 | tr -d ' :\n')" mcp-publisher login http --algorithm ecdsap384 --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}" ``` ```bash Google KMS MY_DOMAIN="example.com" MY_PROJECT="myproject" MY_KEYRING="mykeyring" MY_KEY_NAME="mykey" mcp-publisher login http google-kms --domain="${MY_DOMAIN}" --resource="projects/${MY_PROJECT}/locations/global/keyRings/${MY_KEYRING}/cryptoKeys/${MY_KEY_NAME}/cryptoKeyVersions/1" ``` ```bash Azure Key Vault MY_DOMAIN="example.com" MY_KEY_VAULT="MyKeyVault" MY_KEY_NAME="MyKey" mcp-publisher login http azure-key-vault --domain="${MY_DOMAIN}" --vault "${MY_KEY_VAULT}" --key "${MY_KEY_NAME}" ``` --- ### Modelcontextprotocol Io/Faq --- title: Frequently Asked Questions sidebarTitle: FAQ --- The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). ## General ### What is the difference between "Official MCP Registry", "MCP Registry", "MCP registry", "MCP Registry API", etc? - "MCP Registry API" β€” An API that implements the [OpenAPI spec](https://github.com/modelcontextprotocol/registry/blob/main/docs/reference/api/openapi.yaml) defined by the MCP Registry. - "Official MCP Registry API" β€” The REST API served at `https://registry.modelcontextprotocol.io`, which is a superset of the MCP Registry API. Its OpenAPI spec can be downloaded from [https://registry.modelcontextprotocol.io/openapi.yaml](https://registry.modelcontextprotocol.io/openapi.yaml). - "MCP registry" β€” A third-party service that provides an MCP Registry API. - "Official MCP Registry" (or "The MCP Registry") β€” The service that lives at `https://registry.modelcontextprotocol.io`. ### Can I delete/unpublish my server? Yes, you can change your server's status to `deleted` using the `mcp-publisher status` command: ```bash # Delete a specific version mcp-publisher status --status deleted --message "No longer maintained" \ io.github.my-username/my-server 1.0.0 # Delete all versions mcp-publisher status --status deleted --all-versions --message "Project archived" \ io.github.my-username/my-server ``` Deleted servers are hidden from default API listings but can still be retrieved with `include_deleted=true`. You can restore a deleted server by setting its status back to `active`. **Note**: Server metadata is never permanently removed from the registry. The `deleted` status hides the server from discovery but preserves the historical record. ### How do I update my server metadata? Submit a new `server.json` with a unique version string. Once published, version metadata is immutable (similar to npm). ### Can I add custom metadata when publishing? Yes, custom metadata under `_meta.io.modelcontextprotocol.registry/publisher-provided` is preserved when publishing to the registry. This allows you to include custom metadata specific to your publishing process. There is a 4KB size limit (4096 bytes of JSON). Publishing will fail if this limit is exceeded. ## Reporting Issues ### What if I need to report a spam or malicious server? 1. Report it as abuse to the underlying package registry (e.g. NPM, PyPi, DockerHub, etc.); and 2. Raise a GitHub issue on the registry repo with a title beginning `Abuse report: ` ### What if I need to report a security vulnerability in the registry itself? Follow [the MCP community SECURITY.md](https://github.com/modelcontextprotocol/.github/blob/main/SECURITY.md). --- ### Modelcontextprotocol Io/Github Actions --- title: How to Automate Publishing with GitHub Actions sidebarTitle: GitHub Actions --- The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). ## Step 1: Create a Workflow File In your server project directory, create a `.github/workflows/publish-mcp.yml` file. Here is an example for npm-based local server, but the MCP Registry publishing steps are the same for all package types: ``` /* Detailed source-code truncated for AI context efficiency. */ ``` ``` /* Detailed source-code truncated for AI context efficiency. */ ``` ``` /* Detailed source-code truncated for AI context efficiency. */ ``` ## Step 2: Add Secrets You may need to add a secret depending on which authentication method you choose: - **GitHub OIDC Authentication**: No dedicated secret necessary. - **GitHub PAT Authentication**: Add a `MCP_GITHUB_TOKEN` secret with a GitHub Personal Access Token. The registry only needs to read your organization role to confirm you are an **Owner** before granting its namespace, so grant the minimum for that and **no** repository scopes (the registry never reads or writes your code): - **Classic PAT**: the `read:org` scope (`read:user` is *not* needed; a bare token still authenticates and publishes to your personal namespace). - **Fine-grained PAT**: the **Organization permissions β†’ Members β†’ Read-only** permission (the equivalent of `read:org`). Without it you'll get your personal namespace only. A fine-grained PAT is scoped to one resource owner, so create it for the organization you're publishing to. For an **organization** token, store it as an **Environment secret** on the `mcp-registry-publish` environment rather than a plain repository secret β€” see "Securing your registry token in CI" below for why this matters and how to configure the environment. - **DNS Authentication**: Add a `MCP_PRIVATE_KEY` secret with your Ed25519 private key. You may also need to add secrets for your package registry. For example, the workflow above needs an `NPM_TOKEN` secret with your npm token. For information about how to add secrets to a repository, see [Using secrets in GitHub Actions](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets). ### Securing your registry token in CI Whichever token you use to authenticate, treat it as a high-value credential. When you authenticate to an **organization** namespace, the resulting registry token can publish β€” and overwrite β€” **any** server under `io.github./*`, not just the one in this repository. The relevant question for your threat model is therefore: *who can cause code to run in a job where this secret is exposed?* By default that is **every repository writer** (via branch or tag pushes), not only org Owners β€” so a plain repo secret quietly widens publish access to everyone with write access. GitHub gives you the tools to close that gap. We recommend, in increasing order of strength: 1. **Store the token as an [Environment secret](https://docs.github.com/en/actions/how-tos/deploy/configure-and-manage-deployments/manage-environments), not a repository or organization secret.** A job can only read an environment secret when it declares `environment:` (as the PAT example above does). Note that the `environment:` key alone protects nothing: if the environment does not exist yet, GitHub **auto-creates it with no protection rules** on first run, and a job that declares `environment:` still also receives ordinary repository and organization secrets. The protection comes from storing the token *as a secret on that environment* and configuring the environment (steps 2–3 below) β€” not from the `environment:` key by itself. 2. **Restrict that environment to your default branch and/or release tags** with a [deployment branch rule](https://docs.github.com/en/actions/how-tos/deploy/configure-and-manage-deployments/manage-environments#deployment-branches-and-tags), and protect that branch with [required pull request reviews](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches). Now only reviewed, maintainer-approved code can ever reach the token. 3. **(Strongest) Add a [required reviewer](https://docs.github.com/en/actions/how-tos/deploy/configure-and-manage-deployments/manage-environments#required-reviewers) to the environment** so each publish pauses for an explicit human approval. Two pitfalls to avoid regardless of the above: - **Never publish from a `pull_request_target` workflow that checks out PR-head code.** That trigger runs with your secrets in the base-repo context, so an untrusted fork PR could exfiltrate the token. Environment branch rules do **not** protect you here (the ref is still your base branch) β€” only required reviewers do. - **Avoid self-hosted runners for the publish job on public repositories**, where fork PRs may be able to schedule jobs onto them. By default, pull requests **from forks** receive no secrets and cannot push branches, so external contributors cannot reach the token without one of the misconfigurations above. ## Step 3: Tag and Release Create and push a version tag to trigger the workflow: ```bash git tag v1.0.0 git push origin v1.0.0 ``` The workflow will run tests, build the package, publish the package to npm, and publish the server to the MCP Registry. ## Troubleshooting {/* prettier-ignore-start */} | Error Message | Action | | --- | --- | | "Authentication failed" | Ensure `id-token: write` permission is set for OIDC, or check secrets. | | "invalid audience" | Your `mcp-publisher` binary is too old for this registry deployment. Re-run the install step shown above so you pick up the latest release. | | "Package validation failed" | Verify your package successfully published to the package registry (e.g., npm, PyPI), and that your package has the [necessary verification information](./package-types). | {/* prettier-ignore-end */} --- ### Modelcontextprotocol Io/Moderation Policy --- title: The MCP Registry Moderation Policy sidebarTitle: Moderation Policy --- The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). **TL;DR**: The MCP Registry is quite permissive! We only remove illegal content, malware, spam, and completely broken servers. ## Scope This policy applies to the official MCP Registry at `registry.modelcontextprotocol.io`. Subregistries may have their own moderation policies. If you have questions about content on a specific subregistry, please contact them directly. ## Disclaimer The MCP Registry **does not** make guarantees about moderation, and consumers should assume minimal-to-no moderation. The MCP Registry is a community supported project, and we have limited active moderation capabilities. We largely rely on upstream package registries (like NPM, PyPI, and Docker) or downstream subregistries (like the GitHub MCP Registry) to do more in-depth moderation. This means there may be content in the MCP Registry that should be removed under this policy, but which we haven't yet removed. Consumers should treat scraped data accordingly. ## What We Remove We will remove servers that contain: - Illegal content, which includes obscene content, copyright violations, and hacking tools - Malware, regardless of intentions - Spam, especially mass-created servers that disrupt the registry. Examples: - The same server being submitted multiple times under different names - A server that doesn't do anything but provide a fixed response with some marketing copy - A server with a description stuffed with marketing copy and an unrelated implementation - Non-functioning servers ## What We Don't Remove Generally, we believe in keeping the registry open and pushing moderation to subregistries. We therefore **won't** remove: - Low-quality or buggy servers - Servers with security vulnerabilities - Servers that do the same thing as other servers - Servers that provide or contain adult content ## How Removal Works When we remove a server, we set the server's `status` to `"deleted"`, but the server's metadata remains accessible via the MCP Registry API. Aggregators may then remove the server from their indexes. In extreme cases, we may overwrite or erase the server's metadata. For example, if the metadata itself is unlawful. ## Appeals Think we made a mistake? Open an issue on our [GitHub repository](https://github.com/modelcontextprotocol/registry) with: - The name of the server - Why you believe the server doesn't meet the above criteria for removal ## Changes to This Policy We're still learning how best to run the MCP Registry! As such, we might end up changing this policy in the future. --- ### Modelcontextprotocol Io/Package Types --- title: MCP Registry Supported Package Types sidebarTitle: Package Types --- The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). # Package Types The MCP Registry supports several different package types, and each package type has its own verification method. ## npm Packages For npm packages, the MCP Registry currently supports the npm public registry (`https://registry.npmjs.org`) only. npm packages use `"registryType": "npm"` in `server.json`. For example: ```json server.json highlight={9} { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "io.github.username/email-integration-mcp", "title": "Email Integration", "description": "Send emails and manage email accounts", "version": "1.0.0", "packages": [ { "registryType": "npm", "identifier": "@username/email-integration-mcp", "version": "1.0.0", "transport": { "type": "stdio" } } ] } ``` ### Ownership Verification The MCP Registry verifies ownership of npm packages by checking `mcpName` in `package.json`. The `mcpName` property **MUST** match the server name from `server.json`. For example: ```json package.json { "name": "@username/email-integration-mcp", "version": "1.0.0", "mcpName": "io.github.username/email-integration-mcp" } ``` ## PyPI Packages For PyPI packages, the MCP Registry currently supports the official PyPI registry (`https://pypi.org`) only. PyPI packages use `"registryType": "pypi"` in `server.json`. For example: ```json server.json highlight={9} { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "io.github.username/database-query-mcp", "title": "Database Query", "description": "Execute SQL queries and manage database connections", "version": "1.0.0", "packages": [ { "registryType": "pypi", "identifier": "database-query-mcp", "version": "1.0.0", "transport": { "type": "stdio" } } ] } ``` ### Ownership Verification The MCP Registry verifies ownership of PyPI packages by checking for the existence of an `mcp-name: $SERVER_NAME` string in the package README (which becomes the package description on PyPI). The string may be hidden in a comment, but the `$SERVER_NAME` portion **MUST** match the server name from `server.json`. For example: ```markdown README.md highlight={5} # Database Query MCP Server This MCP server executes SQL queries and manages database connections. ``` The `mcp-name:` token must be followed by a boundary β€” a newline, whitespace, an HTML tag, or the comment close `-->`. Keep it on its own line or inside ``; do not glue it directly to trailing characters such as a sentence-ending period (`…/database-query-mcp.`), which prevents the match. ## NuGet Packages For NuGet packages, the MCP Registry currently supports the official NuGet registry (`https://api.nuget.org/v3/index.json`) only. NuGet packages use `"registryType": "nuget"` in `server.json`. For example: ```json server.json highlight={9} { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "io.github.username/azure-devops-mcp", "title": "Azure DevOps", "description": "Manage Azure DevOps work items and pipelines", "version": "1.0.0", "packages": [ { "registryType": "nuget", "identifier": "Username.AzureDevOpsMcp", "version": "1.0.0", "transport": { "type": "stdio" } } ] } ``` ### Ownership Verification The MCP Registry verifies ownership of NuGet packages by checking for the existence of an `mcp-name: $SERVER_NAME` string in the package README. The string may be hidden in a comment, but the `$SERVER_NAME` portion **MUST** match the server name from `server.json`. For example: ```markdown README.md highlight={5} # Azure DevOps MCP Server This MCP server manages Azure DevOps work items and pipelines. ``` The `mcp-name:` token must be followed by a boundary β€” a newline, whitespace, an HTML tag, or the comment close `-->`. Keep it on its own line or inside ``; do not glue it directly to trailing characters such as a sentence-ending period (`…/azure-devops-mcp.`), which prevents the match. ## Cargo (Rust) Packages For Cargo packages, the MCP Registry currently supports the official crates.io registry (`https://crates.io`) only. Cargo packages use `"registryType": "cargo"` in `server.json`. For example: ```json server.json highlight={9} { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "io.github.username/widget-mcp", "title": "Widget", "description": "Rust-native MCP server", "version": "0.3.0", "packages": [ { "registryType": "cargo", "identifier": "widget-mcp", "version": "0.3.0", "transport": { "type": "stdio" } } ] } ``` ### Runtime Model Cargo's runtime model differs from npm/PyPI/NuGet. `cargo install ` places the compiled binary on PATH at `~/.cargo/bin`, after which MCP clients invoke it directly by name. There is no per-invocation runner equivalent to `npx` (npm), `uvx` (PyPI), or `dnx` (NuGet, .NET 10 SDK Preview 6+) β€” install is one-time, execution is by binary name. The Cargo example above intentionally omits `runtimeHint` for this reason. Rust MCP authors have two first-class distribution paths: - **Cargo (`registryType: cargo`)** β€” source-distributed via crates.io. End users need the Rust toolchain (`rustup`) to run `cargo install`. Idiomatic for the Rust ecosystem and consistent with how Rust CLIs are typically published. - **MCPB (`registryType: mcpb`)** β€” prebuilt binary distributed via GitHub or GitLab Releases. End users need no toolchain. Right choice if the priority is "no Rust toolchain required." Both paths are supported; the choice is the author's. Cargo native support exists so Rust authors who prefer source distribution are not forced into the MCPB binary-packaging workaround. ### Ownership Verification The MCP Registry verifies ownership of Cargo packages by checking for the existence of an `mcp-name: $SERVER_NAME` string in the package README (which is rendered to HTML and served by crates.io's static CDN). The `$SERVER_NAME` portion **MUST** match the server name from `server.json`. For example: ```markdown README.md highlight={5} # Widget MCP Server A Rust-native MCP server for widget operations. - MCP Registry name: `mcp-name: io.github.username/widget-mcp` ``` **Cargo-specific gotcha:** Unlike PyPI and NuGet (which preserve HTML comments in their README rendering), **crates.io strips HTML comments during markdownβ†’HTML conversion**. The `` hidden-comment form that works for PyPI/NuGet **does not work for cargo** β€” the token will not appear in the rendered HTML the validator inspects. Cargo authors must include the `mcp-name:` token as visible markdown text. A simple bullet in the Links section is the recommended pattern. ## Docker/OCI Images For Docker/OCI images, the MCP Registry currently supports: - Docker Hub (`docker.io`) - GitHub Container Registry (`ghcr.io`) - Quay.io (`quay.io`) - Google Artifact Registry (any `*.pkg.dev` domain) - Azure Container Registry (`*.azurecr.io`) - Microsoft Container Registry (`mcr.microsoft.com`) Docker/OCI images use `"registryType": "oci"` in `server.json`. For example: ```json server.json highlight={9} { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "io.github.username/kubernetes-manager-mcp", "title": "Kubernetes Manager", "description": "Deploy and manage Kubernetes resources", "version": "1.0.0", "packages": [ { "registryType": "oci", "identifier": "docker.io/yourusername/kubernetes-manager-mcp:1.0.0", "transport": { "type": "stdio" } } ] } ``` The format of `identifier` is `registry/namespace/repository:tag`. For example, `docker.io/user/app:1.0.0`, `ghcr.io/user/app:1.0.0`, or `quay.io/myorg/my-mcp-server:1.0.0`. The tag can also be specified as a digest. ### Ownership Verification The MCP Registry verifies ownership of Docker/OCI images by checking for an `io.modelcontextprotocol.server.name` annotation. The value of the `io.modelcontextprotocol.server.name` annotation **MUST** match the server name from `server.json`. For example: ```dockerfile Dockerfile LABEL io.modelcontextprotocol.server.name="io.github.username/kubernetes-manager-mcp" ``` ## MCPB Packages For MCPB packages, the MCP Registry currently supports MCPB artifacts hosted via GitHub or GitLab releases. MCPB packages use `"registryType": "mcpb"` in `server.json`. For example: ```json server.json highlight={9} { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "io.github.username/image-processor-mcp", "title": "Image Processor", "description": "Process and transform images with various filters", "version": "1.0.0", "packages": [ { "registryType": "mcpb", "identifier": "https://github.com/username/image-processor-mcp/releases/download/v1.0.0/image-processor.mcpb", "fileSha256": "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce", "transport": { "type": "stdio" } } ] } ``` ### Verification The MCPB package URL (`identifier` in `server.json`) **MUST** contain the string "mcp". That can be as part of the `.mcpb` file extension or in the name of the repository. The package metadata in `server.json` **MUST** include a `fileSha256` property with a SHA-256 hash of the MCPB artifact, which can be computed using the `openssl` command: ```bash openssl dgst -sha256 image-processor.mcpb ``` The MCP Registry does not validate this hash; however, MCP clients **do** validate the hash before installation to ensure file integrity. Downstream registries may also implement their own validation. --- ### Modelcontextprotocol Io/Quickstart --- title: "Quickstart: Publish an MCP Server to the MCP Registry" sidebarTitle: "Quickstart: Publish a Server" --- The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). This tutorial will show you how to publish an MCP server written in TypeScript to the MCP Registry using the official `mcp-publisher` CLI tool. If you are publishing a non-npm package (PyPI, NuGet, OCI, MCPB), the overall flow is identical, but the ownership-verification step in [Step 1](#step-1-add-verification-information-to-the-package) is different per package type. See [Package Types](./package-types) for the verification mechanism that applies to your package, then return here and follow the remaining steps. ## Prerequisites - **Node.js** β€” This tutorial assumes the MCP server is written in TypeScript. - **npm account** β€” The MCP Registry only hosts metadata, not artifacts. Before publishing to the MCP Registry, we will publish the MCP server's package to npm, so you will need an [npm](https://www.npmjs.com) account. - **GitHub account** β€” The MCP Registry supports [multiple authentication methods](./authentication). For simplicity, this tutorial will use GitHub-based authentication, so you will need a [GitHub](https://github.com/) account. If you do not have an MCP server written in TypeScript, you can copy the `weather-server-typescript` server from the [`modelcontextprotocol/quickstart-resources` repository](https://github.com/modelcontextprotocol/quickstart-resources) to follow along with this tutorial: ```bash git clone --depth 1 git@github.com:modelcontextprotocol/quickstart-resources.git cp -r quickstart-resources/weather-server-typescript . rm -rf quickstart-resources cd weather-server-typescript ``` And edit `package.json` to reflect your information: ```diff package.json { - "name": "mcp-quickstart-ts", - "version": "1.0.0", + "name": "@my-username/mcp-weather-server", + "version": "1.0.1", "main": "index.js", ``` ```diff package.json "license": "ISC", - "description": "", + "repository": { + "type": "git", + "url": "https://github.com/my-username/mcp-weather-server.git" + }, + "description": "An MCP server for weather information.", "devDependencies": { ``` ## Step 1: Add verification information to the package The MCP Registry verifies that a server's underlying package matches its metadata. For npm packages, this requires adding an `mcpName` property to `package.json`: ```diff package.json { "name": "@my-username/mcp-weather-server", "version": "1.0.1", + "mcpName": "io.github.my-username/weather", "main": "index.js", ``` The value of `mcpName` will be your server's name in the MCP Registry. Because we will be using GitHub-based authentication, `mcpName` **must** start with `io.github.my-username/`. ## Step 2: Publish the package The MCP Registry only hosts metadata, not artifacts, so we must publish the package to npm before publishing the server to the MCP Registry. Ensure the distribution files are built: ```bash # Navigate to project directory cd weather-server-typescript # Install dependencies npm install # Build the distribution files npm run build ``` Then follow npm's [publishing guide](https://docs.npmjs.com/creating-and-publishing-scoped-public-packages). In particular, you will probably need to run the following commands: ```bash # If necessary, authenticate to npm npm adduser # Publish the package npm publish --access public ``` You can verify your package is published by visiting its npm URL, such as https://www.npmjs.com/package/@my-username/mcp-weather-server. ## Step 3: Install `mcp-publisher` Install the `mcp-publisher` CLI tool using a pre-built binary or [Homebrew](https://brew.sh): ```bash macOS/Linux 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/ ``` ```powershell Windows $arch = if ([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture -eq "Arm64") { "arm64" } else { "amd64" }; Invoke-WebRequest -Uri "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_windows_$arch.tar.gz" -OutFile "mcp-publisher.tar.gz"; tar xf mcp-publisher.tar.gz mcp-publisher.exe; rm mcp-publisher.tar.gz # Move mcp-publisher.exe to a directory in your PATH ``` ```bash brew install mcp-publisher ``` Verify that `mcp-publisher` is correctly installed by running: ```bash mcp-publisher --help ``` You should see output like: ```text Output MCP Registry Publisher Tool Usage: mcp-publisher [arguments] Commands: init Create a server.json file template login Authenticate with the registry logout Clear saved authentication publish Publish server.json to the registry status Update the status of a server version validate Validate server.json without publishing Use 'mcp-publisher --help' for more information about a command. ``` ## Step 4: Create `server.json` The `mcp-publisher init` command can generate a `server.json` template file with some information derived from your project. In your server project directory, run `mcp-publisher init`: ```bash mcp-publisher init ``` Open the generated `server.json` file, and you should see contents like: ```json server.json { "$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.", "repository": { "url": "https://github.com/my-username/mcp-weather-server", "source": "github" }, "version": "1.0.0", "packages": [ { "registryType": "npm", "identifier": "@my-username/mcp-weather-server", "version": "1.0.0", "transport": { "type": "stdio" }, "environmentVariables": [ { "description": "Your API key for the service", "isRequired": true, "format": "string", "isSecret": true, "name": "YOUR_API_KEY" } ] } ] } ``` Edit the contents as necessary: ```diff server.json { "$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.", "repository": { "url": "https://github.com/my-username/mcp-weather-server", "source": "github" }, - "version": "1.0.0", + "version": "1.0.1", "packages": [ { "registryType": "npm", "identifier": "@my-username/mcp-weather-server", - "version": "1.0.0", + "version": "1.0.1", "transport": { "type": "stdio" - }, - "environmentVariables": [ - { - "description": "Your API key for the service", - "isRequired": true, - "format": "string", - "isSecret": true, - "name": "YOUR_API_KEY" - } - ] + } } ] } ``` The `name` property in `server.json` **must** match the `mcpName` property in `package.json`. ## Step 5: Authenticate with the MCP Registry For this tutorial, we will authenticate with the MCP Registry using GitHub-based authentication. Run the `mcp-publisher login` command to initiate authentication: ```bash mcp-publisher login github ``` You should see output like: ```text Output Logging in with github... To authenticate, please: 1. Go to: https://github.com/login/device 2. Enter code: ABCD-1234 3. Authorize this application Waiting for authorization... ``` Visit the link, follow the prompts, and enter the authorization code that was printed in the terminal (e.g., `ABCD-1234` in the above output). Once complete, go back to the terminal, and you should see output like: ```text Output Successfully authenticated! βœ“ Successfully logged in ``` ## Step 6: Publish to the MCP Registry Finally, publish your server to the MCP Registry using the `mcp-publisher publish` command: ```bash mcp-publisher publish ``` You should see output like: ```text Output Publishing to https://registry.modelcontextprotocol.io... βœ“ Successfully published βœ“ Server io.github.my-username/weather version 1.0.1 ``` You can verify that your server is published by searching for it using the MCP Registry API: ```bash curl "https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.my-username/weather" ``` You should see your server's metadata in the search results JSON: ```text Output {"servers":[{ ... "name":"io.github.my-username/weather" ... }]} ``` ## Troubleshooting {/* prettier-ignore-start */} | Error Message | Action | | --- | --- | |"Registry validation failed for package"|Ensure your package includes the required ownership-verification marker for its package type. For npm this is `mcpName` in `package.json`; for PyPI and NuGet it is an `mcp-name: ` line (or HTML comment) in the package README; for other types see [Package Types](./package-types).| |"Invalid or expired Registry JWT token"|Re-authenticate by running `mcp-publisher login github`.| |"You do not have permission to publish this server"|Your authentication method doesn't match your server's namespace format. With GitHub auth, your server name must start with `io.github.your-username/`.| {/* prettier-ignore-end */} ## Next Steps - Learn about [support for other package types](./package-types). - Learn about [support for remote servers](./remote-servers). - Learn how to [use other authentication methods](./authentication), such as [DNS authentication](./authentication#dns-authentication) which enables custom domains for server name prefixes. - Learn how to [automate publishing with GitHub Actions](./github-actions). --- ### Modelcontextprotocol Io/Registry Aggregators --- title: MCP Registry Aggregators sidebarTitle: Registry Aggregators --- The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). Aggregators are downstream consumers of the MCP Registry that provide additional value. For example, a server marketplace that provides user ratings and security scanning. The MCP Registry provides an unauthenticated read-only REST API that aggregators can use to populate their data stores. Aggregators are expected to scrape data on a regular but infrequent basis (e.g., once per hour), and persist the data in their own data store. The MCP Registry **does not provide uptime or data durability guarantees**. ## Consuming the MCP Registry REST API The base URL for the MCP Registry REST API is `https://registry.modelcontextprotocol.io`. It supports the following endpoints: - [`GET /v0.1/servers`](https://registry.modelcontextprotocol.io/docs#/operations/list-servers-v0.1) β€” List all servers. - [`GET /v0.1/servers/{serverName}/versions`](https://registry.modelcontextprotocol.io/docs#/operations/get-server-versions-v0.1) β€” List all versions of a server. - [`GET /v0.1/servers/{serverName}/versions/{version}`](https://registry.modelcontextprotocol.io/docs#/operations/get-server-version-v0.1) β€” Get a specific version of a server. Use the special version `latest` to get the latest version of the server. URL path parameters such as `serverName` and `version` **must** be URL-encoded. For example, `io.modelcontextprotocol/everything` must be encoded as `io.modelcontextprotocol%2Feverything`. Aggregators will most likely scrape the `GET /v0.1/servers` endpoint. ### Pagination The `GET /v0.1/servers` endpoint supports cursor-based pagination. For example, the first page can be fetched using a `limit` query parameter: ```bash curl "https://registry.modelcontextprotocol.io/v0.1/servers?limit=100" ``` ```jsonc Output highlight={5} { "servers": [ /* ... */ ], "metadata": { "count": 100, "nextCursor": "com.example/my-server:1.0.0", }, } ``` Then subsequent pages can be fetched by passing the `nextCursor` value as the `cursor` query parameter: ```bash curl "https://registry.modelcontextprotocol.io/v0.1/servers?limit=100&cursor=com.example/my-server:1.0.0" ``` ### Filtering Since The `GET /v0.1/servers` endpoint supports filtering servers that have been updated since a given timestamp. For example, servers that have been updated since 2025-10-23 can be fetched using an `updated_since` query parameter in [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339) date-time format: ```bash curl "https://registry.modelcontextprotocol.io/v0.1/servers?updated_since=2025-10-23T00:00:00.000Z" ``` ## Server Status Server metadata is generally immutable, except for the `status` field which may be updated to, e.g., `"deprecated"` or `"deleted"`. We recommend that aggregators keep their copy of each server's `status` up to date. The `"deleted"` status typically indicates that a server has violated our permissive [moderation policy](./moderation-policy), suggesting the server might be spam, malware, or illegal. Aggregators may prefer to remove these servers from their index. ## Acting as a Subregistry A subregistry is an aggregator that also implements the [OpenAPI spec](https://github.com/modelcontextprotocol/registry/blob/main/docs/reference/api/openapi.yaml) defined by the MCP Registry. This allows clients, such as MCP host applications, to consume server metadata via a standardized interface. The subregistry OpenAPI spec allows subregistries to inject custom metadata via the `_meta` field. For example, a subregistry could inject user ratings, download counts, and security scan results: ```json server.json highlight={17-26} { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "io.github.username/email-integration-mcp", "title": "Email Integration", "description": "Send emails and manage email accounts", "version": "1.0.0", "packages": [ { "registryType": "npm", "identifier": "@username/email-integration-mcp", "version": "1.0.0", "transport": { "type": "stdio" } } ], "_meta": { "com.example.subregistry/custom": { "user_rating": 4.5, "download_count": 12345, "security_scan": { "last_scanned": "2025-10-23T12:00:00Z", "vulnerabilities_found": 0 } } } } ``` We recommend that custom metadata be put under a key that reflects the subregistry (e.g., `"com.example.subregistry/custom"` in the above example). --- ### Modelcontextprotocol Io/Remote Servers --- title: Publishing Remote Servers sidebarTitle: Remote Servers --- The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). The MCP Registry supports remote MCP servers via the `remotes` property in `server.json`: ```json server.json highlight={7-12} { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "com.example/acme-analytics", "title": "ACME Analytics", "description": "Real-time business intelligence and reporting platform", "version": "2.0.0", "remotes": [ { "type": "streamable-http", "url": "https://analytics.example.com/mcp" } ] } ``` A remote server **MUST** be publicly accessible at its specified URL. ## Transport Type Remote servers can use the Streamable HTTP transport (recommended) or the SSE transport. Remote servers can also support both transports simultaneously at different URLs. Specify the transport by setting the `type` property of the `remotes` entry to either `"streamable-http"` or `"sse"`: ```json server.json highlight={9,13} { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "com.example/acme-analytics", "title": "ACME Analytics", "description": "Real-time business intelligence and reporting platform", "version": "2.0.0", "remotes": [ { "type": "streamable-http", "url": "https://analytics.example.com/mcp" }, { "type": "sse", "url": "https://analytics.example.com/sse" } ] } ``` ## URL Template Variables Remote servers can define URL template variables using `{curly_braces}` notation. This enables multi-tenant deployments where a single server definition can support multiple endpoints with configurable values: ```json server.json highlight={10-17} { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "com.example/acme-analytics", "title": "ACME Analytics", "description": "Real-time business intelligence and reporting platform", "version": "2.0.0", "remotes": [ { "type": "streamable-http", "url": "https://{tenant_id}.analytics.example.com/mcp", "variables": { "tenant_id": { "description": "Your tenant identifier (e.g., 'us-cell1', 'emea-cell1')", "isRequired": true } } } ] } ``` When configuring this server, users provide their `tenant_id` value, and the URL template gets resolved to the appropriate endpoint (e.g., `https://us-cell1.analytics.example.com/mcp`). Variables support additional properties like `default`, `choices`, and `isSecret`: ```json server.json highlight={12-22} { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "com.example/multi-region-mcp", "title": "Multi-Region MCP", "description": "MCP server with regional endpoints", "version": "1.0.0", "remotes": [ { "type": "streamable-http", "url": "https://api.example.com/{region}/mcp", "variables": { "region": { "description": "Deployment region", "isRequired": true, "choices": [ "us-east-1", "eu-west-1", "ap-southeast-1" ], "default": "us-east-1" } } } ] } ``` ## HTTP Headers MCP clients can be instructed to send specific HTTP headers by adding the `headers` property to the `remotes` entry: ```json server.json highlight={11-18} { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "com.example/acme-analytics", "title": "ACME Analytics", "description": "Real-time business intelligence and reporting platform", "version": "2.0.0", "remotes": [ { "type": "streamable-http", "url": "https://analytics.example.com/mcp", "headers": [ { "name": "X-API-Key", "description": "API key for authentication", "isRequired": true, "isSecret": true } ] } ] } ``` ## Supporting Remote and Non-remote Installation The `remotes` property can coexist with the `packages` property in `server.json` in order to allow MCP host applications to choose the preferred method of installation. ```json server.json highlight={7-22} { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "io.github.username/email-integration-mcp", "title": "Email Integration", "description": "Send emails and manage email accounts", "version": "1.0.0", "remotes": [ { "type": "streamable-http", "url": "https://email.example.com/mcp" } ], "packages": [ { "registryType": "npm", "identifier": "@example/email-integration-mcp", "version": "1.0.0", "transport": { "type": "stdio" } } ] } ``` --- ### Modelcontextprotocol Io/Terms Of Service --- title: Official MCP Registry Terms of Service sidebarTitle: Terms of Service --- The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). **Effective date: 2025-09-02** ## Overview These terms (β€œTerms”) govern your access to and use of the official MCP Registry (the service hosted at [https://registry.modelcontextprotocol.io/](https://registry.modelcontextprotocol.io/) or a successor location) (β€œRegistry”), including submissions or publications of MCP servers, references to MCP servers or to data about such servers and/or their developers (β€œRegistry Data”), and related conduct. The Registry is intended to be a centralized repository of MCP servers developed by community members to facilitate easy access by AI applications. These terms are governed by the laws of the State of California. ## For All Users 1. No Warranties. The Registry is provided β€œas is” with no warranties of any kind. That means we don't guarantee the accuracy, completeness, safety, durability, or availability of the Registry, servers included in the registry, or Registry Data. In short, we’re also not responsible for any MCP servers or Registry Data, and we highly recommend that you evaluate each MCP server and its suitability for your intended use case(s) before deciding whether to use it. 2. Access and Use Requirements. To access or use the Registry, you must: 1. Be at least 18 years old. 2. Use the Registry, MCP servers in the Registry, and Registry Data only in ways that are legal under the applicable laws of the United States or other countries including the country in which you are a resident or from which you access and use the Registry, and not be barred from accessing or using the Registry under such laws. You will comply with all applicable law, regulation, and third party rights (including, without limitation, laws regarding the import or export of data or software, privacy, intellectual property, and local laws). You will not use the Registry, MCP servers, or Registry Data to encourage or promote illegal activity or the violation of third party rights or terms of service. 3. Log in via method(s) approved by the Registry maintainers, which may involve using applications or other software owned by third parties. 3. Entity Use. If you are accessing or using the Registry on behalf of an entity, you represent and warrant that you have authority to bind that entity to these Terms. By accepting these Terms, you are doing so on behalf of that entity (and all references to β€œyou” in these Terms refer to that entity). 4. Account Information. In order to access or use the Registry, you may be required to provide certain information (such as identification or contact details) as part of a registration process or in connection with your access or use of the Registry or MCP servers therein. Any information you give must be accurate and up-to-date, and you agree to inform us promptly of any updates. You understand that your use of the Registry may be monitored to ensure quality and verify your compliance with these Terms. 5. Feedback. You are under no obligation to provide feedback or suggestions. If you provide feedback or suggestions about the Registry or the Model Context Protocol, then we (and those we allow) may use such information without obligation to you. 6. Branding. Only use the term β€œOfficial MCP Registry” where it is clear it refers to the Registry, and does not imply affiliation, endorsement, or sponsorship. For example, you can permissibly say β€œAcme Inc. keeps its data up to date by automatically pulling data from the Official MCP Registry” or β€œThis data comes from the Official MCP Registry,” but cannot say β€œThis is the website for the Official MCP Registry,” β€œWe’re the premier destination to view Official MCP Registry data,” or β€œWe’ve partnered with the Official MCP Registry to provide this data.” 7. Modification. We may modify the Terms or any portion to, for example, reflect changes to the law or changes to the Model Context Protocol. We’ll post notice of modifications to the Terms to this website or a successor location. If you do not agree to the modified Terms, you should discontinue your access to and/or use of the Registry. Your continued access to and/or use of the Registry constitutes your acceptance of any modified Terms. 8. Additional Terms. Depending on your intended use case(s), you must also abide by applicable terms below. ## For MCP Developers 9. Prohibitions. By accessing and using the Registry, including by submitting MCP servers and/or Registry Data, you agree not to: 1. Share malicious or harmful content, such as malware, even in good faith or for research purposes, or perform any action with the intent of introducing any viruses, worms, defects, Trojan horses, malware, or any items of a destructive nature; 2. Defame, abuse, harass, stalk, or threaten others; 3. Interfere with or disrupt the Registry or any associated servers or networks; 4. Submit data with the intent of confusing or misleading others, including but not limited to via spam, posting off-topic marketing content, posting MCP servers in a way that falsely implies affiliation with or endorsement by a third party, or repeatedly posting the same or similar MCP servers under different names; 5. Promote or facilitate unlawful online gambling or disruptive commercial messages or advertisements; 6. Use the Registry for any activities where the use or failure of the Registry could lead to death, personal injury, or environmental damage; 7. Use the Registry to process or store any data that is subject to the International Traffic in Arms Regulations maintained by the U.S. Department of State. 10. License. You agree that metadata about MCP servers you submit (e.g., schema name and description, URLs, identifiers) and other Registry Data is intended to be public, and will be dedicated to the public domain under [CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/). By submitting such data, you agree that you have the legal right to make this dedication (i.e., you own the copyright to these submissions or have permission from the copyright owner(s) to do so) and intend to do so. You understand that this dedication is perpetual, irrevocable, and worldwide, and you waive any moral rights you may have in your contributions to the fullest extent permitted by law. This dedication applies only to Registry Data and not to packages in third party registries that you might point to. 11. Privacy and Publicity. You understand that any MCP server metadata you publish may be made public. This includes personal data such as your GitHub username, domain name, or details from your server description. Moreover, you understand that others may process personal information included in your MCP server metadata. For example, subregistries might enrich this data by adding how many stars your GitHub repository has, or perform automated security scanning on your code. By publishing a server, you agree that others may engage in this sort of processing, and you waive rights you might have in some jurisdictions to access, rectify, erase, restrict, or object to such processing. --- ### Modelcontextprotocol Io/Versioning --- title: Versioning Published MCP Servers sidebarTitle: Versioning --- The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues). MCP servers **MUST** define a version string in `server.json`. For example: ```json server.json highlight={6} { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "io.github.username/email-integration-mcp", "title": "Email Integration", "description": "Send emails and manage email accounts", "version": "1.0.0", "packages": [ { "registryType": "npm", "identifier": "@username/email-integration-mcp", "version": "1.0.0", "transport": { "type": "stdio" } } ] } ``` The version string **MUST** be unique for each publication of the server. Once published, the version string (and other metadata) cannot be changed. ## Version Format The MCP Registry recommends [semantic versioning](https://semver.org/), but supports any version string format. When a server is published, the MCP Registry will attempt to parse its version as a semantic version string for sorting purposes, and will mark the version as "latest" if appropriate. If parsing fails, the version will always be marked as "latest". If a server uses semantic version strings but publishes a new version that does _not_ conform to semantic versioning, the new version will be marked as "latest" even if it would otherwise be sorted before the semantic version strings. As an error prevention mechanism, the MCP Registry prohibits version strings that appear to refer to ranges of versions. | Example | Type | Guidance | | -------------- | ------------------- | ------------------------------ | | `1.0.0` | semantic version | **Recommended** | | `2.1.3-alpha` | semantic prerelease | **Recommended** | | `1.0.0-beta.1` | semantic prerelease | **Recommended** | | `3.0.0-rc.2` | semantic prerelease | **Recommended** | | `2025.11.25` | semantic date | Recommended | | `2025.6.18` | semantic date | Recommended **(⚠️Caution!⚠️)** | | `2025.06.18` | non-semantic date | Allowed **(⚠️Caution!⚠️)** | | `2025-06-18` | non-semantic date | Allowed | | `v1.0` | prefixed version | Allowed | | `^1.2.3` | version range | Prohibited | | `~1.2.3` | version range | Prohibited | | `>=1.2.3` | version range | Prohibited | | `<=1.2.3` | version range | Prohibited | | `>1.2.3` | version range | Prohibited | | `<1.2.3` | version range | Prohibited | | `1.x` | version range | Prohibited | | `1.2.*` | version range | Prohibited | | `1 - 2` | version range | Prohibited | | `1.2 \|\| 1.3` | version range | Prohibited | ## Best Practices ### Use Semantic Versioning Use [semantic versioning](https://semver.org/) for version strings. ### Align Server Version with Package Version For local servers, align the server version with the underlying package version in order to prevent confusion: ```json server.json highlight={2,7} { "version": "1.2.3", "packages": [ { "registryType": "npm", "identifier": "@my-username/my-server", "version": "1.2.3", "transport": { "type": "stdio" } } ] } ``` If there are multiple underlying packages, use the server version to indicate the overall release version: ```json server.json highlight={2,7,15} { "version": "1.3.0", "packages": [ { "registryType": "npm", "identifier": "@my-username/my-server", "version": "1.3.0", "transport": { "type": "stdio" } }, { "registryType": "nuget", "identifier": "MyUsername.MyServer", "version": "1.0.0", "transport": { "type": "stdio" } } ] } ``` ### Align Server Version with Remote API Version For remote servers with an API version, the server version should align with the API version: ```json server.json highlight={2,6} { "version": "2.1.0", "remotes": [ { "type": "streamable-http", "url": "https://api.myservice.com/mcp/v2.1" } ] } ``` ### Use Prerelease Versions for Registry-only Updates If you anticipate publishing a server multiple times _without_ changing the underlying package or remote URL β€” for example, to update other parts of the metadata β€” use semantic prerelease versions: ```json server.json highlight={2} { "version": "1.2.3-1", "packages": [ { "registryType": "npm", "identifier": "@my-username/my-server", "version": "1.2.3", "transport": { "type": "stdio" } } ] } ``` According to semantic versioning, prerelease versions such as `1.2.3-1` are sorted before regular semantic versions such as `1.2.3`. Therefore, if you publish a prerelease version _after_ its corresponding regular version, the prerelease version will **not** be marked as "latest". ## Aggregator Recommendations MCP Registry aggregators **SHOULD**: 1. Attempt to interpret versions as semantic versions when possible 2. Use the following version comparison rules: - If one version is marked as "latest", treat it as later - If both versions are valid semantic versions, use semantic versioning comparison rules - If neither versions are valid semantic versions, compare published timestamp - If one version is a valid semantic version and the other is not, treat the semantic version as later --- ### Reference/Cli/Commands # Publisher CLI Commands Reference Complete command reference for the `mcp-publisher` CLI tool. See the [publishing guide](../../modelcontextprotocol-io/quickstart.mdx) for a walkthrough of using the CLI to publish a server. ## Installation Install via Homebrew (macOS/Linux): ```bash $ brew install mcp-publisher ``` ## Global Options All commands support: - `--help`, `-h` - Show command help `--registry` is a flag on `login` only (default: `https://registry.modelcontextprotocol.io`). The other commands read the registry URL from the stored login token, so passing `--registry` to `publish` would be interpreted as the `server.json` path. ## Commands ### `mcp-publisher init` Generate a `server.json` template with automatic detection. **Usage:** ```bash mcp-publisher init ``` **Behavior:** - Creates `server.json` in current directory - Auto-detects package managers (`package.json`, `setup.py`, etc.) - Pre-fills fields where possible - Writes `TODO:` placeholders for fields it cannot detect β€” it is non-interactive and takes no flags **Example output:** ```json { "name": "io.github.username/server-name", "description": "TODO: Add server description", "version": "1.0.0", "packages": [ { "registryType": "npm", "identifier": "detected-package-name", "version": "1.0.0" } ] } ``` ### `mcp-publisher login ` Authenticate with the registry. **Authentication Methods:** #### GitHub Interactive ```bash mcp-publisher login github [--token=PAT] [--registry=URL] ``` - Opens browser for GitHub OAuth flow - Grants access to `io.github.{username}/*` and `io.github.{org}/*` namespaces - `--token` supplies a GitHub Personal Access Token instead of the interactive flow, which is how [publishing from GitHub Actions](../../modelcontextprotocol-io/github-actions.mdx) authenticates without a browser. This flag is accepted by `login github` only. #### GitHub OIDC (CI/CD) ```bash mcp-publisher login github-oidc [--registry=URL] ``` - Uses GitHub Actions OIDC tokens automatically - Requires `id-token: write` permission in workflow - No browser interaction needed The CLI derives the OIDC `aud` claim from `--registry` (scheme + host, e.g. `https://registry.modelcontextprotocol.io`) so tokens are bound to the specific deployment they were minted for. Self-hosters must set `MCP_REGISTRY_GITHUB_OIDC_AUDIENCE` on the registry to the matching value; publishers running an older `mcp-publisher` will fail with `invalid audience` and need to upgrade. Also see [the guide to publishing from GitHub Actions](../../modelcontextprotocol-io/github-actions.mdx). #### DNS Verification ```bash mcp-publisher login dns --domain=example.com --private-key=HEX_KEY [--algorithm=ed25519|ecdsap384] [--registry=URL] ``` - Verifies domain ownership via DNS TXT record - Grants access to `com.example.*` namespaces - Requires Ed25519 private key (64-character hex) or ECDSA P-384 private key (96-character hex) - `--algorithm` defaults to `ed25519`. **For an ECDSA P-384 key you must pass `--algorithm ecdsap384`**, otherwise the key is rejected with `invalid seed length: expected 32 bytes, got 48`. - The private key can be stored in a cloud signing provider like Google KMS or Azure Key Vault. Cloud providers derive the algorithm from the key itself, so `--algorithm` does not apply to them. **Setup:** (for Ed25519, recommended) ```bash # Generate keypair openssl genpkey -algorithm Ed25519 -out key.pem # Get public key for DNS record openssl pkey -in key.pem -pubout -outform DER | tail -c 32 | base64 # Add DNS TXT record: # example.com. IN TXT "v=MCPv1; k=ed25519; p=PUBLIC_KEY" # Extract private key for login openssl pkey -in key.pem -noout -text | grep -A3 "priv:" | tail -n +2 | tr -d ' :\n' ``` **Setup:** (for ECDSA P-384) ```bash # Generate keypair openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:secp384r1 -out key.pem # Get public key for DNS record openssl ec -in key.pem -text -noout -conv_form compressed | grep -A4 "pub:" | tail -n +2 | tr -d ' :\n' | xxd -r -p | base64 # Add DNS TXT record: # example.com. IN TXT "v=MCPv1; k=ecdsap384; p=PUBLIC_KEY" # Extract private key for login openssl ec -in -noout -text | grep -A4 "priv:" | tail -n +2 | tr -d ' :\n' # Log in, selecting the ECDSA P-384 algorithm explicitly mcp-publisher login dns --algorithm ecdsap384 --domain=example.com --private-key=HEX_KEY ``` **Setup:** (for Google KMS signing) This requires the [gcloud CLI](https://cloud.google.com/sdk/docs/install). ```bash # log in and set default project gcloud auth login gcloud config set project myproject # Create a keyring in your project gcloud kms keyrings create mykeyring --location global # Create an Ed25519 signing key gcloud kms keys create mykey --default-algorithm=ec-sign-ed25519 --purpose=asymmetric-signing --keyring=mykeyring --location=global # Enable Application Default Credentials (ADC) so the publisher tool can sign gcloud auth application-default login # Attempt login to show the public key mcp-publisher login dns google-kms --domain=example.com --resource=projects/myproject/locations/global/keyRings/mykeyring/cryptoKeys/mykey/cryptoKeyVersions/1 # Copy the "Expected proof record" and add the TXT record # example.com. IN TXT "v=MCPv1; k=ed25519; p=PUBLIC_KEY" # Re-run the login command mcp-publisher login dns google-kms --domain=example.com --resource=projects/myproject/locations/global/keyRings/mykeyring/cryptoKeys/mykey/cryptoKeyVersions/1 ``` **Setup:** (for Azure Key Vault signing) This requires the [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest). ```bash # log in and set default subscription az login az account set --subscription "My Subscription (name or ID)" # Create a resource group az group create --location westus --resource-group MyResourceGroup # Create a Key Vault az keyvault create --name MyKeyVault --location westus --resource-group MyResourceGroup # Create an ECDSA P-384 signing key az keyvault key create --name MyKey --vault-name MyKeyVault --curve P-384 # Attempt login to show the public key mcp-publisher login dns azure-key-vault --domain=example.com --vault MyKeyVault --key MyKey # Copy the "Expected proof record" and add the TXT record # example.com. IN TXT "v=MCPv1; k=ecdsap384; p=PUBLIC_KEY" # Re-run the login command mcp-publisher login dns azure-key-vault --domain=example.com --vault MyKeyVault --key MyKey ``` #### HTTP Verification ```bash mcp-publisher login http --domain=example.com --private-key=HEX_KEY [--algorithm=ed25519|ecdsap384] [--registry=URL] ``` - Verifies domain ownership via HTTPS endpoint - Grants access to `com.example.*` namespaces - Requires Ed25519 private key (64-character hex) or ECDSA P-384 private key (96-character hex) - `--algorithm` defaults to `ed25519`. **For an ECDSA P-384 key you must pass `--algorithm ecdsap384`**. - The private key can be stored in a cloud signing provider like Google KMS or Azure Key Vault. Cloud providers derive the algorithm from the key itself, so `--algorithm` does not apply to them. **Setup:** (for Ed25519, recommended) ```bash # Generate keypair (same as DNS) openssl genpkey -algorithm Ed25519 -out key.pem # Host public key at: # https://example.com/.well-known/mcp-registry-auth # Content: v=MCPv1; k=ed25519; p=PUBLIC_KEY ``` **Setup:** (for ECDSA P-384) ```bash # Generate keypair (same as DNS) openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:secp384r1 -out key.pem # Host public key at: # https://example.com/.well-known/mcp-registry-auth # Content: v=MCPv1; k=ecdsap384; p=PUBLIC_KEY # Log in, selecting the ECDSA P-384 algorithm explicitly mcp-publisher login http --algorithm ecdsap384 --domain=example.com --private-key=HEX_KEY ``` Cloud signing is also supported for HTTP authentication, similar to the DNS examples above. Just swap out the `dns` positional argument for `http`. #### Anonymous (Testing) ```bash mcp-publisher login none [--registry=URL] ``` - No authentication - for local testing only - Only works with local registry instances ### `mcp-publisher validate` Validate a `server.json` file without publishing. **Usage:** ```bash mcp-publisher validate [file] ``` **Arguments:** - `file` - Path to server.json file (default: `./server.json`) **Behavior:** - Performs exhaustive validation, reporting all issues at once (not just the first error) - Validates JSON syntax and schema compliance - Runs semantic validation (business logic checks) - Checks for deprecated schema versions and provides migration guidance - Includes detailed error locations with JSON paths (e.g., `packages[0].transport.url`) - Shows validation issue type (json, schema, semantic, linter) - Displays severity level (error, warning, info) - Provides schema references showing which validation rule triggered each error **Example output:** ```bash $ mcp-publisher validate βœ… server.json is valid $ mcp-publisher validate custom-server.json ❌ Validation failed with 2 issue(s): 1. [error] repository.url (schema) '' has invalid format 'uri' Reference: #/definitions/Repository/properties/url/format from: [#/definitions/ServerDetail]/properties/repository/[#/definitions/Repository]/properties/url/format 2. [error] name (semantic) server name must be in format 'dns-namespace/name' Reference: invalid-server-name ``` ### `mcp-publisher publish` Publish server to the registry. For detailed guidance on the publishing process, see the [publishing guide](../../modelcontextprotocol-io/quickstart.mdx). **Usage:** ```bash mcp-publisher publish [PATH] ``` **Options:** - `PATH` - Path to server.json (default: `./server.json`) **Process:** 1. Validates `server.json` against schema 2. Publishes the `server.json` to the registry server URL specified in the login token 3. Server: Verifies package ownership (see [Official Registry Requirements](../server-json/official-registry-requirements.md)) 4. Server: Checks namespace authentication 5. Server: Publishes to registry **Example:** ```bash # Basic publish mcp-publisher publish # Custom file location mcp-publisher publish ./config/server.json ``` ### `mcp-publisher status` Update the lifecycle status of a published server. **Usage:** ```bash mcp-publisher status --status [flags] [version] ``` **Flags:** - `--status` (required) - New status: `active`, `deprecated`, or `deleted` - `--message` - Optional message explaining the status change (not allowed when status is `active`) - `--all-versions` - Apply status change to all versions of the server - `--yes`, `-y` - Skip confirmation prompt (only applies when using `--all-versions`) **Arguments:** - `server-name` - Full server name (e.g., `io.github.user/my-server`) - `version` - Server version to update (required unless `--all-versions` is set) **Status Values:** - `active` - Server is active and visible in default listings - `deprecated` - Server is deprecated but still visible with a warning message - `deleted` - Server is hidden from default listings **Examples:** ```bash # Deprecate a specific version mcp-publisher status --status deprecated --message "Please upgrade to 2.0.0" \ io.github.user/my-server 1.0.0 # Delete a version with security issues mcp-publisher status --status deleted --message "Critical security vulnerability" \ io.github.user/my-server 1.0.0 # Restore a version to active mcp-publisher status --status active io.github.user/my-server 1.0.0 # Deprecate all versions at once mcp-publisher status --status deprecated --all-versions --message "Project archived" \ io.github.user/my-server ``` **Requirements:** - Must be logged in with `publish` or `edit` permission for the server namespace ### `mcp-publisher logout` Clear stored authentication credentials. **Usage:** ```bash mcp-publisher logout ``` **Behavior:** - Removes `~/.config/mcp-publisher/token.json` - Also cleans up legacy token files (`~/.mcp_publisher_token`, `.mcpregistry_*`) - Does not revoke tokens on server side ## Configuration ### Token Storage Authentication tokens are stored in `~/.config/mcp-publisher/token.json` as JSON: ```json { "token": "jwt-token-here", "method": "github", "registry": "https://registry.modelcontextprotocol.io" } ``` > **Note:** Tokens were previously stored in `~/.mcp_publisher_token`. If you are upgrading, run `mcp-publisher logout` followed by `mcp-publisher login` to migrate to the new location. --- ### Reference/Api/CHANGELOG # Registry API Changelog Changes to the REST API endpoints and responses. ## Unreleased ### Added #### Server Status Management Endpoints New endpoints for managing server lifecycle status: - `PATCH /v0/servers/{serverName}/versions/{version}/status` - Update status of a specific server version - `PATCH /v0/servers/{serverName}/status` - Update status of all versions of a server in a single transaction **Status values:** - `active` - Server is active and visible in default listings - `deprecated` - Server is deprecated but still visible with a warning message - `deleted` - Server is hidden from default listings **Authentication:** Requires `publish` or `edit` permission for the server namespace. #### New Response Fields New fields added to `_meta["io.modelcontextprotocol.registry/official"]` (RegistryExtensions): - `statusChangedAt` - Timestamp when the server status was last changed - `statusMessage` - Optional message explaining status change (e.g., deprecation reason, migration guidance) #### Server Filtering Enhancements New `include_deleted` query parameter added to multiple endpoints: - `GET /v0/servers` - Include deleted servers in list results (default: `false`, automatically `true` when `updated_since` is provided) - `GET /v0/servers/{serverName}/versions/{version}` - Include deleted servers in detail results (default: `false`) - `GET /v0/servers/{serverName}/versions` - Include deleted servers in version history (default: `false`) ## 2025-10-17 ### Added #### API Versioning - v0.1 Introduction Introduced `/v0.1/` as a stable API version while `/v0/` continues as the development version. **New version paths:** - All `/v0/` endpoints are now also available at `/v0.1/` - Both versions currently share identical behavior - `/v0/` will continue to evolve with additive changes (new optional fields, new endpoints) - `/v0.1/` will remain stable with only additive, backward-compatible changes - Both versions will be maintained until a future v1.0 release **Migration guidance:** - Production applications should consider using `/v0.1/` for stability - Development and testing can continue using `/v0/` for latest features - No immediate action required - `/v0/` remains fully supported ### ⚠️ BREAKING CHANGES #### Endpoint Simplification Removed redundant endpoint to simplify API surface and reduce implementation burden for subregistries. **Removed endpoints:** - `GET /v0/servers/{serverName}` - Use `GET /v0/servers/{serverName}/versions/latest` instead ## 2025-09-29 ### ⚠️ BREAKING CHANGES #### API Endpoint Restructuring API endpoints updated to use server names instead of server IDs for better usability. **Changed endpoints:** - `GET /v0/servers/{server_id}` β†’ `GET /v0/servers/{serverName}` - `GET /v0/servers/{server_id}/versions` β†’ `GET /v0/servers/{serverName}/versions` **New endpoints:** - `GET /v0/servers/{serverName}/versions/{version}` - Get specific server version - `PUT /v0/servers/{serverName}/versions/{version}` - Edit server version (admin only) **Response format changes:** - Introduced `ServerResponse` schema separating server data from registry metadata - Moved `status` field from server data to `_meta.io.modelcontextprotocol.registry/official` - Removed `io.modelcontextprotocol.registry/official` metadata from `ServerDetail` schema ### Changed - OpenAPI spec version: `2025-09-16` β†’ `2025-09-29` ## 2025-09-16 ### ⚠️ BREAKING CHANGES #### Server ID Endpoints ([#396](https://github.com/modelcontextprotocol/registry/issues/396)) API endpoints updated for consistent server identification across versions. **Problem:** Each server version had a unique ID, preventing version history tracking and server renaming. **Solution:** Introduced consistent server identification across versions. **Changed endpoints:** - `GET /v0/servers/{id}` β†’ `GET /v0/servers/{server_id}` **New endpoints:** - `GET /v0/servers/{server_id}/versions` - List all versions of a server - `GET /v0/servers/{server_id}?version=1.0.0` - Get specific version **Changed response metadata:** - `_meta.*.id` β†’ `_meta.*.serverId` - Added: `_meta.*.versionId` #### Migration Examples **Old Structure:** ```json { "_meta": { "io.modelcontextprotocol.registry/official": { "id": "550e8400-e29b-41d4-a716-446655440000", "published_at": "2024-01-01T00:00:00Z", "is_latest": true } } } ``` **New Structure:** ```json { "_meta": { "io.modelcontextprotocol.registry/official": { "serverId": "550e8400-e29b-41d4-a716-446655440000", "versionId": "773f9b2e-1a47-4c8d-b5e6-2f8d9c4a7b3e", "published_at": "2024-01-01T00:00:00Z", "is_latest": true } } } ``` #### Migration Checklist for API Consumers - [ ] Update API endpoint URLs from `/v0/servers/{id}` to `/v0/servers/{server_id}` - [ ] Update code reading registry metadata from `id` to `serverId`/`versionId` - [ ] Add support for new `/v0/servers/{server_id}/versions` endpoint if needed - [ ] Update JSON parsing to expect camelCase field names - [ ] Test with new API responses ### Changed - OpenAPI spec version: `2025-07-09` β†’ `2025-09-16` ## 2025-07-09 Initial release of the Registry API. --- ### Reference/Api/Extensions # Registry Extensions Specification A standardized way for registries to provide experimental or community-driven features without committing them to the core API specification. ## Motivation [The core generic registry API](./generic-registry-api.md) intentionally stays minimal to ensure stability and broad adoption. Extensions provide a path for: - **Experimentation**: Try new features without core API changes - **Community innovation**: Anyone can implement custom extensions - **Gradual adoption**: Popular extensions may inform future core API features - **Avoiding breaking changes**: Failed experiments can be deprecated without API versioning churn ## URL Structure Extensions live under the `/v0.1/x/` prefix: ``` /v0.1/x//[/] ``` **Components:** - ``: Reverse domain ownership (e.g., `com.example`, `io.github.username`) - ``: Extension name (lowercase, hyphens for word separation) - ``: Extension-specific path structure (optional) **Examples:** ``` /v0.1/x/com.example/search?q=database /v0.1/x/com.example/stats /v0.1/x/io.github.username/custom-feature ``` ## Conventions Where possible: - Follow standard REST conventions, return simple JSON responses, and avoid special headers - For list endpoints, use cursor-based pagination matching the core API - Extensions requiring authentication **SHOULD** follow the [Registry Authorization Specification](./registry-authorization.md) - Build open-source implementations in a composable way on top of the core APIs (e.g. as opposed to via custom database integration) ## Implementation Requirements Registries implementing extensions **SHOULD** namespace extensions properly to avoid conflicts. Clients consuming extensions **MUST** gracefully handle missing extensions. ## Example A simple server stats extension: ```bash GET /v0.1/x/com.example/stats ``` ```json { "totalServers": 1234, "totalVersions": 5678, "recentPublishes": 42 } ``` ## Future Considerations - **Extension discovery**: A potential `/v0.1/x` endpoint to list available extensions - **Extension metadata**: Standardized metadata format for extension capabilities - **Defining common extensions**: Like semantic conventions from OpenTelemetry, develop common extensions that registries can adopt (possibly under an experimental namespace) - Search extension for free-text search across server metadata ([#389](https://github.com/modelcontextprotocol/registry/issues/389)) - MCP server extension to expose the registry itself as an MCP server for programmatic access ([#24](https://github.com/modelcontextprotocol/registry/issues/24)) --- ### Reference/Api/Generic Registry Api # Generic Registry API Specification A standardized RESTful HTTP API for MCP registries to provide consistent endpoints for discovering and retrieving MCP servers. Also see: - For authentication and authorization, see the [registry authorization specification](./registry-authorization.md). ## Browse the Complete API Specification **πŸ“‹ View the full API specification interactively**: Open [openapi.yaml](./openapi.yaml) in an OpenAPI viewer like [Stoplight Elements](https://elements-demo.stoplight.io/?spec=https://raw.githubusercontent.com/modelcontextprotocol/registry/refs/heads/main/docs/reference/api/openapi.yaml). The official registry has some more endpoints and restrictions on top of this. See the [official registry API spec](./official-registry-api.md) for details. ## Quick Reference ### Core Endpoints - **`GET /v0.1/servers`** - List all servers with pagination - **`GET /v0.1/servers/{serverName}/versions`** - List all versions of a server - **`GET /v0.1/servers/{serverName}/versions/{version}`** - Get specific version of server. Use the special version `latest` to get the latest version. - **`POST /v0.1/publish`** - Publish new server (optional, registry-specific authentication) - **`PUT /v0.1/servers/{serverName}/versions/{version}`** - Update specific server version (optional; the official registry implements this as an admin endpoint) - **`DELETE /v0.1/servers/{serverName}/versions/{version}`** - Delete specific server version (optional, not implemented by official registry) - **`PATCH /v0.1/servers/{serverName}/versions/{version}/status`** - Update server version status (optional) - **`PATCH /v0.1/servers/{serverName}/status`** - Update status for all versions (optional) Server names and version strings should be URL-encoded in paths. ### Authentication No authentication required by default. Subregistries may optionally require authentication following the [registry authorization specification](./registry-authorization.md). ### Content Type All requests and responses use `application/json` ### Pagination List endpoints use cursor-based pagination for efficient, stable results. #### Usage 1. **Initial request**: Omit the `cursor` parameter 2. **Subsequent requests**: Use the `nextCursor` value from the previous response 3. **End of results**: When `nextCursor` is null or empty, there are no more results **Important**: Always treat cursors as opaque strings. Never manually construct or modify cursor values. ### Basic Example: List Servers ```bash curl https://registry.example.com/v0.1/servers?limit=10 ``` ```json { "servers": [ { "server": { "name": "io.modelcontextprotocol/filesystem", "description": "Filesystem operations server", "version": "1.0.2" }, "_meta": { "io.modelcontextprotocol.registry/official": { "status": "active", "publishedAt": "2025-01-01T10:30:00Z", "isLatest": true } } } ], "metadata": { "count": 10, "nextCursor": "com.example/my-server:1.0.0" } } ``` For complete endpoint documentation, view the OpenAPI specification in a schema viewer. --- ### Reference/Api/Official Registry Api # Official MCP Registry API This document describes the API for the official MCP Registry hosted at `registry.modelcontextprotocol.io`. This API is based on the [generic registry API](./generic-registry-api.md) with additional endpoints and authentication. For publishing servers using the API, see the [publishing guide](../../modelcontextprotocol-io/quickstart.mdx). ## Base URLs - **Production**: `https://registry.modelcontextprotocol.io` - **Staging**: `https://staging.registry.modelcontextprotocol.io` ## Interactive Documentation - **[Live API Docs](https://registry.modelcontextprotocol.io/docs)** - Stoplight elements with try-it-now functionality - **[OpenAPI Spec](https://registry.modelcontextprotocol.io/openapi.yaml)** - Complete machine-readable specification ## Extensions The official registry implements the [Generic Registry API](./generic-registry-api.md) with the following specific configurations and extensions: ### Authentication Publishing requires namespace-based authentication: - **GitHub OAuth** - For `io.github.*` namespaces - **GitHub OIDC** - For publishing from GitHub Actions - **DNS verification** - For domain-based namespaces (`com.example.*`) - **HTTP verification** - For domain-based namespaces (`com.example.*`) See [Publisher Commands](../cli/commands.md) for authentication setup. ### Package Validation The official registry enforces additional [package validation requirements](../server-json/official-registry-requirements.md) when publishing. ### Server List Filtering The official registry extends the `GET /v0.1/servers` endpoint with additional query parameters for improved discovery and synchronization: - `updated_since` - Filter servers updated after RFC3339 timestamp (e.g., `2025-08-07T13:15:04.280Z`) - `search` - Case-insensitive substring search on server names (e.g., `filesystem`) - This is intentionally simple. For more advanced searching and filtering, use a subregistry. - `version` - Filter by version (currently supports `latest` for latest versions only) - `include_deleted` - Include deleted servers in results (default: `false`, but automatically `true` when `updated_since` is provided for incremental sync) These extensions enable efficient incremental synchronization for downstream registries and improved server discovery. Parameters can be combined and work with standard cursor-based pagination. Example: `GET /v0.1/servers?search=filesystem&updated_since=2025-08-01T00:00:00Z&version=latest` ### Server Detail The `GET /v0.1/servers/{serverName}/versions/{version}` endpoint returns detailed information about a specific server version. **Path parameters:** - `serverName` - URL-encoded server name (e.g., `io.github.user%2Fmy-server`) - `version` - Server version or `latest` for the most recent version **Query parameters:** - `include_deleted` - Include deleted servers in results (default: `false`) ### Server Version History The `GET /v0.1/servers/{serverName}/versions` endpoint returns all versions of a server. **Path parameters:** - `serverName` - URL-encoded server name (e.g., `io.github.user%2Fmy-server`) **Query parameters:** - `include_deleted` - Include deleted servers in results (default: `false`) ### Additional endpoints #### Auth endpoints - POST `/v0.1/auth/dns` - Exchange signed DNS challenge for auth token - POST `/v0.1/auth/http` - Exchange signed HTTP challenge for auth token - POST `/v0.1/auth/github-at` - Exchange GitHub access token for auth token - POST `/v0.1/auth/github-oidc` - Exchange GitHub OIDC token for auth token - POST `/v0.1/auth/oidc` - Exchange Google OIDC token for auth token (for admins) #### Status endpoints ##### Update Single Version Status `PATCH /v0.1/servers/{serverName}/versions/{version}/status` - Update status of a specific server version. **Path parameters:** - `serverName` - URL-encoded server name (e.g., `io.github.user%2Fmy-server`) - `version` - Server version to update **Request body:** - `status` (required) - New status: `active`, `deprecated`, or `deleted` - `statusMessage` (optional) - Message explaining the status change (max 500 characters, not allowed when status is `active`) ##### Update All Versions Status `PATCH /v0.1/servers/{serverName}/status` - Update status of all versions of a server in a single transaction. **Path parameters:** - `serverName` - URL-encoded server name (e.g., `io.github.user%2Fmy-server`) **Request body:** - `status` (required) - New status: `active`, `deprecated`, or `deleted` - `statusMessage` (optional) - Message explaining the status change (max 500 characters, not allowed when status is `active`) **Status values:** - `active` - Server is active and visible in default listings - `deprecated` - Server is deprecated but still visible with a warning message - `deleted` - Server is hidden from default listings (use `include_deleted=true` to show) **Authentication:** Requires `publish` or `edit` permission for the server namespace. #### Validation endpoint - POST `/v0.1/validate` - Validate a `server.json` without publishing it. Used by `mcp-publisher validate`. #### Service endpoints - GET `/v0.1/ping` - Liveness check - GET `/v0.1/version` - Registry version information #### Admin endpoints - GET `/metrics` - Prometheus metrics endpoint - GET `/v0.1/health` - Basic health check endpoint - PUT `/v0.1/servers/{serverName}/versions/{version}` - Edit specific server version Note that `/v0.1/validate`, `/v0.1/ping`, `/v0.1/version` and `/v0.1/health` are not described in [openapi.yaml](./openapi.yaml), which covers the server and publish endpoints only. --- ### Reference/Api/Registry Authorization # Registry Authorization MCP registries wishing to implement authentication SHOULD follow the [MCP Authorization Specification](https://modelcontextprotocol.io/specification/draft/basic/authorization). ## How it works The registry acts as an OAuth 2.1 Resource Server, identical to how MCP servers work. This means: - **MCP clients** can reuse their existing MCP authorization implementation without any changes - **Registries** validate access tokens the same way MCP servers do - **Users** get a consistent login experience across MCP servers and registries ## Registry-Specific Scopes Registries MAY use these scopes: - `mcp-registry:read` - List and read server metadata - `mcp-registry:write` - Publish, update, and delete servers These are recommendations - registries may use any set of scopes they deem sensible. Note that scopes only control what *types* of operations a user can perform. Registries should still apply user-level authorization to control which specific resources a user can access. For example, a user with `mcp-registry:write` might only be able to publish servers to namespaces they own, and may not have permissions to edit servers if the registry treats servers as immutable. ## Official Registry Authentication The official modelcontextprotocol.io registry remains public for reading. For publishing servers, it uses a custom JWT-based authentication system for legacy reasons - see [its API spec](official-registry-api.md#authentication). This may change in future to align with the MCP Authorization Specification. --- ### Reference/Server Json/CHANGELOG # Server JSON Schema Changelog Changes to the server.json schema and format. ## Draft (Unreleased) This section tracks changes that are in development and not yet released. The draft schema is available at [`server.schema.json`](./draft/server.schema.json) in this repository. ### Changed #### Transport URL Pattern Now Accepts Template Variables The `url` field in `StreamableHttpTransport` and `SseTransport` now accepts URLs that start with a template variable (e.g., `{baseUrl}`), in addition to the existing `http://` and `https://` prefixes. **Example:** ```json { "remotes": [{ "type": "streamable-http", "url": "{baseUrl}/mcp", "variables": { "baseUrl": { "description": "Base URL for the MCP server", "isRequired": true } } }] } ``` **Migration:** No changes required. Existing servers continue to work unchanged. ### Notes When ready for release, changes in this section will be moved to a dated version section (e.g., `## 2025-XX-XX`) and the schema will be published to a versioned URL. --- ## 2025-12-11 ### Changed #### URL Template Variables for Remote Servers ([#570](https://github.com/modelcontextprotocol/registry/pull/570)) Remote servers can now define URL template variables using `{curly_braces}` notation. This enables multi-tenant deployments where a single server definition can support multiple endpoints with configurable values. **Example:** ```json { "remotes": [{ "type": "streamable-http", "url": "https://{tenant_id}.api.example.com/mcp", "variables": { "tenant_id": { "description": "Your tenant identifier", "isRequired": true } } }] } ``` **Migration:** No changes required. Existing servers continue to work unchanged. --- ## 2025-10-17 ### Changed The `version` field is now **optional** for MCPB packages, providing flexibility for publishers. **Key Changes:** - **MCPB packages can now include an optional `version` field** - Previously rejected by validation, MCPB packages can now optionally specify a version field for clarity and metadata purposes. - **Both formats are valid**: - MCPB packages **with** version field: Provides explicit version metadata - MCPB packages **without** version field: Version information is embedded in the download URL (as before) **Migration:** Publishers using MCPB packages can optionally add a `version` field to their package configuration. This is particularly useful when: - The version information is not clearly visible in the download URL - You want to provide explicit version metadata for tooling and clients - You need consistent version tracking across different package types Existing MCPB packages without the version field continue to work without any changes. **Example - MCPB Package with optional version:** ```json { "packages": [{ "registryType": "mcpb", "identifier": "https://github.com/example/releases/download/v1.0.0/package.mcpb", "version": "1.0.0", "fileSha256": "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce", "transport": { "type": "stdio" } }] } ``` **Example - MCPB Package without version (still valid):** ```json { "packages": [{ "registryType": "mcpb", "identifier": "https://github.com/example/releases/download/v1.0.0/package.mcpb", "fileSha256": "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce", "transport": { "type": "stdio" } }] } ``` ### Schema Version - Schema version: `2025-10-11` β†’ `2025-10-17` ## 2025-10-11 ### Changed #### Package Format Enhancements ([#634](https://github.com/modelcontextprotocol/registry/pull/634)) The `Package` schema has been refactored to better support different package types with dedicated handling per registry type. **Key Changes:** - **`version` field is now optional** - Previously required for all packages, now only used by npm, pypi, and nuget. OCI packages include version in the identifier (e.g., `ghcr.io/owner/repo:v1.0.0`), and MCPB packages use direct download URLs. - **Enhanced documentation** - Added detailed comments explaining which fields are relevant for each `registryType`: - **NPM/PyPI/NuGet**: Use `registryType`, `identifier` (package name), `version`, optional `registryBaseUrl` - **OCI**: Use `registryType`, `identifier` (full image reference with tag) - **MCPB**: Use `registryType`, `identifier` (download URL), `fileSha256` (required) - **Field clarifications**: - `identifier`: Now clearly documented as package name for registries, full image reference for OCI, or download URL for MCPB - `fileSha256`: Clarified as required for MCPB packages and optional for other types - `registryBaseUrl`: Clarified as used by npm/pypi/nuget but not by oci/mcpb **Migration:** Publishers using OCI or MCPB packages can now omit the `version` field, as it's either embedded in the identifier (OCI) or not applicable (MCPB direct downloads). Publishers using npm, pypi, or nuget should continue to provide the `version` field as before. **Example - OCI Package (version in identifier):** ```json { "packages": [{ "registryType": "oci", "identifier": "ghcr.io/modelcontextprotocol/server-example:v1.2.3", "transport": { "type": "stdio" } }] } ``` **Example - MCPB Package (no version field):** ```json { "packages": [{ "registryType": "mcpb", "identifier": "https://github.com/example/releases/download/v1.0.0/package.mcpb", "fileSha256": "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce", "transport": { "type": "stdio" } }] } ``` ### Schema Version - Schema version: `2025-09-29` β†’ `2025-10-11` ## 2025-09-29 ### ⚠️ BREAKING CHANGES #### Schema Simplification Removed registry-managed fields from publisher-controlled server.json schema. **Removed fields:** - `status` field from Server object (now managed by registry in API responses) - `io.modelcontextprotocol.registry/official` from `_meta` (read-only, added by registry) **Migration:** Publishers should remove these fields from their `server.json` files. The registry will manage server status and official metadata separately. ### Changed - Schema version: `2025-09-16` β†’ `2025-09-29` ## 2025-09-16 ### ⚠️ BREAKING CHANGES #### Field Names: snake_case β†’ camelCase ([#428](https://github.com/modelcontextprotocol/registry/issues/428)) All JSON field names standardized to camelCase. **All existing `server.json` files must be updated.** **Changed fields:** - `registry_type` β†’ `registryType` - `registry_base_url` β†’ `registryBaseUrl` - `file_sha256` β†’ `fileSha256` - `runtime_hint` β†’ `runtimeHint` - `runtime_arguments` β†’ `runtimeArguments` - `package_arguments` β†’ `packageArguments` - `environment_variables` β†’ `environmentVariables` - `is_required` β†’ `isRequired` - `is_secret` β†’ `isSecret` - `value_hint` β†’ `valueHint` - `is_repeated` β†’ `isRepeated` - `website_url` β†’ `websiteUrl` #### Migration Examples **Package Configuration:** ```json // OLD - Will be rejected { "packages": [{ "registry_type": "npm", "registry_base_url": "https://registry.npmjs.org", "file_sha256": "abc123...", "runtime_hint": "node", "runtime_arguments": [...], "package_arguments": [...], "environment_variables": [...] }] } // NEW - Required format { "packages": [{ "registryType": "npm", "registryBaseUrl": "https://registry.npmjs.org", "fileSha256": "abc123...", "runtimeHint": "node", "runtimeArguments": [...], "packageArguments": [...], "environmentVariables": [...] }] } ``` **Arguments Configuration:** ```json // OLD - Will be rejected { "runtime_arguments": [ { "name": "port", "is_required": true, "is_repeated": false, "value_hint": "8080" } ] } // NEW - Required format { "runtimeArguments": [ { "name": "port", "isRequired": true, "isRepeated": false, "valueHint": "8080" } ] } ``` **Environment Variables:** ```json // OLD - Will be rejected { "environment_variables": [ { "name": "API_KEY", "is_required": true, "is_secret": true } ] } // NEW - Required format { "environmentVariables": [ { "name": "API_KEY", "isRequired": true, "isSecret": true } ] } ``` #### Migration Checklist for Publishers - [ ] Update your `server.json` files to use camelCase field names - [ ] Test server publishing with new CLI version - [ ] Update any automation scripts that reference old field names - [ ] Update documentation referencing old field names #### Updated Schema Reference πŸ”— **Current schema**: https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json ### Changed - Schema version: `2025-07-09` β†’ `2025-09-16` ## 2025-07-09 Initial release of the server.json schema. --- ### Reference/Server Json/CONTRIBUTING # Contributing to server.json Schema This document describes the process for making and releasing changes to the `server.json` schema. ## Making Changes 1. **Modify the OpenAPI spec**: Edit `docs/reference/api/openapi.yaml` with your schema changes. The `ServerDetail` component defines the server.json structure. 2. **Regenerate the schema**: Run `make generate-schema` to update `server.schema.json` from the OpenAPI spec. 3. **Update the changelog**: Add your changes to the "Draft (Unreleased)" section in `CHANGELOG.md`. 4. **Open a PR**: Submit a pull request to this repository for review. ## Releasing Changes When the draft changes are ready for release: 1. **Update the changelog**: Move changes from "Draft (Unreleased)" to a new dated section (e.g., `## 2025-XX-XX`). 2. **Update the schema URL**: Change the `$id` in the schema and the example URL in `openapi.yaml` from `draft` to the release date (e.g., `2025-XX-XX`). 3. **Merge the PR**: Get approval and merge the changes to main. 4. **Publish to static hosting**: Open a PR on [modelcontextprotocol/static](https://github.com/modelcontextprotocol/static/tree/main/schemas) to add the new versioned schema file. This "locks in" the released schema at its versioned URL. ## Schema Versioning - **Draft schema**: `https://raw.githubusercontent.com/modelcontextprotocol/registry/main/docs/reference/server-json/draft/server.schema.json` - For in-progress changes, may change without notice. - **Released schemas**: `https://static.modelcontextprotocol.io/schemas/YYYY-MM-DD/server.schema.json` - Stable, versioned by release date. --- ### Reference/Server Json/Generic Server Json # server.json Format Specification A `server.json` file is a standardized way to describe MCP servers for registry publishing, client discovery, and package management. Also see: - For step-by-step instructions on creating and using server.json files, see the [publishing guide](../../modelcontextprotocol-io/quickstart.mdx). - For understanding the validation requirements when publishing to the official registry, see [official registry requirements](./official-registry-requirements.md). ## Browse the Complete Schema **πŸ“‹ View the full specification interactively**: Open [server.schema.json](./draft/server.schema.json) in a schema viewer like [json-schema.app](https://json-schema.app/view/%23?url=https%3A%2F%2Fstatic.modelcontextprotocol.io%2Fschemas%2F2025-09-29%2Fserver.schema.json). The schema contains all field definitions, validation rules, examples, and detailed descriptions. The official registry has some more restrictions on top of this. See the [official registry requirements](./official-registry-requirements.md) for details. ## Extension Metadata with `_meta` The optional `_meta` field allows publishers to include custom metadata alongside their server definitions using reverse DNS namespacing. ```jsonc { "_meta": { "io.modelcontextprotocol.registry/publisher-provided": { // Your custom metadata here } } } ``` When publishing to the official registry, custom metadata must be placed under the key `io.modelcontextprotocol.registry/publisher-provided`. See the [official registry requirements](./official-registry-requirements.md) for detailed restrictions and examples. ## Examples ### Basic Server with NPM Package ```json { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "io.modelcontextprotocol.anonymous/brave-search", "description": "MCP server for Brave Search API integration", "title": "Brave Search", "websiteUrl": "https://anonymous.modelcontextprotocol.io/examples", "repository": { "url": "https://github.com/modelcontextprotocol/servers", "source": "github" }, "version": "1.0.2", "packages": [ { "registryType": "npm", "registryBaseUrl": "https://registry.npmjs.org", "identifier": "@modelcontextprotocol/server-brave-search", "version": "1.0.2", "transport": { "type": "stdio" }, "environmentVariables": [ { "name": "BRAVE_API_KEY", "description": "Brave Search API Key", "isRequired": true, "isSecret": true } ] } ], "_meta": { "io.modelcontextprotocol.registry/publisher-provided": { "tool": "npm-publisher", "version": "1.0.1", "build_info": { "timestamp": "2023-12-01T10:30:00Z" } } } } ``` ### Server in a Monorepo with Subfolder For MCP servers located within a subdirectory of a larger repository (monorepo structure), use the `subfolder` field to specify the relative path: ```json { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "io.modelcontextprotocol/everything", "description": "MCP server that exercises all the features of the MCP protocol", "title": "Everything", "repository": { "url": "https://github.com/modelcontextprotocol/servers", "source": "github", "subfolder": "src/everything" }, "version": "0.6.2", "packages": [ { "registryType": "npm", "registryBaseUrl": "https://registry.npmjs.org", "identifier": "@modelcontextprotocol/everything", "version": "0.6.2", "transport": { "type": "stdio" } } ], "_meta": { "io.modelcontextprotocol.registry/publisher-provided": { "tool": "npm-publisher", "version": "1.0.1", "build_info": { "timestamp": "2023-12-01T10:30:00Z" } } } } ``` ### Constant (fixed) arguments needed to start the MCP server Suppose your MCP server application requires a `mcp start` CLI arguments to start in MCP server mode. Express these as positional arguments like this: ```json { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "io.github.joelverhagen/knapcode-samplemcpserver", "description": "Sample NuGet MCP server for a random number and random weather", "version": "0.4.0-beta", "packages": [ { "registryType": "nuget", "registryBaseUrl": "https://api.nuget.org/v3/index.json", "identifier": "Knapcode.SampleMcpServer", "version": "0.4.0-beta", "transport": { "type": "stdio" }, "packageArguments": [ { "type": "positional", "value": "mcp" }, { "type": "positional", "value": "start" } ] } ], "_meta": { "io.modelcontextprotocol.registry/publisher-provided": { "tool": "nuget-publisher", "version": "2.1.0", "build_info": { "timestamp": "2023-11-15T14:22:00Z", "pipeline_id": "nuget-build-456" } } } } ``` This will essentially instruct the MCP client to execute `dnx Knapcode.SampleMcpServer@0.4.0-beta -- mcp start` instead of the default `dnx Knapcode.SampleMcpServer@0.4.0-beta` (when no `packageArguments` are provided). ### Filesystem Server with Multiple Packages ``` /* Detailed source-code truncated for AI context efficiency. */ ``` The same `registryType` / `identifier` pattern works for other supported OCI hosts. For example, an image on Quay.io: ```json { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "io.github.example/quay-sample-mcp", "description": "Example MCP server distributed as an OCI image on Quay.io", "version": "1.0.0", "packages": [ { "registryType": "oci", "identifier": "quay.io/myorg/my-mcp-server:1.0.0", "transport": { "type": "stdio" } } ] } ``` ### Remote Server Example ```json { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "io.modelcontextprotocol.anonymous/mcp-fs", "description": "Cloud-hosted MCP filesystem server", "repository": { "url": "https://github.com/example/remote-fs", "source": "github", "id": "xyz789ab-cdef-0123-4567-890ghijklmno" }, "version": "2.0.0", "remotes": [ { "type": "streamable-http", "url": "https://mcp-fs.anonymous.modelcontextprotocol.io/http" } ], "_meta": { "io.modelcontextprotocol.registry/publisher-provided": { "tool": "cloud-deployer", "version": "2.4.0", "build_info": { "commit": "f7e8d9c2b1a0", "timestamp": "2023-12-05T08:45:00Z", "deployment_id": "remote-fs-deploy-456", "region": "us-west-2" } } } } ``` ### Python Package Example ```json { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "io.github.example/weather-mcp", "description": "Python MCP server for weather data access", "title": "Weather", "repository": { "url": "https://github.com/example/weather-mcp", "source": "github", "id": "def456gh-ijkl-7890-mnop-qrstuvwxyz12" }, "version": "0.5.0", "packages": [ { "registryType": "pypi", "registryBaseUrl": "https://pypi.org", "identifier": "weather-mcp-server", "version": "0.5.0", "runtimeHint": "uvx", "transport": { "type": "stdio" }, "environmentVariables": [ { "name": "WEATHER_API_KEY", "description": "API key for weather service", "isRequired": true, "isSecret": true }, { "name": "WEATHER_UNITS", "description": "Temperature units (celsius, fahrenheit)", "default": "celsius" } ] } ], "_meta": { "io.modelcontextprotocol.registry/publisher-provided": { "tool": "poetry-publisher", "version": "1.8.3", "build_info": { "python_version": "3.11.5", "timestamp": "2023-11-28T16:20:00Z", "build_id": "pypi-weather-123", "dependencies_hash": "sha256:a9b8c7d6e5f4" } } } } ``` ### Cargo (Rust) Package Example `cargo install ` places the binary on PATH (via `~/.cargo/bin`); MCP clients invoke it directly by name. There is no single-shot equivalent of `npx` (npm), `uvx` (PyPI), or `dnx` (NuGet, .NET 10 SDK) for cargo β€” install once, run by name. ```json { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "io.github.example/widget-mcp", "description": "Rust-native MCP server", "title": "Widget", "repository": { "url": "https://github.com/example/widget-mcp", "source": "github" }, "version": "0.3.0", "packages": [ { "registryType": "cargo", "registryBaseUrl": "https://crates.io", "identifier": "widget-mcp", "version": "0.3.0", "transport": { "type": "stdio" } } ] } ``` ### NuGet (.NET) Package Example The `dnx` tool ships with the .NET 10 SDK, starting with Preview 6. ```json { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "io.github.joelverhagen/knapcode-samplemcpserver", "description": "Sample NuGet MCP server for a random number and random weather", "repository": { "url": "https://github.com/joelverhagen/Knapcode.SampleMcpServer", "source": "github", "id": "example-nuget-id-0000-1111-222222222222" }, "version": "0.5.0", "packages": [ { "registryType": "nuget", "registryBaseUrl": "https://api.nuget.org/v3/index.json", "identifier": "Knapcode.SampleMcpServer", "version": "0.5.0", "runtimeHint": "dnx", "transport": { "type": "stdio" }, "environmentVariables": [ { "name": "WEATHER_CHOICES", "description": "Comma separated list of weather descriptions to randomly select.", "isRequired": true, "isSecret": false } ] } ], "_meta": { "io.modelcontextprotocol.registry/publisher-provided": { "tool": "dotnet-publisher", "version": "8.0.100", "build_info": { "dotnet_version": "8.0.0", "timestamp": "2023-12-10T12:15:00Z", "configuration": "Release", "target_framework": "net8.0", "build_number": "20231210.1" } } } } ``` ### Complex Docker Server with Multiple Arguments ``` /* Detailed source-code truncated for AI context efficiency. */ ``` ### Server with Remote and Package Options ``` /* Detailed source-code truncated for AI context efficiency. */ ``` ### MCP Bundle (MCPB) Package Example ```json { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "io.modelcontextprotocol/text-editor", "description": "MCP Bundle server for advanced text editing capabilities", "title": "Text Editor", "repository": { "url": "https://github.com/modelcontextprotocol/text-editor-mcpb", "source": "github" }, "version": "1.0.2", "packages": [ { "registryType": "mcpb", "identifier": "https://github.com/modelcontextprotocol/text-editor-mcpb/releases/download/v1.0.2/text-editor.mcpb", "fileSha256": "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce", "transport": { "type": "stdio" } } ], "_meta": { "io.modelcontextprotocol.registry/publisher-provided": { "tool": "mcpb-publisher", "version": "1.0.0", "build_info": { "timestamp": "2023-12-02T09:15:00Z", "bundle_format": "mcpb-v1" } } } } ``` This example shows an MCPB (MCP Bundle) package that: - Is hosted on GitHub Releases (an allowlisted provider) - Includes a SHA-256 hash for integrity verification - Can be downloaded and executed directly by MCP clients that support MCPB ### Embedded MCP inside a CLI tool Some CLI tools bundle an MCP server, without a standalone MCP package or a public repository. In these cases, reuse the existing `packages` shape by pointing at the host CLI package and supplying the `packageArguments` and `runtimeHint` if needed to start the MCP server. ```json { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "io.snyk/cli-mcp", "description": "MCP server provided by the Snyk CLI", "title": "Snyk", "version": "1.1298.0", "packages": [ { "registryType": "npm", "registryBaseUrl": "https://registry.npmjs.org", "identifier": "snyk", "version": "1.1298.0", "transport": { "type": "stdio" }, "packageArguments": [ { "type": "positional", "value": "mcp" }, { "type": "named", "name": "-t", "description": "Transport type for MCP server", "default": "stdio", "choices": ["stdio", "sse"] } ] } ] } ``` ### Server with Custom Installation Path For MCP servers that follow a custom installation path or are embedded in applications without standalone packages, use the `websiteUrl` field to direct users to setup documentation: ```json { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "io.modelcontextprotocol.anonymous/embedded-mcp", "description": "MCP server embedded in a Desktop app", "websiteUrl": "https://anonymous.modelcontextprotocol.io/embedded-mcp-guide", "version": "0.1.0" } ``` ### Remote Server with URL Templating This example demonstrates URL templating for remote servers, useful for multi-tenant deployments where each instance has its own endpoint. Unlike Package transports (which reference parent arguments/environment variables), Remote transports define their own variables: ```json { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "io.modelcontextprotocol.anonymous/multi-tenant-server", "description": "MCP server with configurable remote endpoint", "title": "Multi-Tenant Server", "version": "1.0.0", "remotes": [ { "type": "streamable-http", "url": "https://anonymous.modelcontextprotocol.io/mcp/{tenant_id}", "variables": { "tenant_id": { "description": "Tenant identifier (e.g., 'us-cell1', 'emea-cell1')", "isRequired": true } } } ] } ``` Clients configure the tenant identifier, and the `{tenant_id}` variable in the URL gets replaced with the provided variable value to connect to the appropriate tenant endpoint (e.g., `https://anonymous.modelcontextprotocol.io/mcp/us-cell1` or `https://anonymous.modelcontextprotocol.io/mcp/emea-cell1`). The same URL templating works with SSE transport: ```json { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "io.modelcontextprotocol.anonymous/events-server", "description": "MCP server using SSE with tenant-specific endpoints", "version": "1.0.0", "remotes": [ { "type": "sse", "url": "https://events.anonymous.modelcontextprotocol.io/sse/{tenant_id}", "variables": { "tenant_id": { "description": "Tenant identifier", "isRequired": true } } } ] } ``` ### Local Server with URL Templating This example demonstrates URL templating for local/package servers, where variables reference parent Package arguments or environment variables: ```json { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "io.github.example/configurable-server", "description": "Local MCP server with configurable port", "title": "Configurable Server", "version": "1.0.0", "packages": [ { "registryType": "npm", "registryBaseUrl": "https://registry.npmjs.org", "identifier": "@example/mcp-server", "version": "1.0.0", "transport": { "type": "streamable-http", "url": "http://localhost:{--port}/mcp" }, "packageArguments": [ { "type": "named", "name": "--port", "description": "Port for the server to listen on", "default": "3000" } ] } ] } ``` The `{--port}` variable in the URL references the `--port` argument `name` from packageArguments. For positional arguments, an argument with the `valueHint` of `port` could similarly be referenced as `{port}`. When the package runs with `--port 8080`, the URL becomes `http://localhost:8080/mcp`. ---