Make mcpb package version optional and fix schema validation (#686)
<!-- Provide a brief summary of your changes --> ## Motivation and Context <!-- Why is this change needed? What problem does it solve? --> The following PR: * Makes mcpb package version property optional * Bumps the schema and reflects that in the code * Makes sure the publisher tests are being covered by doing `make check` ## How Has This Been Tested? <!-- Have you tested this in a real application? Which scenarios were tested? --> ## Breaking Changes <!-- Will users need to update their code or configurations? --> ## Types of changes <!-- What types of changes does your code introduce? Put an `x` in all the boxes that apply: --> - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update ## Checklist <!-- Go over all the following points, and put an `x` in all the boxes that apply. --> - [ ] I have read the [MCP Documentation](https://modelcontextprotocol.io) - [ ] My code follows the repository's style guidelines - [ ] New and existing tests pass locally - [ ] I have added appropriate error handling - [ ] I have added or updated documentation as needed ## Additional context <!-- Add any other context, implementation notes, or design decisions --> --------- Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
This commit is contained in:
parent
f870a4c000
commit
0828bc483c
16 changed files with 131 additions and 81 deletions
2
Makefile
2
Makefile
|
|
@ -32,7 +32,7 @@ test-unit: ## Run unit tests with coverage (requires PostgreSQL)
|
||||||
@echo "Waiting for PostgreSQL to be ready..."
|
@echo "Waiting for PostgreSQL to be ready..."
|
||||||
@sleep 3
|
@sleep 3
|
||||||
@echo "Running unit tests..."
|
@echo "Running unit tests..."
|
||||||
go test -v -race -coverprofile=coverage.out -covermode=atomic ./internal/...
|
go test -v -race -coverprofile=coverage.out -covermode=atomic ./internal/... ./cmd/...
|
||||||
go tool cover -html=coverage.out -o coverage.html
|
go tool cover -html=coverage.out -o coverage.html
|
||||||
@echo "Coverage report generated: coverage.html"
|
@echo "Coverage report generated: coverage.html"
|
||||||
@echo "Stopping PostgreSQL..."
|
@echo "Stopping PostgreSQL..."
|
||||||
|
|
|
||||||
|
|
@ -44,8 +44,8 @@ func TestPublishCommand_DeprecatedSchema(t *testing.T) {
|
||||||
errorSubstr: "deprecated schema detected",
|
errorSubstr: "deprecated schema detected",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "current 2025-09-29 schema should pass validation",
|
name: "current 2025-10-17 schema should pass validation",
|
||||||
schema: "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
schema: "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
||||||
expectError: false,
|
expectError: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
||||||
"name": "io.github.domdomegg/airtable-mcp-server",
|
"name": "io.github.domdomegg/airtable-mcp-server",
|
||||||
"description": "Read and write access to Airtable database schemas, tables, and records.",
|
"description": "Read and write access to Airtable database schemas, tables, and records.",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|
@ -62,7 +62,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
||||||
"name": "io.github.domdomegg/airtable-mcp-server",
|
"name": "io.github.domdomegg/airtable-mcp-server",
|
||||||
"description": "Read and write access to Airtable database schemas, tables, and records.",
|
"description": "Read and write access to Airtable database schemas, tables, and records.",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|
@ -115,7 +115,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
||||||
"name": "io.github.domdomegg/time-mcp-nuget",
|
"name": "io.github.domdomegg/time-mcp-nuget",
|
||||||
"description": "Get the current UTC time in RFC 3339 format.",
|
"description": "Get the current UTC time in RFC 3339 format.",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|
@ -136,7 +136,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
||||||
"name": "io.github.domdomegg/time-mcp-pypi",
|
"name": "io.github.domdomegg/time-mcp-pypi",
|
||||||
"description": "Get the current UTC time in RFC 3339 format.",
|
"description": "Get the current UTC time in RFC 3339 format.",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ You can also add custom metadata to servers using the `_meta` field. For example
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
||||||
"name": "io.github.yourname/weather-server",
|
"name": "io.github.yourname/weather-server",
|
||||||
"description": "MCP server for weather data access",
|
"description": "MCP server for weather data access",
|
||||||
"status": "active",
|
"status": "active",
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ This creates a `server.json` with auto-detected values. You'll see something lik
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
||||||
"name": "io.github.yourname/weather-data-mcp",
|
"name": "io.github.yourname/weather-data-mcp",
|
||||||
"title": "Weather Data",
|
"title": "Weather Data",
|
||||||
"description": "Access real-time weather data and forecasts",
|
"description": "Access real-time weather data and forecasts",
|
||||||
|
|
@ -153,7 +153,7 @@ Add an `mcpName` field to your `package.json`:
|
||||||
### Example server.json
|
### Example server.json
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
||||||
"name": "io.github.username/slack-integration-mcp",
|
"name": "io.github.username/slack-integration-mcp",
|
||||||
"title": "Slack Integration",
|
"title": "Slack Integration",
|
||||||
"description": "Send messages and manage Slack workspaces",
|
"description": "Send messages and manage Slack workspaces",
|
||||||
|
|
@ -192,7 +192,7 @@ Add it to your README.md file (which becomes the package description on PyPI). T
|
||||||
### Example server.json
|
### Example server.json
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
||||||
"name": "io.github.username/database-query-mcp",
|
"name": "io.github.username/database-query-mcp",
|
||||||
"title": "Database Query",
|
"title": "Database Query",
|
||||||
"description": "Execute SQL queries and manage database connections",
|
"description": "Execute SQL queries and manage database connections",
|
||||||
|
|
@ -231,7 +231,7 @@ Add a README file to your NuGet package that includes the server name. This can
|
||||||
### Example server.json
|
### Example server.json
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
||||||
"name": "io.github.username/azure-devops-mcp",
|
"name": "io.github.username/azure-devops-mcp",
|
||||||
"title": "Azure DevOps",
|
"title": "Azure DevOps",
|
||||||
"description": "Manage Azure DevOps work items and pipelines",
|
"description": "Manage Azure DevOps work items and pipelines",
|
||||||
|
|
@ -274,7 +274,7 @@ LABEL io.modelcontextprotocol.server.name="io.github.username/server-name"
|
||||||
### Example server.json (Docker Hub)
|
### Example server.json (Docker Hub)
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
||||||
"name": "io.github.username/kubernetes-manager-mcp",
|
"name": "io.github.username/kubernetes-manager-mcp",
|
||||||
"title": "Kubernetes Manager",
|
"title": "Kubernetes Manager",
|
||||||
"description": "Deploy and manage Kubernetes resources",
|
"description": "Deploy and manage Kubernetes resources",
|
||||||
|
|
@ -282,9 +282,7 @@ LABEL io.modelcontextprotocol.server.name="io.github.username/server-name"
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"registryType": "oci",
|
"registryType": "oci",
|
||||||
"registryBaseUrl": "https://docker.io",
|
"identifier": "docker.io/yourusername/kubernetes-manager-mcp:1.0.0",
|
||||||
"identifier": "yourusername/kubernetes-manager-mcp",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"transport": {
|
"transport": {
|
||||||
"type": "stdio"
|
"type": "stdio"
|
||||||
}
|
}
|
||||||
|
|
@ -296,7 +294,7 @@ LABEL io.modelcontextprotocol.server.name="io.github.username/server-name"
|
||||||
### Example server.json (GitHub Container Registry)
|
### Example server.json (GitHub Container Registry)
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
||||||
"name": "io.github.username/git-operations-mcp",
|
"name": "io.github.username/git-operations-mcp",
|
||||||
"title": "Git Operations",
|
"title": "Git Operations",
|
||||||
"description": "Advanced Git repository management and operations",
|
"description": "Advanced Git repository management and operations",
|
||||||
|
|
@ -304,9 +302,7 @@ LABEL io.modelcontextprotocol.server.name="io.github.username/server-name"
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"registryType": "oci",
|
"registryType": "oci",
|
||||||
"registryBaseUrl": "https://ghcr.io",
|
"identifier": "ghcr.io/username/git-operations-mcp:1.0.0",
|
||||||
"identifier": "username/git-operations-mcp",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"transport": {
|
"transport": {
|
||||||
"type": "stdio"
|
"type": "stdio"
|
||||||
}
|
}
|
||||||
|
|
@ -315,9 +311,9 @@ LABEL io.modelcontextprotocol.server.name="io.github.username/server-name"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The identifier is `namespace/repository`, and version is the tag and optionally digest.
|
The identifier format is `registry/namespace/repository:tag` (e.g., `docker.io/user/app:1.0.0` or `ghcr.io/user/app:1.0.0`). The version can also be specified as a digest.
|
||||||
|
|
||||||
The official MCP registry currently supports Docker Hub (`https://docker.io`) and GitHub Container Registry (`https://ghcr.io`).
|
The official MCP registry currently supports Docker Hub (`docker.io`) and GitHub Container Registry (`ghcr.io`).
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
|
@ -339,7 +335,7 @@ openssl dgst -sha256 server.mcpb
|
||||||
### Example server.json
|
### Example server.json
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
||||||
"name": "io.github.username/image-processor-mcp",
|
"name": "io.github.username/image-processor-mcp",
|
||||||
"title": "Image Processor",
|
"title": "Image Processor",
|
||||||
"description": "Process and transform images with various filters",
|
"description": "Process and transform images with various filters",
|
||||||
|
|
@ -348,7 +344,6 @@ openssl dgst -sha256 server.mcpb
|
||||||
{
|
{
|
||||||
"registryType": "mcpb",
|
"registryType": "mcpb",
|
||||||
"identifier": "https://github.com/username/image-processor-mcp/releases/download/v1.0.0/image-processor.mcpb",
|
"identifier": "https://github.com/username/image-processor-mcp/releases/download/v1.0.0/image-processor.mcpb",
|
||||||
"version": "1.0.0",
|
|
||||||
"fileSha256": "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce",
|
"fileSha256": "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce",
|
||||||
"transport": {
|
"transport": {
|
||||||
"type": "stdio"
|
"type": "stdio"
|
||||||
|
|
@ -385,7 +380,7 @@ Add the `remotes` field to your `server.json` (can coexist with `packages`):
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
||||||
"name": "com.yourcompany/acme-analytics",
|
"name": "com.yourcompany/acme-analytics",
|
||||||
"title": "ACME Analytics",
|
"title": "ACME Analytics",
|
||||||
"description": "Real-time business intelligence and reporting platform",
|
"description": "Real-time business intelligence and reporting platform",
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
openapi: 3.1.0
|
openapi: 3.1.0
|
||||||
jsonSchemaDialect: "https://json-schema.org/draft/2020-12/schema"
|
jsonSchemaDialect: "https://json-schema.org/draft/2020-12/schema"
|
||||||
$id: https://modelcontextprotocol.io/schemas/draft/2025-10-11/server-registry-openapi
|
$id: https://modelcontextprotocol.io/schemas/draft/2025-10-17/server-registry-openapi
|
||||||
info:
|
info:
|
||||||
title: MCP Server Registry API
|
title: MCP Server Registry API
|
||||||
version: "2025-10-11"
|
version: "2025-10-17"
|
||||||
summary: API for discovering and accessing MCP server metadata
|
summary: API for discovering and accessing MCP server metadata
|
||||||
description: |
|
description: |
|
||||||
Specification for a theoretical REST API that serves up metadata about MCP servers.
|
Specification for a theoretical REST API that serves up metadata about MCP servers.
|
||||||
|
|
@ -616,7 +616,7 @@ components:
|
||||||
type: string
|
type: string
|
||||||
format: uri
|
format: uri
|
||||||
description: JSON Schema URI for this server.json format
|
description: JSON Schema URI for this server.json format
|
||||||
example: "https://static.modelcontextprotocol.io/schemas/2025-10-11/server.schema.json"
|
example: "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json"
|
||||||
packages:
|
packages:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,60 @@
|
||||||
|
|
||||||
Changes to the server.json schema and format.
|
Changes to the server.json schema and format.
|
||||||
|
|
||||||
|
## 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
|
## 2025-10-11
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ The official registry has some more restrictions on top of this. See the [offici
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
||||||
"name": "io.modelcontextprotocol.anonymous/brave-search",
|
"name": "io.modelcontextprotocol.anonymous/brave-search",
|
||||||
"description": "MCP server for Brave Search API integration",
|
"description": "MCP server for Brave Search API integration",
|
||||||
"title": "Brave Search",
|
"title": "Brave Search",
|
||||||
|
|
@ -69,7 +69,7 @@ For MCP servers located within a subdirectory of a larger repository (monorepo s
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
||||||
"name": "io.modelcontextprotocol/everything",
|
"name": "io.modelcontextprotocol/everything",
|
||||||
"description": "MCP server that exercises all the features of the MCP protocol",
|
"description": "MCP server that exercises all the features of the MCP protocol",
|
||||||
"title": "Everything",
|
"title": "Everything",
|
||||||
|
|
@ -108,7 +108,7 @@ Suppose your MCP server application requires a `mcp start` CLI arguments to star
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
||||||
"name": "io.github.joelverhagen/knapcode-samplemcpserver",
|
"name": "io.github.joelverhagen/knapcode-samplemcpserver",
|
||||||
"description": "Sample NuGet MCP server for a random number and random weather",
|
"description": "Sample NuGet MCP server for a random number and random weather",
|
||||||
"version": "0.4.0-beta",
|
"version": "0.4.0-beta",
|
||||||
|
|
@ -152,7 +152,7 @@ This will essentially instruct the MCP client to execute `dnx Knapcode.SampleMcp
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
||||||
"name": "io.github.modelcontextprotocol/filesystem",
|
"name": "io.github.modelcontextprotocol/filesystem",
|
||||||
"description": "Node.js server implementing Model Context Protocol (MCP) for filesystem operations.",
|
"description": "Node.js server implementing Model Context Protocol (MCP) for filesystem operations.",
|
||||||
"title": "Filesystem",
|
"title": "Filesystem",
|
||||||
|
|
@ -252,7 +252,7 @@ This will essentially instruct the MCP client to execute `dnx Knapcode.SampleMcp
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
||||||
"name": "io.modelcontextprotocol.anonymous/mcp-fs",
|
"name": "io.modelcontextprotocol.anonymous/mcp-fs",
|
||||||
"description": "Cloud-hosted MCP filesystem server",
|
"description": "Cloud-hosted MCP filesystem server",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|
@ -286,7 +286,7 @@ This will essentially instruct the MCP client to execute `dnx Knapcode.SampleMcp
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
||||||
"name": "io.github.example/weather-mcp",
|
"name": "io.github.example/weather-mcp",
|
||||||
"description": "Python MCP server for weather data access",
|
"description": "Python MCP server for weather data access",
|
||||||
"title": "Weather",
|
"title": "Weather",
|
||||||
|
|
@ -342,7 +342,7 @@ The `dnx` tool ships with the .NET 10 SDK, starting with Preview 6.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
||||||
"name": "io.github.joelverhagen/knapcode-samplemcpserver",
|
"name": "io.github.joelverhagen/knapcode-samplemcpserver",
|
||||||
"description": "Sample NuGet MCP server for a random number and random weather",
|
"description": "Sample NuGet MCP server for a random number and random weather",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|
@ -391,7 +391,7 @@ The `dnx` tool ships with the .NET 10 SDK, starting with Preview 6.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
||||||
"name": "io.github.example/database-manager",
|
"name": "io.github.example/database-manager",
|
||||||
"description": "MCP server for database operations with support for multiple database types",
|
"description": "MCP server for database operations with support for multiple database types",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|
@ -500,7 +500,7 @@ The `dnx` tool ships with the .NET 10 SDK, starting with Preview 6.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
||||||
"name": "io.modelcontextprotocol.anonymous/hybrid-mcp",
|
"name": "io.modelcontextprotocol.anonymous/hybrid-mcp",
|
||||||
"description": "MCP server available as both local package and remote service",
|
"description": "MCP server available as both local package and remote service",
|
||||||
"title": "Hybrid",
|
"title": "Hybrid",
|
||||||
|
|
@ -586,7 +586,7 @@ The `dnx` tool ships with the .NET 10 SDK, starting with Preview 6.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
||||||
"name": "io.modelcontextprotocol/text-editor",
|
"name": "io.modelcontextprotocol/text-editor",
|
||||||
"description": "MCP Bundle server for advanced text editing capabilities",
|
"description": "MCP Bundle server for advanced text editing capabilities",
|
||||||
"title": "Text Editor",
|
"title": "Text Editor",
|
||||||
|
|
@ -629,7 +629,7 @@ Some CLI tools bundle an MCP server, without a standalone MCP package or a publi
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
||||||
"name": "io.snyk/cli-mcp",
|
"name": "io.snyk/cli-mcp",
|
||||||
"description": "MCP server provided by the Snyk CLI",
|
"description": "MCP server provided by the Snyk CLI",
|
||||||
"title": "Snyk",
|
"title": "Snyk",
|
||||||
|
|
@ -664,7 +664,7 @@ For MCP servers that follow a custom installation path or are embedded in applic
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
||||||
"name": "io.modelcontextprotocol.anonymous/embedded-mcp",
|
"name": "io.modelcontextprotocol.anonymous/embedded-mcp",
|
||||||
"description": "MCP server embedded in a Desktop app",
|
"description": "MCP server embedded in a Desktop app",
|
||||||
"websiteUrl": "https://anonymous.modelcontextprotocol.io/embedded-mcp-guide",
|
"websiteUrl": "https://anonymous.modelcontextprotocol.io/embedded-mcp-guide",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"$comment": "This file is auto-generated from docs/reference/api/openapi.yaml. Do not edit manually. Run 'make generate-schema' to update.",
|
"$comment": "This file is auto-generated from docs/reference/api/openapi.yaml. Do not edit manually. Run 'make generate-schema' to update.",
|
||||||
"$id": "https://static.modelcontextprotocol.io/schemas/2025-10-11/server.schema.json",
|
"$id": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
||||||
"$ref": "#/definitions/ServerDetail",
|
"$ref": "#/definitions/ServerDetail",
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
"definitions": {
|
"definitions": {
|
||||||
|
|
@ -373,7 +373,7 @@
|
||||||
"properties": {
|
"properties": {
|
||||||
"$schema": {
|
"$schema": {
|
||||||
"description": "JSON Schema URI for this server.json format",
|
"description": "JSON Schema URI for this server.json format",
|
||||||
"example": "https://static.modelcontextprotocol.io/schemas/2025-10-11/server.schema.json",
|
"example": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
||||||
"format": "uri",
|
"format": "uri",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -27,14 +27,13 @@ func ValidateMCPB(ctx context.Context, pkg model.Package, _ string) error {
|
||||||
return ErrMissingIdentifierForMCPB
|
return ErrMissingIdentifierForMCPB
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate that old format fields are not present
|
// Validate that registryBaseUrl is not present
|
||||||
// MCPB packages use URL in identifier, version is embedded in the URL
|
// MCPB packages use full download URLs in identifier
|
||||||
if pkg.Version != "" {
|
|
||||||
return fmt.Errorf("MCPB packages must not have 'version' field - version should be embedded in the download URL")
|
|
||||||
}
|
|
||||||
if pkg.RegistryBaseURL != "" {
|
if pkg.RegistryBaseURL != "" {
|
||||||
return fmt.Errorf("MCPB packages must not have 'registryBaseUrl' field - use the full download URL in 'identifier' instead")
|
return fmt.Errorf("MCPB packages must not have 'registryBaseUrl' field - use the full download URL in 'identifier' instead")
|
||||||
}
|
}
|
||||||
|
// Note: version field is optional for MCPB packages
|
||||||
|
// It can be included for clarity or omitted if the version is embedded in the download URL
|
||||||
|
|
||||||
err := validateMCPBUrl(pkg.Identifier)
|
err := validateMCPBUrl(pkg.Identifier)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -120,67 +120,68 @@ func TestValidateMCPB(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestValidateMCPB_RejectsExtraFields(t *testing.T) {
|
func TestValidateMCPB_OptionalFields(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
pkg model.Package
|
pkg model.Package
|
||||||
|
expectError bool
|
||||||
errorMessage string
|
errorMessage string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "MCPB package with version field should be rejected",
|
name: "MCPB package with optional version field should pass",
|
||||||
pkg: model.Package{
|
pkg: model.Package{
|
||||||
RegistryType: model.RegistryTypeMCPB,
|
RegistryType: model.RegistryTypeMCPB,
|
||||||
Identifier: "https://github.com/example/repo/releases/download/v1.0.0/server.mcpb",
|
Identifier: "https://github.com/domdomegg/airtable-mcp-server/releases/download/v1.7.2/airtable-mcp-server.mcpb",
|
||||||
Version: "1.0.0",
|
Version: "1.7.2",
|
||||||
FileSHA256: "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce",
|
FileSHA256: "8220de07a08ebe908f04da139ea03dbfe29758141347e945da60535fb7bcca20",
|
||||||
},
|
},
|
||||||
errorMessage: "MCPB packages must not have 'version' field",
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "MCPB package without version field should pass",
|
||||||
|
pkg: model.Package{
|
||||||
|
RegistryType: model.RegistryTypeMCPB,
|
||||||
|
Identifier: "https://github.com/domdomegg/airtable-mcp-server/releases/download/v1.7.2/airtable-mcp-server.mcpb",
|
||||||
|
FileSHA256: "8220de07a08ebe908f04da139ea03dbfe29758141347e945da60535fb7bcca20",
|
||||||
|
},
|
||||||
|
expectError: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "MCPB package with registryBaseUrl should be rejected",
|
name: "MCPB package with registryBaseUrl should be rejected",
|
||||||
pkg: model.Package{
|
pkg: model.Package{
|
||||||
RegistryType: model.RegistryTypeMCPB,
|
RegistryType: model.RegistryTypeMCPB,
|
||||||
Identifier: "https://github.com/example/repo/releases/download/v1.0.0/server.mcpb",
|
Identifier: "https://github.com/domdomegg/airtable-mcp-server/releases/download/v1.7.2/airtable-mcp-server.mcpb",
|
||||||
RegistryBaseURL: "https://github.com",
|
RegistryBaseURL: "https://github.com",
|
||||||
FileSHA256: "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce",
|
FileSHA256: "8220de07a08ebe908f04da139ea03dbfe29758141347e945da60535fb7bcca20",
|
||||||
},
|
},
|
||||||
|
expectError: true,
|
||||||
errorMessage: "MCPB packages must not have 'registryBaseUrl' field",
|
errorMessage: "MCPB packages must not have 'registryBaseUrl' field",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "MCPB package with both extra fields should fail on version first",
|
name: "MCPB package with both version and registryBaseUrl should fail on registryBaseUrl",
|
||||||
pkg: model.Package{
|
pkg: model.Package{
|
||||||
RegistryType: model.RegistryTypeMCPB,
|
RegistryType: model.RegistryTypeMCPB,
|
||||||
Identifier: "https://github.com/example/repo/releases/download/v1.0.0/server.mcpb",
|
Identifier: "https://github.com/domdomegg/airtable-mcp-server/releases/download/v1.7.2/airtable-mcp-server.mcpb",
|
||||||
Version: "1.0.0",
|
Version: "1.7.2",
|
||||||
RegistryBaseURL: "https://github.com",
|
RegistryBaseURL: "https://github.com",
|
||||||
FileSHA256: "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce",
|
FileSHA256: "8220de07a08ebe908f04da139ea03dbfe29758141347e945da60535fb7bcca20",
|
||||||
},
|
},
|
||||||
errorMessage: "MCPB packages must not have 'version' field",
|
expectError: true,
|
||||||
},
|
errorMessage: "MCPB packages must not have 'registryBaseUrl' field",
|
||||||
{
|
|
||||||
name: "MCPB package with canonical format should pass extra field validation",
|
|
||||||
pkg: model.Package{
|
|
||||||
RegistryType: model.RegistryTypeMCPB,
|
|
||||||
Identifier: "https://github.com/domdomegg/airtable-mcp-server/releases/download/v1.7.2/airtable-mcp-server.mcpb",
|
|
||||||
FileSHA256: "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce",
|
|
||||||
},
|
|
||||||
errorMessage: "", // Should pass extra field check (will succeed as this is a real package)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
err := registries.ValidateMCPB(ctx, tt.pkg, "com.example/test")
|
err := registries.ValidateMCPB(ctx, tt.pkg, "io.github.domdomegg/airtable-mcp-server")
|
||||||
|
|
||||||
if tt.errorMessage != "" {
|
if tt.expectError {
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.Error(), tt.errorMessage)
|
assert.Contains(t, err.Error(), tt.errorMessage)
|
||||||
} else if err != nil {
|
} else {
|
||||||
// Should not fail with extra field error (may pass completely for real package)
|
assert.NoError(t, err)
|
||||||
assert.NotContains(t, err.Error(), "must not have 'version'")
|
|
||||||
assert.NotContains(t, err.Error(), "must not have 'registryBaseUrl'")
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ func TestValidate(t *testing.T) {
|
||||||
expectedError: "schema version https://static.modelcontextprotocol.io/schemas/2025-01-27/server.schema.json is not supported",
|
expectedError: "schema version https://static.modelcontextprotocol.io/schemas/2025-01-27/server.schema.json is not supported",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Schema version accepts current schema (2025-09-29)",
|
name: "Schema version accepts current schema (2025-10-17)",
|
||||||
serverDetail: apiv0.ServerJSON{
|
serverDetail: apiv0.ServerJSON{
|
||||||
Schema: model.CurrentSchemaURL,
|
Schema: model.CurrentSchemaURL,
|
||||||
Name: "com.example/test-server",
|
Name: "com.example/test-server",
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ type ServerMeta struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServerJSON struct {
|
type ServerJSON struct {
|
||||||
Schema string `json:"$schema" required:"true" minLength:"1" format:"uri" doc:"JSON Schema URI for this server.json format" example:"https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json"`
|
Schema string `json:"$schema" required:"true" minLength:"1" format:"uri" doc:"JSON Schema URI for this server.json format" example:"https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json"`
|
||||||
Name string `json:"name" minLength:"3" maxLength:"200" pattern:"^[a-zA-Z0-9.-]+/[a-zA-Z0-9._-]+$" doc:"Server name in reverse-DNS format. Must contain exactly one forward slash separating namespace from server name." example:"io.github.user/weather"`
|
Name string `json:"name" minLength:"3" maxLength:"200" pattern:"^[a-zA-Z0-9.-]+/[a-zA-Z0-9._-]+$" doc:"Server name in reverse-DNS format. Must contain exactly one forward slash separating namespace from server name." example:"io.github.user/weather"`
|
||||||
Description string `json:"description" minLength:"1" maxLength:"100" doc:"Clear human-readable explanation of server functionality." example:"MCP server providing weather data and forecasts via OpenWeatherMap API"`
|
Description string `json:"description" minLength:"1" maxLength:"100" doc:"Clear human-readable explanation of server functionality." example:"MCP server providing weather data and forecasts via OpenWeatherMap API"`
|
||||||
Title string `json:"title,omitempty" minLength:"1" maxLength:"100" doc:"Optional human-readable title or display name for the MCP server." example:"Weather API"`
|
Title string `json:"title,omitempty" minLength:"1" maxLength:"100" doc:"Optional human-readable title or display name for the MCP server." example:"Weather API"`
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ const (
|
||||||
// Schema versions
|
// Schema versions
|
||||||
const (
|
const (
|
||||||
// CurrentSchemaVersion is the current supported schema version date
|
// CurrentSchemaVersion is the current supported schema version date
|
||||||
CurrentSchemaVersion = "2025-09-29"
|
CurrentSchemaVersion = "2025-10-17"
|
||||||
// CurrentSchemaURL is the full URL to the current schema
|
// CurrentSchemaURL is the full URL to the current schema
|
||||||
CurrentSchemaURL = "https://static.modelcontextprotocol.io/schemas/" + CurrentSchemaVersion + "/server.schema.json"
|
CurrentSchemaURL = "https://static.modelcontextprotocol.io/schemas/" + CurrentSchemaVersion + "/server.schema.json"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ type Transport struct {
|
||||||
// - PyPI: RegistryType, Identifier (package name), Version, RegistryBaseURL (optional)
|
// - PyPI: RegistryType, Identifier (package name), Version, RegistryBaseURL (optional)
|
||||||
// - NuGet: RegistryType, Identifier (package ID), Version, RegistryBaseURL (optional)
|
// - NuGet: RegistryType, Identifier (package ID), Version, RegistryBaseURL (optional)
|
||||||
// - OCI: RegistryType, Identifier (full image reference like "ghcr.io/owner/repo:tag")
|
// - OCI: RegistryType, Identifier (full image reference like "ghcr.io/owner/repo:tag")
|
||||||
// - MCPB: RegistryType, Identifier (download URL), FileSHA256 (required)
|
// - MCPB: RegistryType, Identifier (download URL), Version (optional), FileSHA256 (required)
|
||||||
type Package struct {
|
type Package struct {
|
||||||
// RegistryType indicates how to download packages (e.g., "npm", "pypi", "oci", "nuget", "mcpb")
|
// RegistryType indicates how to download packages (e.g., "npm", "pypi", "oci", "nuget", "mcpb")
|
||||||
RegistryType string `json:"registryType" minLength:"1" doc:"Registry type indicating how to download packages (e.g., 'npm', 'pypi', 'oci', 'nuget', 'mcpb')" example:"npm"`
|
RegistryType string `json:"registryType" minLength:"1" doc:"Registry type indicating how to download packages (e.g., 'npm', 'pypi', 'oci', 'nuget', 'mcpb')" example:"npm"`
|
||||||
|
|
@ -31,7 +31,7 @@ type Package struct {
|
||||||
// - For OCI: full image reference (e.g., "ghcr.io/owner/repo:v1.0.0")
|
// - For OCI: full image reference (e.g., "ghcr.io/owner/repo:v1.0.0")
|
||||||
// - For MCPB: direct download URL
|
// - For MCPB: direct download URL
|
||||||
Identifier string `json:"identifier" minLength:"1" doc:"Package identifier - either a package name (for registries) or URL (for direct downloads)" example:"@modelcontextprotocol/server-brave-search"`
|
Identifier string `json:"identifier" minLength:"1" doc:"Package identifier - either a package name (for registries) or URL (for direct downloads)" example:"@modelcontextprotocol/server-brave-search"`
|
||||||
// Version is the package version (used by npm, pypi, nuget; not used by oci, mcpb where version is in the identifier)
|
// Version is the package version (required for npm, pypi, nuget; optional for mcpb; not used by oci where version is in the identifier)
|
||||||
Version string `json:"version,omitempty" minLength:"1" doc:"Package version. Must be a specific version. Version ranges are rejected (e.g., '^1.2.3', '~1.2.3', '>=1.2.3', '1.x', '1.*')." example:"1.0.2"`
|
Version string `json:"version,omitempty" minLength:"1" doc:"Package version. Must be a specific version. Version ranges are rejected (e.g., '^1.2.3', '~1.2.3', '>=1.2.3', '1.x', '1.*')." example:"1.0.2"`
|
||||||
// FileSHA256 is the SHA-256 hash for integrity verification (required for mcpb, optional for others)
|
// FileSHA256 is the SHA-256 hash for integrity verification (required for mcpb, optional for others)
|
||||||
FileSHA256 string `json:"fileSha256,omitempty" pattern:"^[a-f0-9]{64}$" doc:"SHA-256 hash of the package file for integrity verification. Required for MCPB packages and optional for other package types. Authors are responsible for generating correct SHA-256 hashes when creating server.json. If present, MCP clients must validate the downloaded file matches the hash before running packages to ensure file integrity." example:"fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce"`
|
FileSHA256 string `json:"fileSha256,omitempty" pattern:"^[a-f0-9]{64}$" doc:"SHA-256 hash of the package file for integrity verification. Required for MCPB packages and optional for other package types. Authors are responsible for generating correct SHA-256 hashes when creating server.json. If present, MCP clients must validate the downloaded file matches the hash before running packages to ensure file integrity." example:"fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce"`
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,8 @@ cleanup() {
|
||||||
|
|
||||||
go build -o ./bin/publisher ./cmd/publisher
|
go build -o ./bin/publisher ./cmd/publisher
|
||||||
|
|
||||||
docker build -t registry .
|
# Build the registry image with both tags, forcing a rebuild to ensure latest code
|
||||||
|
docker build --no-cache -t registry -t modelcontextprotocol/registry:dev .
|
||||||
|
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue