Introduce serverId, rename id to versionId (#465)

Wielded Claude Code to do this; across many iterations and have reviewed
all the code + largely rewritten the PR commentary here.

This PR implements the server ID consistency changes from
[#396](https://github.com/modelcontextprotocol/registry/issues/396). Our
goal is to:
- Rename `id` to `versionId` to make it clear that it is the ID for a
specific version, not a server
- Introduce `serverId` as a net new concept. This will be 1:1 with
`name`. While not strictly necessary (we could just use name as a PK),
the convention of having a UUID as an ID on our objects is just a
helpful subjective architecture decision. Plus also leaves open the door
to reconsidering the "should we allow renames" question.

## Key Changes

### 🗄️ Database Schema Changes
- **Renamed**: `id` column → `version_id` (unique identifier for each
server version)

### 📋 Metadata Structure Updates
- **JSON Schema**: Updated
`_meta.io.modelcontextprotocol.registry/official` to include:
  - `serverId`: Consistent ID across all versions of a server
  - `versionId`: Unique ID for this specific version (renamed from `id`)

### 🔧 API Endpoint Updates
- **New endpoint structure**: Changed from version-ID-based to
server-ID-based URLs:
  - `GET /v0/servers/{server_id}` - Get latest version of server
  - `GET /v0/servers/{server_id}?version=X.X.X` - Get specific version  
- `GET /v0/servers/{server_id}/versions` - List all versions of a server
  - `PUT /v0/servers/{server_id}?version=X.X.X` - Edit specific version
- **Consistent URL patterns**: All server endpoints now use `server_id`
as the primary identifier
 
### Database Migration Strategy
The migration (`005_add_server_id_rename_version_id.sql`):
1. Adds new `version_id` column  
2. Generates deterministic UUIDs for servers with the same name
3. Updates JSON metadata to add `serverId` and rename `id` to
`versionId`
4. Copies existing `id` values to `version_id` column
5. Drops old `id` column and makes `version_id` the new primary key

## Testing

Besides of course automated tests passing, I:
1. Set up my local db with the pre-005-migration state, imported all the
production data
2. Ran the migration
3. Spot checked some diffs of the data that resulted --

```json
// before
{
  "name": "io.github.ruvnet/ruv-swarm",
  "_meta": {
    "io.modelcontextprotocol.registry/official": {
      "id": "0a9cda7f-2b73-4839-844b-6582f335de42",
      "is_latest": false,
      "updated_at": "2025-09-10T17:21:34.92137012Z",
      "published_at": "2025-09-10T17:15:32.69690433Z"
    }
  },
  "version": "1.0.18",
  "repository": {
    "url": "",
    "source": ""
  },
  "description": "Neural network swarm orchestration with WebAssembly acceleration and MCP integration"
}

// after
{
  "name": "io.github.ruvnet/ruv-swarm",
  "_meta": {
    "io.modelcontextprotocol.registry/official": {
      "serverId": "e8538952-96af-4a33-9e31-7fbed012521e",
      "is_latest": false,
      "versionId": "0a9cda7f-2b73-4839-844b-6582f335de42",
      "updated_at": "2025-09-10T17:21:34.92137012Z",
      "published_at": "2025-09-10T17:15:32.69690433Z"
    }
  },
  "version": "1.0.18",
  "repository": {
    "url": "",
    "source": ""
  },
  "description": "Neural network swarm orchestration with WebAssembly acceleration and MCP integration"
}

```

Servers that have multiple versions stayed consistent in their
`serverId`:

```json
// First version
{
  "name": "app.getdialer/dialer",
  "_meta": {
    "io.modelcontextprotocol.registry/official": {
      "serverId": "80f08bdb-f3e4-4f19-b072-1676ae49b663",
      "is_latest": false,
      "versionId": "04f25b17-331d-48d7-b08b-37baece208a7",
      "updated_at": "2025-09-09T00:16:49.162620072Z",
      "published_at": "2025-09-08T23:47:09.452137971Z"
    }
  },
  "status": "active",
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
  "remotes": [
    {
      "url": "https://getdialer.app/mcp",
      "type": "streamable-http"
    },
    {
      "url": "https://getdialer.app/sse",
      "type": "sse"
    }
  ],
  "version": "1.0.0",
  "repository": {
    "url": "",
    "source": ""
  },
  "description": "An MCP server that provides your you make outbound phone calls using your own phone number"
}

// Second version
{
  "name": "app.getdialer/dialer",
  "_meta": {
    "io.modelcontextprotocol.registry/official": {
      "serverId": "80f08bdb-f3e4-4f19-b072-1676ae49b663",
      "is_latest": true,
      "versionId": "faa26cb9-3aad-415e-aece-f715fff5fe5e",
      "updated_at": "2025-09-09T00:16:49.107842808Z",
      "published_at": "2025-09-09T00:16:49.107842808Z"
    }
  },
  "status": "active",
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
  "remotes": [
    {
      "url": "https://getdialer.app/mcp",
      "type": "streamable-http"
    },
    {
      "url": "https://getdialer.app/sse",
      "type": "sse"
    }
  ],
  "version": "1.0.1",
  "repository": {
    "url": "",
    "source": ""
  },
  "description": "An MCP server that provides lets you make outbound phone calls using your own phone number"
}
```

And I also tried publishing a test server anew; looked good:

```bash
⏺ Bash(curl -s "http://localhost:8080/v0/servers/102dc4e4-6ca8-40e6-aa11-cd45763d3352" | jq '.')
  ⎿  {                                                                                             
       "$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
       "name": "io.modelcontextprotocol.anonymous/test-claude-migration-server",
       "description": "A test server to verify the new migration and API endpoints work correctly",
       "status": "active",
       "repository": {
         "url": "https://github.com/example/test-claude-migration-server",
         "source": "github",
         "id": "example/test-claude-migration-server"
       },
       "version": "1.0.0",
       "_meta": {
         "io.modelcontextprotocol.registry/official": {
           "serverId": "102dc4e4-6ca8-40e6-aa11-cd45763d3352",
           "versionId": "48c9cd55-7bc4-4ac2-896e-59e81eb653e4",
           "published_at": "2025-09-12T18:33:23.264203-07:00",
           "updated_at": "2025-09-12T18:33:23.264203-07:00",
           "is_latest": true
         }
       }
     }

⏺ Bash(curl -s "http://localhost:8080/v0/servers/102dc4e4-6ca8-40e6-aa11-cd45763d3352/versions" | jq '.')
  ⎿  {                                                                                                 
       "servers": [
         {
           "$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
           "name": "io.modelcontextprotocol.anonymous/test-claude-migration-server",
           "description": "A test server to verify the new migration and API endpoints work correctly",
           "status": "active",
           "repository": {
             "url": "https://github.com/example/test-claude-migration-server",
             "source": "github",
             "id": "example/test-claude-migration-server"
           },
           "version": "1.0.0",
           "_meta": {
             "io.modelcontextprotocol.registry/official": {
               "serverId": "102dc4e4-6ca8-40e6-aa11-cd45763d3352",
               "versionId": "48c9cd55-7bc4-4ac2-896e-59e81eb653e4",
               "published_at": "2025-09-12T18:33:23.264203-07:00",
               "updated_at": "2025-09-12T18:33:23.264203-07:00",
               "is_latest": true
             }
           }
         }
       ],
       "metadata": {
         "count": 1
       }
     }
```

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Preeti Dewani <preetidewani1990@gmail.com>
Co-authored-by: adam jones <adamj+git@anthropic.com>
Co-authored-by: Adam Jones <adamj@anthropic.com>
This commit is contained in:
Tadas Antanavicius 2025-09-21 08:00:09 -04:00 • committed by GitHub
commit 56e8b4879b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
43 changed files with 5185 additions and 3466 deletions

3
.gitignore vendored
View file

@ -1,5 +1,6 @@
build/
bin/
tmp/
.db
.env
@ -11,4 +12,4 @@ validate-schemas
coverage.out
coverage.html
deploy/infra/infra
registry
registry

View file

@ -301,7 +301,7 @@ func createServerJSON(
// Create server structure
return apiv0.ServerJSON{
Schema: "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
Schema: "https://static.modelcontextprotocol.io/schemas/2025-09-16/server.schema.json",
Name: name,
Description: description,
Status: model.StatusActive,

View file

@ -37,6 +37,17 @@ func PublishCommand(args []string) error {
return fmt.Errorf("invalid server.json: %w", err)
}
// Check for deprecated schema and recommend migration
// Allow empty schema (will use default) but reject old schemas
if serverJSON.Schema != "" && !strings.Contains(serverJSON.Schema, "2025-09-16") {
return fmt.Errorf(`deprecated schema detected :%s.
Migrate to the current schema format for new servers.
📋 Migration checklist: https://github.com/modelcontextprotocol/registry/blob/main/docs/reference/server-json/CHANGELOG.md#migration-checklist-for-publishers
📖 Full changelog with examples: https://github.com/modelcontextprotocol/registry/blob/main/docs/reference/server-json/CHANGELOG.md`, serverJSON.Schema)
}
// Load saved token
homeDir, err := os.UserHomeDir()
if err != nil {
@ -71,8 +82,8 @@ func PublishCommand(args []string) error {
}
_, _ = fmt.Fprintln(os.Stdout, "✓ Successfully published")
if serverID := response.GetID(); serverID != "" {
_, _ = fmt.Fprintf(os.Stdout, "✓ Server Id %s", serverID)
if serverID := response.GetServerID(); serverID != "" {
_, _ = fmt.Fprintf(os.Stdout, "✓ Server Id %s version %s\n", serverID, response.Version)
}
return nil

View file

@ -0,0 +1,134 @@
package commands_test
import (
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"
"github.com/modelcontextprotocol/registry/cmd/publisher/commands"
apiv0 "github.com/modelcontextprotocol/registry/pkg/api/v0"
"github.com/modelcontextprotocol/registry/pkg/model"
)
func TestPublishCommand_DeprecatedSchema(t *testing.T) {
// Create a temporary directory for test files
tempDir, err := os.MkdirTemp("", "mcp-publisher-test")
if err != nil {
t.Fatalf("Failed to create temp dir: %v", err)
}
defer os.RemoveAll(tempDir)
// Change to temp directory
originalDir, err := os.Getwd()
if err != nil {
t.Fatalf("Failed to get current dir: %v", err)
}
defer func() { _ = os.Chdir(originalDir) }()
if err := os.Chdir(tempDir); err != nil {
t.Fatalf("Failed to change to temp dir: %v", err)
}
tests := []struct {
name string
schema string
expectError bool
errorSubstr string
}{
{
name: "deprecated 2025-07-09 schema should show warning",
schema: "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
expectError: true,
errorSubstr: "deprecated schema detected",
},
{
name: "current 2025-09-16 schema should pass validation",
schema: "https://static.modelcontextprotocol.io/schemas/2025-09-16/server.schema.json",
expectError: false,
},
{
name: "empty schema should pass validation",
schema: "",
expectError: false,
},
{
name: "custom schema without 2025-07-09 should pass validation",
schema: "https://example.com/custom.schema.json",
expectError: true,
errorSubstr: "deprecated schema detected",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Create test server.json with specific schema
serverJSON := apiv0.ServerJSON{
Schema: tt.schema,
Name: "com.example/test-server",
Description: "A test server",
Version: "1.0.0",
Status: model.StatusActive,
Repository: model.Repository{
URL: "https://github.com/example/test",
Source: "github",
},
Packages: []model.Package{
{
RegistryType: model.RegistryTypeNPM,
RegistryBaseURL: model.RegistryURLNPM,
Identifier: "@example/test-server",
Version: "1.0.0",
Transport: model.Transport{
Type: model.TransportTypeStdio,
},
},
},
}
jsonData, err := json.MarshalIndent(serverJSON, "", " ")
if err != nil {
t.Fatalf("Failed to marshal test JSON: %v", err)
}
// Write server.json to temp directory
serverFile := filepath.Join(tempDir, "server.json")
if err := os.WriteFile(serverFile, jsonData, 0o600); err != nil {
t.Fatalf("Failed to write server.json: %v", err)
}
err = commands.PublishCommand([]string{})
if tt.expectError {
if err == nil {
t.Errorf("Expected error for deprecated schema, but got none")
return
}
if !strings.Contains(err.Error(), tt.errorSubstr) {
t.Errorf("Expected error containing '%s', got: %v", tt.errorSubstr, err)
}
// Check that the error contains the migration links
if !strings.Contains(err.Error(), "Migration checklist:") {
t.Errorf("Expected error to contain migration checklist link")
}
if !strings.Contains(err.Error(), "Full changelog with examples:") {
t.Errorf("Expected error to contain changelog link")
}
} else {
// For non-deprecated schemas, we expect the command to fail at auth step, not schema validation
if err != nil && strings.Contains(err.Error(), "deprecated schema detected") {
t.Errorf("Unexpected deprecated schema error for schema '%s': %v", tt.schema, err)
}
// We expect auth errors for valid schemas since we don't have a token
if err != nil && !strings.Contains(err.Error(), "not authenticated") && !strings.Contains(err.Error(), "failed to read token") {
t.Logf("Expected auth error for valid schema, got: %v", err)
}
}
// Clean up for next test
os.Remove(serverFile)
})
}
}

File diff suppressed because it is too large Load diff

View file

@ -23,6 +23,7 @@ services:
- MCP_REGISTRY_OIDC_EXTRA_CLAIMS=${MCP_REGISTRY_OIDC_EXTRA_CLAIMS:-[{"hd":"modelcontextprotocol.io"}]}
- MCP_REGISTRY_OIDC_EDIT_PERMISSIONS=${MCP_REGISTRY_OIDC_EDIT_PERMISSIONS:-*}
- MCP_REGISTRY_OIDC_PUBLISH_PERMISSIONS=${MCP_REGISTRY_OIDC_PUBLISH_PERMISSIONS:-*}
- MCP_REGISTRY_ENABLE_REGISTRY_VALIDATION=${MCP_REGISTRY_ENABLE_REGISTRY_VALIDATION:-true}
ports:
- 8080:8080
restart: "unless-stopped"

View file

@ -58,14 +58,14 @@ If server authors expect to have multiple registry versions for the same package
### For Semantic Versions
The registry attempts to parse versions as semantic versions. If successful, it uses semantic version comparison rules to determine:
- Version ordering in lists
- Which version is marked as `is_latest`
- Which version is marked as `isLatest`
### For Non-Semantic Versions
If version parsing as semantic version fails:
- The registry will always mark the version as latest (overriding any previous version)
- Clients should fall back to using publish timestamp for ordering
**Important Note**: This behavior means that for servers with mixed semantic and non-semantic versions, the `is_latest` flag may not align with the total ordering. A non-semantic version published after semantic versions will be marked as latest, even if semantic versions are considered "higher" in the ordering.
**Important Note**: This behavior means that for servers with mixed semantic and non-semantic versions, the `isLatest` flag may not align with the total ordering. A non-semantic version published after semantic versions will be marked as latest, even if semantic versions are considered "higher" in the ordering.
## Implementation Details
@ -73,13 +73,13 @@ If version parsing as semantic version fails:
1. **Validation**: Versions are validated for uniqueness within a server name
2. **Parsing**: The registry attempts to parse each version as semantic version
3. **Comparison**: Uses semantic version rules when possible, falls back to timestamp
4. **Latest Flag**: The `is_latest` field is set based on the comparison results
4. **Latest Flag**: The `isLatest` field is set based on the comparison results
### Client Recommendations
Registry clients SHOULD:
1. Attempt to interpret versions as semantic versions when possible
2. Use the following ordering rules:
- If one version is marked as is_latest: it is later
- If one version is marked as isLatest: it is later
- If both versions are valid semver: use semver comparison
- If neither are valid semver: use publish timestamp
- If one is semver and one is not: semver version is considered higher
@ -145,7 +145,7 @@ Existing servers with non-semantic versions will continue to work without change
"version": "1.2.3",
"packages": [
{
"registry_type": "npm",
"registryType": "npm",
"identifier": "@myorg/server",
"version": "1.2.3"
}
@ -160,7 +160,7 @@ Existing servers with non-semantic versions will continue to work without change
"version": "1.2.3-1",
"packages": [
{
"registry_type": "npm",
"registryType": "npm",
"identifier": "@myorg/server",
"version": "1.2.3"
}
@ -196,12 +196,12 @@ Version strategy options:
"version": "1.3.0",
"packages": [
{
"registry_type": "npm",
"registryType": "npm",
"identifier": "@myorg/server",
"version": "1.3.0"
},
{
"registry_type": "oci",
"registryType": "oci",
"identifier": "myorg/server",
"version": "1.2.5"
}

View file

@ -16,31 +16,111 @@ This is a brief guide for admins and moderators managing content on the registry
./tools/admin/auth.sh
```
## Edit a Server
## Edit a Specific Server Version
Step 1: Download Server
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_ID="<server-uuid>"
export SERVER_ID="<server-uuid>" # This is the server ID (consistent across versions)
export VERSION="<version-string>" # e.g., "1.0.0" (optional, defaults to latest)
# Get specific version
curl -s "https://registry.modelcontextprotocol.io/v0/servers/${SERVER_ID}?version=${VERSION}" > server.json
# Or get latest version (omit version parameter)
curl -s "https://registry.modelcontextprotocol.io/v0/servers/${SERVER_ID}" > server.json
```
Step 2: Open `server.json` and make changes. You cannot change the server name.
### Step 2: Make Changes
Step 3: Push Changes
Open `server.json` and edit the specific version details. You cannot change the server name or version number.
### Step 3: Update Version
```bash
curl -X PUT "https://registry.modelcontextprotocol.io/v0/servers/${SERVER_ID}" \
# Update specific version
curl -X PUT "https://registry.modelcontextprotocol.io/v0/servers/${SERVER_ID}?version=${VERSION}" \
-H "Authorization: Bearer ${REGISTRY_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"server\": $(cat server.json)}"
-d "$(cat server.json)"
```
## Takedown a Server
## Edit an Entire Server (All Versions)
Use this when you need to apply changes across all versions of a server (e.g., mark entire server as deleted, apply content scrubbing).
### Step 1: List All Versions
```bash
export SERVER_ID="<server-uuid>" # This is the server ID (consistent across versions)
curl -s "https://registry.modelcontextprotocol.io/v0/servers/${SERVER_ID}/versions" > all_versions.json
```
### Step 2: Extract Version IDs
```bash
# Extract all version IDs from the server
jq -r '.servers[]._meta."io.modelcontextprotocol.registry/official".version_id' all_versions.json > version_ids.txt
```
### Step 3: Apply Changes to All Versions
```bash
# For each version, download, modify, and update
while read VERSION_ID; do
echo "Processing version: $VERSION_ID"
curl -s "https://registry.modelcontextprotocol.io/v0/servers/${VERSION_ID}" > temp_version.json
# Apply your changes here (e.g., set status to "deleted")
jq '.status = "deleted"' temp_version.json > modified_version.json
curl -X PUT "https://registry.modelcontextprotocol.io/v0/servers/${VERSION_ID}" \
-H "Authorization: Bearer ${REGISTRY_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"server\": $(cat modified_version.json)}"
done < version_ids.txt
# Clean up temporary files
rm temp_version.json modified_version.json version_ids.txt all_versions.json
```
## Quick Operations
### Get Latest Version of a Server
```bash
export SERVER_ID="<server-uuid>"
curl -s "https://registry.modelcontextprotocol.io/v0/servers/${SERVER_ID}" > latest_version.json
export VERSION_ID=$(jq -r '._meta."io.modelcontextprotocol.registry/official".version_id' latest_version.json)
echo "Latest version ID: $VERSION_ID"
```
### Takedown a Specific Version
```bash
export VERSION_ID="<version-uuid>"
./tools/admin/takedown.sh
```
This soft deletes the server. If you need to delete the content of a server (usually only where legally necessary), use the edit workflow above to scrub it all.
### Takedown an Entire Server (All Versions)
```bash
export SERVER_ID="<server-uuid>"
# Get all version IDs and takedown each one
curl -s "https://registry.modelcontextprotocol.io/v0/servers/${SERVER_ID}/versions" | \
jq -r '.servers[]._meta."io.modelcontextprotocol.registry/official".version_id' | \
while read VERSION_ID; do
echo "Taking down version: $VERSION_ID"
./tools/admin/takedown.sh "$VERSION_ID"
done
```
## Notes
- **Version-specific changes**: Only affect that particular version
- **Server-wide changes**: Must be applied to each version individually
- **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)

View file

@ -9,7 +9,9 @@ Integration patterns and best practices for building applications that consume M
**Authentication**: Not required for read-only access
- **`GET /v0/servers`** - List all servers with pagination
- **`GET /v0/servers/{id}`** - Get server details by UUID
- **`GET /v0/servers/{server_id}`** - Get latest version of server by server ID
- **`GET /v0/servers/{server_id}?version=X.X.X`** - Get specific version of server
- **`GET /v0/servers/{server_id}/versions`** - List all versions of a server
See the [interactive API documentation](https://registry.modelcontextprotocol.io/docs) for complete request/response schemas.
@ -18,7 +20,7 @@ See the [interactive API documentation](https://registry.modelcontextprotocol.io
## Building a subregistry
**Create enhanced registries** - ETL official registry data and add your own metadata like ratings, security scans, or compatibility info.
For now we recommend scraping the `GET /v0/servers` endpoint on some regular basis. In the future we might provide a filter for updated_at ([#291](https://github.com/modelcontextprotocol/registry/issues/291)) to get only recently changed servers.
For now we recommend scraping the `GET /v0/servers` endpoint on some regular basis. In the future we might provide a filter for updatedAt ([#291](https://github.com/modelcontextprotocol/registry/issues/291)) to get only recently changed servers.
Servers are generally immutable, except for the `status` field which can be updated to `deleted` (among other states). For these packages, we recommend you also update the status field to `deleted` or remove the package from your registry quickly. This is because this status generally indicates it has violated our permissive [moderation guidelines](../administration/moderation-guidelines.md), suggesting it is illegal, malware or spam.
@ -30,14 +32,14 @@ You can also add custom metadata to servers using the `_meta` field. For example
```json
{
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-16/server.schema.json",
"name": "io.github.yourname/weather-server",
"description": "MCP server for weather data access",
"status": "active",
"version": "1.0.0",
"packages": [
{
"registry_type": "npm",
"registryType": "npm",
"identifier": "weather-mcp-server",
"version": "1.0.0"
}

View file

@ -4,7 +4,7 @@ The MCP Registry project is a **metaregistry**, meaning that it hosts metadata f
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.registry_type` string enum in the [`server.json` schema](../../reference/server-json/server.schema.json). For example, this could be "npm" (for npmjs.com packages) or "pypi" (for PyPI packages).
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/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.
@ -34,13 +34,13 @@ These steps may evolve as additional validations or details are discovered and m
- 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/server.schema.json)
- Add your package registry name to the `registry_type` example array.
- Add your package registry base url to the `registry_base_url` example array.
- Add the single-shot CLI command name to the `runtime_hint` example value array.
- 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 `registry_type` enum value array.
- Add your package registry base url to the `registry_base_url` enum value array.
- Add the single-shot CLI command name to the `runtime_hint` example value array.
- 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:

View file

@ -92,13 +92,13 @@ This creates a `server.json` with auto-detected values. You'll see something lik
```json
{
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-16/server.schema.json",
"name": "io.github.yourname/your-server",
"description": "A description of your MCP server",
"version": "1.0.0",
"packages": [
{
"registry_type": "npm",
"registryType": "npm",
"identifier": "your-package-name",
"version": "1.0.0"
}
@ -151,7 +151,7 @@ Add an `mcpName` field to your `package.json`:
"name": "io.github.username/server-name",
"packages": [
{
"registry_type": "npm",
"registryType": "npm",
"identifier": "your-npm-package",
"version": "1.0.0"
}
@ -183,7 +183,7 @@ Add it to your README.md file (which becomes the package description on PyPI). T
"name": "io.github.username/server-name",
"packages": [
{
"registry_type": "pypi",
"registryType": "pypi",
"identifier": "your-pypi-package",
"version": "1.0.0"
}
@ -215,7 +215,7 @@ Add a README file to your NuGet package that includes the server name. This can
"name": "io.github.username/server-name",
"packages": [
{
"registry_type": "nuget",
"registryType": "nuget",
"identifier": "Your.NuGet.Package",
"version": "1.0.0"
}
@ -251,8 +251,8 @@ LABEL io.modelcontextprotocol.server.name="io.github.username/server-name"
"name": "io.github.username/server-name",
"packages": [
{
"registry_type": "oci",
"registry_base_url": "https://docker.io",
"registryType": "oci",
"registryBaseUrl": "https://docker.io",
"identifier": "yourusername/your-mcp-server",
"version": "1.0.0"
}
@ -266,8 +266,8 @@ LABEL io.modelcontextprotocol.server.name="io.github.username/server-name"
"name": "io.github.username/server-name",
"packages": [
{
"registry_type": "oci",
"registry_base_url": "https://ghcr.io",
"registryType": "oci",
"registryBaseUrl": "https://ghcr.io",
"identifier": "yourusername/your-mcp-server",
"version": "1.0.0"
}
@ -302,9 +302,9 @@ openssl dgst -sha256 server.mcpb
"name": "io.github.username/server-name",
"packages": [
{
"registry_type": "mcpb",
"registryType": "mcpb",
"identifier": "https://github.com/you/your-repo/releases/download/v1.0.0/server.mcpb",
"file_sha256": "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce"
"fileSha256": "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce"
}
]
}
@ -337,7 +337,7 @@ Add the `remotes` field to your `server.json` (can coexist with `packages`):
```json
{
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-16/server.schema.json",
"name": "com.yourcompany/api-server",
"description": "Cloud-hosted MCP server for API operations",
"version": "2.0.0",
@ -388,8 +388,8 @@ Configure headers that clients should send when connecting:
{
"name": "X-API-Key",
"description": "API key for authentication",
"is_required": true,
"is_secret": true
"isRequired": true,
"isSecret": true
}
]
}

View file

@ -0,0 +1,70 @@
# Registry API Changelog
Changes to the REST API endpoints and responses.
## 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.

View file

@ -15,7 +15,9 @@ The official registry has some more endpoints and restrictions on top of this. S
### Core Endpoints
- **`GET /v0/servers`** - List all servers with pagination
- **`GET /v0/servers/{id}`** - Get server details by UUID
- **`GET /v0/servers/{server_id}`** - Get latest version of server by server ID
- **`GET /v0/servers/{server_id}?version=X.X.X`** - Get specific version of server
- **`GET /v0/servers/{server_id}/versions`** - List all versions of a server
- **`POST /v0/publish`** - Publish new server (optional, registry-specific authentication)
### Authentication

View file

@ -58,4 +58,4 @@ Example: `GET /v0/servers?search=filesystem&updated_since=2025-08-01T00:00:00Z&v
#### Admin endpoints
- GET `/metrics` - Prometheus metrics endpoint
- GET `/v0/health` - Basic health check endpoint
- PUT `/v0/servers/{id}` - Edit existing server
- PUT `/v0/servers/{server_id}?version=X.X.X` - Edit specific server version (version parameter required)

View file

@ -1,9 +1,9 @@
openapi: 3.1.0
jsonSchemaDialect: "https://json-schema.org/draft/2020-12/schema"
$id: https://modelcontextprotocol.io/schemas/draft/2025-07-09/server-registry-openapi
$id: https://modelcontextprotocol.io/schemas/draft/2025-09-16/server-registry-openapi
info:
title: MCP Server Registry API
version: "2025-07-09"
version: "2025-09-16"
summary: API for discovering and accessing MCP server metadata
description: |
Specification for a theoretical REST API that serves up metadata about MCP servers.
@ -36,21 +36,21 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ServerList'
/v0/servers/{id}:
/v0/servers/{server_id}:
get:
summary: Get MCP server details
description: Returns detailed information about a specific MCP server
description: Returns detailed information about a specific MCP server. Returns latest version by default, or specific version if version query parameter is provided.
parameters:
- name: id
- name: server_id
in: path
required: true
description: Unique ID of the server
description: Unique server ID (consistent across all versions)
schema:
type: string
format: uuid
- name: version
in: query
description: Desired MCP server version
description: Specific version to retrieve (e.g., "1.0.0"). If not provided, returns latest version.
schema:
type: string
responses:
@ -70,6 +70,35 @@ paths:
error:
type: string
example: "Server not found"
/v0/servers/{server_id}/versions:
get:
summary: List all versions of an MCP server
description: Returns all available versions for a specific MCP server, ordered by publication date (newest first)
parameters:
- name: server_id
in: path
required: true
description: Unique server ID (consistent across all versions)
schema:
type: string
format: uuid
responses:
'200':
description: A list of all versions for the server
content:
application/json:
schema:
$ref: '#/components/schemas/ServerList'
'404':
description: Server not found
content:
application/json:
schema:
type: object
properties:
error:
type: string
example: "Server not found"
/v0/publish:
post:
summary: Publish MCP server (Optional)
@ -99,8 +128,8 @@ paths:
source: "github"
version: "1.0.2"
packages:
- registry_type: "npm"
registry_base_url: "https://registry.npmjs.org"
- registryType: "npm"
registryBaseUrl: "https://registry.npmjs.org"
identifier: "@modelcontextprotocol/server-filesystem"
version: "1.0.2"
_meta:
@ -121,8 +150,8 @@ paths:
source: "github"
version: "1.0.0"
packages:
- registry_type: "npm"
registry_base_url: "https://registry.npmjs.org"
- registryType: "npm"
registryBaseUrl: "https://registry.npmjs.org"
identifier: "@example/mcp-demo-server"
version: "1.0.0"
_meta:
@ -233,7 +262,7 @@ components:
type: string
example: "1.0.2"
description: "Version string for this server. SHOULD follow semantic versioning (e.g., '1.0.2', '2.1.0-alpha'). Equivalent of Implementation.version in MCP specification."
website_url:
websiteUrl:
type: string
format: uri
description: "Optional URL to the server's homepage, documentation, or project website. This provides a central link for users to learn more about the server. Particularly useful when the server has custom installation instructions or setup requirements."
@ -269,7 +298,7 @@ components:
Package:
type: object
properties:
registry_type:
registryType:
type: string
description: Registry type indicating how to download packages (e.g., 'npm', 'pypi', 'oci', 'nuget', 'mcpb')
examples:
@ -278,7 +307,7 @@ components:
- "oci"
- "nuget"
- "mcpb"
registry_base_url:
registryBaseUrl:
type: string
format: uri
description: Base URL of the package registry
@ -299,25 +328,25 @@ components:
type: string
description: Package version
example: "1.0.2"
file_sha256:
fileSha256:
type: string
description: SHA-256 hash of the package file for integrity verification.
example: "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce"
runtime_hint:
runtimeHint:
type: string
description: A hint to help clients determine the appropriate runtime for the package. This field should be provided when `runtime_arguments` are present.
description: A hint to help clients determine the appropriate runtime for the package. This field should be provided when `runtimeArguments` are present.
examples: [npx, uvx, dnx]
runtime_arguments:
runtimeArguments:
type: array
description: A list of arguments to be passed to the package's runtime command (such as docker or npx). The `runtime_hint` field should be provided when `runtime_arguments` are present.
description: A list of arguments to be passed to the package's runtime command (such as docker or npx). The `runtimeHint` field should be provided when `runtimeArguments` are present.
items:
$ref: '#/components/schemas/Argument'
package_arguments:
packageArguments:
type: array
description: A list of arguments to be passed to the package's binary.
items:
$ref: '#/components/schemas/Argument'
environment_variables:
environmentVariables:
type: array
description: A mapping of environment variables to be set when running the package.
items:
@ -329,7 +358,7 @@ components:
description:
description: A description of the input, which clients can use to provide context to the user.
type: string
is_required:
isRequired:
type: boolean
default: false
format:
@ -346,7 +375,7 @@ components:
The default value for the input. If this is not set, the user may be prompted to provide a value. If a value is set, it should not be configurable by end users.
Identifiers wrapped in `{curly_braces}` will be replaced with the corresponding properties from the input `variables` map. If an identifier in braces is not found in `variables`, or if `variables` is not provided, the `{curly_braces}` substring should remain unchanged.
is_secret:
isSecret:
type: boolean
description: Indicates whether the input is a secret value (e.g., password, token). If true, clients should handle the value securely.
default: false
@ -383,11 +412,11 @@ components:
type: string
enum: [positional]
example: "positional"
value_hint:
valueHint:
type: string
description: An identifier-like hint for the value. This is not part of the command line, but can be used by client configuration and to provide hints to users.
example: file_path
is_repeated:
isRepeated:
type: boolean
description: Whether the argument can be repeated multiple times in the command line.
default: false
@ -395,7 +424,7 @@ components:
- required:
- value
- required:
- value_hint
- valueHint
NamedArgument:
description: A command-line `--flag={value}`.
@ -414,7 +443,7 @@ components:
type: string
description: The flag name, including any leading dashes.
example: "--port"
is_repeated:
isRepeated:
type: boolean
description: Whether the argument can be repeated multiple times.
default: false
@ -468,7 +497,7 @@ components:
type: string
format: uri
description: JSON Schema URI for this server.json format
example: "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json"
example: "https://static.modelcontextprotocol.io/schemas/2025-09-16/server.schema.json"
packages:
type: array
items:
@ -496,16 +525,22 @@ components:
type: object
description: Registry-specific metadata managed by the MCP registry system
required:
- id
- serverId
- versionId
- published_at
- updated_at
- is_latest
properties:
id:
serverId:
type: string
format: uuid
description: Unique registry identifier for this server entry
description: Consistent ID across all versions of a server
example: "550e8400-e29b-41d4-a716-446655440000"
versionId:
type: string
format: uuid
description: Unique ID for this specific version
example: "773f9b2e-1a47-4c8d-b5e6-2f8d9c4a7b3e"
published_at:
type: string
format: date-time

View file

@ -43,7 +43,7 @@ mcp-publisher init [options]
"version": "1.0.0",
"packages": [
{
"registry_type": "npm",
"registryType": "npm",
"identifier": "detected-package-name",
"version": "1.0.0"
}

View file

@ -0,0 +1,126 @@
# Server JSON Schema Changelog
Changes to the server.json schema and format.
## 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-16/server.schema.json
### Changed
- Schema version: `2025-07-09` → `2025-09-16`
## 2025-07-09
Initial release of the server.json schema.

View file

@ -8,7 +8,7 @@ Also see:
## Browse the Complete Schema
**📋 View the full specification interactively**: Open [server.schema.json](./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-07-09%2Fserver.schema.json).
**📋 View the full specification interactively**: Open [server.schema.json](./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-16%2Fserver.schema.json).
The schema contains all field definitions, validation rules, examples, and detailed descriptions.
@ -22,11 +22,11 @@ The official registry has some more restrictions on top of this. See the [offici
```json
{
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-16/server.schema.json",
"name": "io.modelcontextprotocol.anonymous/brave-search",
"description": "MCP server for Brave Search API integration",
"status": "active",
"website_url": "https://anonymous.modelcontextprotocol.io/examples",
"websiteUrl": "https://anonymous.modelcontextprotocol.io/examples",
"repository": {
"url": "https://github.com/modelcontextprotocol/servers",
"source": "github"
@ -34,19 +34,19 @@ The official registry has some more restrictions on top of this. See the [offici
"version": "1.0.2",
"packages": [
{
"registry_type": "npm",
"registry_base_url": "https://registry.npmjs.org",
"registryType": "npm",
"registryBaseUrl": "https://registry.npmjs.org",
"identifier": "@modelcontextprotocol/server-brave-search",
"version": "1.0.2",
"transport": {
"type": "stdio"
},
"environment_variables": [
"environmentVariables": [
{
"name": "BRAVE_API_KEY",
"description": "Brave Search API Key",
"is_required": true,
"is_secret": true
"isRequired": true,
"isSecret": true
}
]
}
@ -69,7 +69,7 @@ For MCP servers located within a subdirectory of a larger repository (monorepo s
```json
{
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-16/server.schema.json",
"name": "io.modelcontextprotocol/everything",
"description": "MCP server that exercises all the features of the MCP protocol",
"status": "active",
@ -81,8 +81,8 @@ For MCP servers located within a subdirectory of a larger repository (monorepo s
"version": "0.6.2",
"packages": [
{
"registry_type": "npm",
"registry_base_url": "https://registry.npmjs.org",
"registryType": "npm",
"registryBaseUrl": "https://registry.npmjs.org",
"identifier": "@modelcontextprotocol/everything",
"version": "0.6.2",
"transport": {
@ -113,14 +113,14 @@ Suppose your MCP server application requires a `mcp start` CLI arguments to star
"version": "0.4.0-beta",
"packages": [
{
"registry_type": "nuget",
"registry_base_url": "https://api.nuget.org",
"registryType": "nuget",
"registryBaseUrl": "https://api.nuget.org",
"identifier": "Knapcode.SampleMcpServer",
"version": "0.4.0-beta",
"transport": {
"type": "stdio"
},
"package_arguments": [
"packageArguments": [
{
"type": "positional",
"value": "mcp"
@ -145,13 +145,13 @@ Suppose your MCP server application requires a `mcp start` CLI arguments to star
}
```
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 `package_arguments` are provided).
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
```json
{
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-16/server.schema.json",
"name": "io.github.modelcontextprotocol/filesystem",
"description": "Node.js server implementing Model Context Protocol (MCP) for filesystem operations.",
"status": "active",
@ -163,24 +163,24 @@ This will essentially instruct the MCP client to execute `dnx Knapcode.SampleMcp
"version": "1.0.2",
"packages": [
{
"registry_type": "npm",
"registry_base_url": "https://registry.npmjs.org",
"registryType": "npm",
"registryBaseUrl": "https://registry.npmjs.org",
"identifier": "@modelcontextprotocol/server-filesystem",
"version": "1.0.2",
"transport": {
"type": "stdio"
},
"package_arguments": [
"packageArguments": [
{
"type": "positional",
"value_hint": "target_dir",
"valueHint": "target_dir",
"description": "Path to access",
"default": "/Users/username/Desktop",
"is_required": true,
"is_repeated": true
"isRequired": true,
"isRepeated": true
}
],
"environment_variables": [
"environmentVariables": [
{
"name": "LOG_LEVEL",
"description": "Logging level (debug, info, warn, error)",
@ -189,43 +189,43 @@ This will essentially instruct the MCP client to execute `dnx Knapcode.SampleMcp
]
},
{
"registry_type": "oci",
"registry_base_url": "https://docker.io",
"registryType": "oci",
"registryBaseUrl": "https://docker.io",
"identifier": "mcp/filesystem",
"version": "1.0.2",
"transport": {
"type": "stdio"
},
"runtime_arguments": [
"runtimeArguments": [
{
"type": "named",
"description": "Mount a volume into the container",
"name": "--mount",
"value": "type=bind,src={source_path},dst={target_path}",
"is_required": true,
"is_repeated": true,
"isRequired": true,
"isRepeated": true,
"variables": {
"source_path": {
"description": "Source path on host",
"format": "filepath",
"is_required": true
"isRequired": true
},
"target_path": {
"description": "Path to mount in the container. It should be rooted in `/project` directory.",
"is_required": true,
"isRequired": true,
"default": "/project"
}
}
}
],
"package_arguments": [
"packageArguments": [
{
"type": "positional",
"value_hint": "target_dir",
"valueHint": "target_dir",
"value": "/project"
}
],
"environment_variables": [
"environmentVariables": [
{
"name": "LOG_LEVEL",
"description": "Logging level (debug, info, warn, error)",
@ -296,20 +296,20 @@ This will essentially instruct the MCP client to execute `dnx Knapcode.SampleMcp
"version": "0.5.0",
"packages": [
{
"registry_type": "pypi",
"registry_base_url": "https://pypi.org",
"registryType": "pypi",
"registryBaseUrl": "https://pypi.org",
"identifier": "weather-mcp-server",
"version": "0.5.0",
"runtime_hint": "uvx",
"runtimeHint": "uvx",
"transport": {
"type": "stdio"
},
"environment_variables": [
"environmentVariables": [
{
"name": "WEATHER_API_KEY",
"description": "API key for weather service",
"is_required": true,
"is_secret": true
"isRequired": true,
"isSecret": true
},
{
"name": "WEATHER_UNITS",
@ -350,20 +350,20 @@ The `dnx` tool ships with the .NET 10 SDK, starting with Preview 6.
"version": "0.5.0",
"packages": [
{
"registry_type": "nuget",
"registry_base_url": "https://api.nuget.org",
"registryType": "nuget",
"registryBaseUrl": "https://api.nuget.org",
"identifier": "Knapcode.SampleMcpServer",
"version": "0.5.0",
"runtime_hint": "dnx",
"runtimeHint": "dnx",
"transport": {
"type": "stdio"
},
"environment_variables": [
"environmentVariables": [
{
"name": "WEATHER_CHOICES",
"description": "Comma separated list of weather descriptions to randomly select.",
"is_required": true,
"is_secret": false
"isRequired": true,
"isSecret": false
}
]
}
@ -398,14 +398,14 @@ The `dnx` tool ships with the .NET 10 SDK, starting with Preview 6.
"version": "3.1.0",
"packages": [
{
"registry_type": "oci",
"registry_base_url": "https://docker.io",
"registryType": "oci",
"registryBaseUrl": "https://docker.io",
"identifier": "example/database-manager-mcp",
"version": "3.1.0",
"transport": {
"type": "stdio"
},
"runtime_arguments": [
"runtimeArguments": [
{
"type": "named",
"name": "--network",
@ -417,7 +417,7 @@ The `dnx` tool ships with the .NET 10 SDK, starting with Preview 6.
"name": "-e",
"value": "DB_TYPE={db_type}",
"description": "Database type to connect to",
"is_repeated": true,
"isRepeated": true,
"variables": {
"db_type": {
"description": "Type of database",
@ -427,18 +427,18 @@ The `dnx` tool ships with the .NET 10 SDK, starting with Preview 6.
"mongodb",
"redis"
],
"is_required": true
"isRequired": true
}
}
}
],
"package_arguments": [
"packageArguments": [
{
"type": "named",
"name": "--host",
"description": "Database host",
"default": "localhost",
"is_required": true
"isRequired": true
},
{
"type": "named",
@ -448,22 +448,22 @@ The `dnx` tool ships with the .NET 10 SDK, starting with Preview 6.
},
{
"type": "positional",
"value_hint": "database_name",
"valueHint": "database_name",
"description": "Name of the database to connect to",
"is_required": true
"isRequired": true
}
],
"environment_variables": [
"environmentVariables": [
{
"name": "DB_USERNAME",
"description": "Database username",
"is_required": true
"isRequired": true
},
{
"name": "DB_PASSWORD",
"description": "Database password",
"is_required": true,
"is_secret": true
"isRequired": true,
"isSecret": true
},
{
"name": "SSL_MODE",
@ -498,7 +498,7 @@ The `dnx` tool ships with the .NET 10 SDK, starting with Preview 6.
```json
{
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-16/server.schema.json",
"name": "io.modelcontextprotocol.anonymous/hybrid-mcp",
"description": "MCP server available as both local package and remote service",
"repository": {
@ -509,15 +509,15 @@ The `dnx` tool ships with the .NET 10 SDK, starting with Preview 6.
"version": "1.5.0",
"packages": [
{
"registry_type": "npm",
"registry_base_url": "https://registry.npmjs.org",
"registryType": "npm",
"registryBaseUrl": "https://registry.npmjs.org",
"identifier": "@example/hybrid-mcp-server",
"version": "1.5.0",
"runtime_hint": "npx",
"runtimeHint": "npx",
"transport": {
"type": "stdio"
},
"package_arguments": [
"packageArguments": [
{
"type": "named",
"name": "--mode",
@ -540,8 +540,8 @@ The `dnx` tool ships with the .NET 10 SDK, starting with Preview 6.
{
"name": "X-API-Key",
"description": "API key for authentication",
"is_required": true,
"is_secret": true
"isRequired": true,
"isSecret": true
},
{
"name": "X-Region",
@ -592,11 +592,11 @@ The `dnx` tool ships with the .NET 10 SDK, starting with Preview 6.
"version": "1.0.2",
"packages": [
{
"registry_type": "mcpb",
"registry_base_url": "https://github.com",
"registryType": "mcpb",
"registryBaseUrl": "https://github.com",
"identifier": "https://github.com/modelcontextprotocol/text-editor-mcpb/releases/download/v1.0.2/text-editor.mcpb",
"version": "1.0.2",
"file_sha256": "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce",
"fileSha256": "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce",
"transport": {
"type": "stdio"
}
@ -622,25 +622,25 @@ This example shows an MCPB (MCP Bundle) package that:
### 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 `package_arguments` and `runtime_hint` if needed to start the MCP server.
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-07-09/server.schema.json",
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-16/server.schema.json",
"name": "io.snyk/cli-mcp",
"description": "MCP server provided by the Snyk CLI",
"status": "active",
"version": "1.1298.0",
"packages": [
{
"registry_type": "npm",
"registry_base_url": "https://registry.npmjs.org",
"registryType": "npm",
"registryBaseUrl": "https://registry.npmjs.org",
"identifier": "snyk",
"version": "1.1298.0",
"transport": {
"type": "stdio"
},
"package_arguments": [
"packageArguments": [
{ "type": "positional", "value": "mcp" },
{
"type": "named",
@ -657,15 +657,15 @@ Some CLI tools bundle an MCP server, without a standalone MCP package or a publi
### Server with Custom Installation Path
For MCP servers that follow a custom installation path or are embedded in applications without standalone packages, use the `website_url` field to direct users to setup documentation:
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-07-09/server.schema.json",
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-16/server.schema.json",
"name": "io.modelcontextprotocol.anonymous/embedded-mcp",
"description": "MCP server embedded in a Desktop app",
"status": "active",
"website_url": "https://anonymous.modelcontextprotocol.io/embedded-mcp-guide",
"websiteUrl": "https://anonymous.modelcontextprotocol.io/embedded-mcp-guide",
"version": "0.1.0"
}
```
@ -685,19 +685,19 @@ For MCP servers that follow a custom installation path or are embedded in applic
"version": "0.9.5",
"packages": [
{
"registry_type": "npm",
"registry_base_url": "https://registry.npmjs.org",
"registryType": "npm",
"registryBaseUrl": "https://registry.npmjs.org",
"identifier": "@legacy/old-weather-server",
"version": "0.9.5",
"transport": {
"type": "stdio"
},
"environment_variables": [
"environmentVariables": [
{
"name": "WEATHER_API_KEY",
"description": "Weather API key",
"is_required": true,
"is_secret": true
"isRequired": true,
"isSecret": true
}
]
}

View file

@ -1,6 +1,6 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
"$id": "https://static.modelcontextprotocol.io/schemas/2025-09-16/server.schema.json",
"title": "MCP Server Detail",
"$ref": "#/definitions/ServerDetail",
"definitions": {
@ -74,7 +74,7 @@
"example": "1.0.2",
"description": "Version string for this server. SHOULD follow semantic versioning (e.g., '1.0.2', '2.1.0-alpha'). Equivalent of Implementation.version in MCP specification. Non-semantic versions are allowed but may not sort predictably. Version ranges are rejected (e.g., '^1.2.3', '~1.2.3', '>=1.2.3', '1.x', '1.*')."
},
"website_url": {
"websiteUrl": {
"type": "string",
"format": "uri",
"description": "Optional URL to the server's homepage, documentation, or project website. This provides a central link for users to learn more about the server. Particularly useful when the server has custom installation instructions or setup requirements.",
@ -86,18 +86,18 @@
"type": "object",
"additionalProperties": false,
"required": [
"registry_type",
"registryType",
"identifier",
"version",
"transport"
],
"properties": {
"registry_type": {
"registryType": {
"type": "string",
"description": "Registry type indicating how to download packages (e.g., 'npm', 'pypi', 'oci', 'nuget', 'mcpb')",
"examples": ["npm", "pypi", "oci", "nuget", "mcpb"]
},
"registry_base_url": {
"registryBaseUrl": {
"type": "string",
"format": "uri",
"description": "Base URL of the package registry",
@ -117,15 +117,15 @@
"example": "1.0.2",
"minLength": 1
},
"file_sha256": {
"fileSha256": {
"type": "string",
"pattern": "^[a-f0-9]{64}$",
"description": "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"
},
"runtime_hint": {
"runtimeHint": {
"type": "string",
"description": "A hint to help clients determine the appropriate runtime for the package. This field should be provided when `runtime_arguments` are present.",
"description": "A hint to help clients determine the appropriate runtime for the package. This field should be provided when `runtimeArguments` are present.",
"examples": [
"npx",
"uvx",
@ -147,21 +147,21 @@
],
"description": "Transport protocol configuration for the package"
},
"runtime_arguments": {
"runtimeArguments": {
"type": "array",
"description": "A list of arguments to be passed to the package's runtime command (such as docker or npx). The `runtime_hint` field should be provided when `runtime_arguments` are present.",
"description": "A list of arguments to be passed to the package's runtime command (such as docker or npx). The `runtimeHint` field should be provided when `runtimeArguments` are present.",
"items": {
"$ref": "#/definitions/Argument"
}
},
"package_arguments": {
"packageArguments": {
"type": "array",
"description": "A list of arguments to be passed to the package's binary.",
"items": {
"$ref": "#/definitions/Argument"
}
},
"environment_variables": {
"environmentVariables": {
"type": "array",
"description": "A mapping of environment variables to be set when running the package.",
"items": {
@ -177,7 +177,7 @@
"description": "A description of the input, which clients can use to provide context to the user.",
"type": "string"
},
"is_required": {
"isRequired": {
"type": "boolean",
"default": false
},
@ -196,7 +196,7 @@
"type": "string",
"description": "The default value for the input. If this is not set, the user may be prompted to provide a value. If a value is set, it should not be configurable by end users.\n\nIdentifiers wrapped in `{curly_braces}` will be replaced with the corresponding properties from the input `variables` map. If an identifier in braces is not found in `variables`, or if `variables` is not provided, the `{curly_braces}` substring should remain unchanged.\n"
},
"is_secret": {
"isSecret": {
"type": "boolean",
"description": "Indicates whether the input is a secret value (e.g., password, token). If true, clients should handle the value securely.",
"default": false
@ -253,12 +253,12 @@
],
"example": "positional"
},
"value_hint": {
"valueHint": {
"type": "string",
"description": "An identifier-like hint for the value. This is not part of the command line, but can be used by client configuration and to provide hints to users.",
"example": "file_path"
},
"is_repeated": {
"isRepeated": {
"type": "boolean",
"description": "Whether the argument can be repeated multiple times in the command line.",
"default": false
@ -267,7 +267,7 @@
"anyOf": [
{
"required": [
"value_hint"
"valueHint"
]
},
{
@ -304,7 +304,7 @@
"description": "The flag name, including any leading dashes.",
"example": "--port"
},
"is_repeated": {
"isRepeated": {
"type": "boolean",
"description": "Whether the argument can be repeated multiple times.",
"default": false
@ -377,7 +377,7 @@
},
"url": {
"type": "string",
"description": "URL template for the streamable-http transport. Variables in {curly_braces} reference argument value_hints, argument names, or environment variable names. After variable substitution, this should produce a valid URI.",
"description": "URL template for the streamable-http transport. Variables in {curly_braces} reference argument valueHints, argument names, or environment variable names. After variable substitution, this should produce a valid URI.",
"example": "https://api.example.com/mcp"
},
"headers": {
@ -432,7 +432,7 @@
"type": "string",
"format": "uri",
"description": "JSON Schema URI for this server.json format",
"example": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json"
"example": "https://static.modelcontextprotocol.io/schemas/2025-09-16/server.schema.json"
},
"packages": {
"type": "array",
@ -475,4 +475,4 @@
]
}
}
}
}

View file

@ -18,7 +18,8 @@ import (
// EditServerInput represents the input for editing a server
type EditServerInput struct {
Authorization string `header:"Authorization" doc:"Registry JWT token with edit permissions" required:"true"`
ID string `path:"id" doc:"Server ID (UUID)" format:"uuid"`
ServerID string `path:"server_id" doc:"Server ID (UUID)" format:"uuid"`
Version string `query:"version" doc:"Version to edit (e.g., '1.0.0')" required:"true" example:"1.0.0"`
Body apiv0.ServerJSON `body:""`
}
@ -30,9 +31,9 @@ func RegisterEditEndpoints(api huma.API, registry service.RegistryService, cfg *
huma.Register(api, huma.Operation{
OperationID: "edit-server",
Method: http.MethodPut,
Path: "/v0/servers/{id}",
Path: "/v0/servers/{server_id}",
Summary: "Edit MCP server",
Description: "Update an existing MCP server (admin only)",
Description: "Update an existing MCP server (admin only). Requires version query parameter to specify which version to edit.",
Tags: []string{"admin"},
Security: []map[string][]string{
{"bearer": {}},
@ -53,7 +54,8 @@ func RegisterEditEndpoints(api huma.API, registry service.RegistryService, cfg *
}
// Get current server to check permissions against existing name
currentServer, err := registry.GetByID(input.ID)
currentServer, err := registry.GetByServerIDAndVersion(input.ServerID, input.Version)
if err != nil {
if errors.Is(err, database.ErrNotFound) {
return nil, huma.Error404NotFound("Server not found")
@ -76,8 +78,13 @@ func RegisterEditEndpoints(api huma.API, registry service.RegistryService, cfg *
return nil, huma.Error400BadRequest("Cannot change status of deleted server. Deleted servers cannot be undeleted.")
}
// Edit the server
updatedServer, err := registry.EditServer(input.ID, input.Body)
// Edit the server using the version_id from the current server
versionID := currentServer.GetVersionID()
if versionID == "" {
return nil, huma.Error500InternalServerError("Server version ID not found in metadata", nil)
}
updatedServer, err := registry.EditServer(versionID, input.Body)
if err != nil {
if errors.Is(err, database.ErrNotFound) {
return nil, huma.Error404NotFound("Server not found")

Some files were not shown because too many files have changed in this diff Show more