Skip to content

Latest commit

 

History

History
379 lines (288 loc) · 12.2 KB

File metadata and controls

379 lines (288 loc) · 12.2 KB

MCP Server Versioning - Operations Guide

This guide covers the operational workflows for managing multiple versions of MCP servers in the gateway registry.

For the technical design and architecture details, see Server Versioning Design.

UI Demo

The following shows the version badges on server cards (both the MCP server-reported version and the user-provided routing version) and the version swap workflow using the Version Selector Modal:

MCP Server Versioning UI Flow


Understanding What You See on the Dashboard

Version Badges on Server Cards

Each server card can display up to two version indicators:

Routing Version Badge (e.g., v2.0.0 with a dropdown arrow):

  • This is the user-provided version label that controls which backend receives traffic.
  • Only appears when the server has multiple versions registered.
  • Clicking the badge opens the Version Selector Modal where you can switch the active version.
  • Single-version servers do not show this badge.

MCP Server Version Badge (e.g., srv 2.14.5):

  • This is the software version reported by the running MCP server during health checks.
  • This value is determined automatically -- it comes from the serverInfo.version field in the MCP initialize response.
  • A small green dot appears if this version changed within the last 24 hours, indicating the upstream server deployed a new build.
  • This badge is informational only and does not affect routing.

These two versions are independent. The routing version is an operational label you control. The MCP server version is a fact about what code is running at the backend URL.


Workflow 1: MCP Server Updates Its Own Version

Scenario: An MCP server developer deploys a new build. The server at https://mcp.context7.com/mcp starts reporting version 2.14.5 instead of 2.14.4. No admin action was taken.

What happens automatically:

  1. The next health check runs against the active version endpoint
  2. The health check reads serverInfo.version from the MCP initialize response
  3. The registry detects the version changed from 2.14.4 to 2.14.5
  4. The registry stores:
    • mcp_server_version: 2.14.5 (new value)
    • mcp_server_version_previous: 2.14.4 (old value)
    • mcp_server_version_updated_at: current timestamp
  5. A WARNING log message is emitted noting the version change
  6. The dashboard card shows srv 2.14.5 with a green dot indicator

What the admin sees:

  • The srv badge on the server card updates to show the new version
  • A green dot appears next to the version for 24 hours
  • Hovering over the badge shows: "MCP Server Version: 2.14.5 (previously 2.14.4)"

No action required. This is purely informational. The routing configuration does not change. Traffic continues to flow to the same backend URL. The user-provided routing version label (e.g., v1.0.0) is unaffected.


Workflow 2: Platform Admin Registers a New Version of a Server

Scenario: A platform admin wants to add version v2.0.0 of Context7 pointing to a new backend URL, while keeping v1.0.0 active.

Step 1: Register the New Version

Register the server with the same path but a different version. The registry detects this is a new version of an existing server and creates it as an inactive version document.

Using the CLI:

uv run python -m api.registry_management register \
  --name "Context7 MCP Server" \
  --path /context7 \
  --version v2.0.0 \
  --status beta \
  --proxy-url "https://mcp-v2.context7.com/mcp" \
  --transport streamable-http \
  --tags documentation,search,libraries

Using the API:

curl -X POST https://gateway.example.com/api/servers/register \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "server_name": "Context7 MCP Server",
    "path": "/context7",
    "version": "v2.0.0",
    "status": "beta",
    "proxy_pass_url": "https://mcp-v2.context7.com/mcp",
    "supported_transports": ["streamable-http"],
    "tags": ["documentation", "search", "libraries"]
  }'

Using a JSON config file:

uv run python -m api.registry_management register --config cli/examples/context7-v2-server-config.json

Example config file (context7-v2-server-config.json):

{
  "server_name": "Context7 MCP Server",
  "description": "Up-to-date Docs for LLMs and AI code editors (Version 2 - Beta)",
  "path": "/context7",
  "version": "v2.0.0",
  "status": "beta",
  "proxy_pass_url": "https://mcp-v2.context7.com/mcp",
  "supported_transports": ["streamable-http"],
  "tags": ["documentation", "search", "libraries", "packages", "api-reference", "code-examples"]
}

What Happens After Registration

  1. The registry detects that /context7 already exists with version v1.0.0
  2. A new inactive version document is created at _id: /context7:v2.0.0
  3. The active version document at _id: /context7 is updated with /context7:v2.0.0 added to its other_version_ids array
  4. The nginx configuration is regenerated to include a version map entry for v2.0.0
  5. Nginx is reloaded

The API response includes "is_new_version": true to confirm a version was added rather than a new server created.

What the Admin Sees on the Dashboard

  • The existing Context7 server card now shows a version badge (e.g., v1.0.0 with a dropdown arrow)
  • Clicking the badge opens the Version Selector Modal showing both versions
  • v1.0.0 is marked as ACTIVE (green badge)
  • v2.0.0 is marked as beta (blue badge)

Step 2: Test the New Version

Before promoting v2.0.0 to active, test it using the X-MCP-Server-Version header:

In an AI coding assistant (e.g., Roo Code, Claude Desktop):

Add the header to the MCP server configuration:

{
  "mcpServers": {
    "context7": {
      "type": "streamable-http",
      "url": "https://gateway.example.com/context7",
      "headers": {
        "X-MCP-Server-Version": "v2.0.0",
        "X-Authorization": "Bearer <token>"
      }
    }
  }
}

With curl:

curl -X POST https://gateway.example.com/context7 \
  -H "X-MCP-Server-Version: v2.0.0" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc": "2.0", "method": "tools/list", "id": 1}'

Traffic without the header continues to route to v1.0.0. Only requests with the explicit header reach v2.0.0.

Step 3: Promote to Active

Once testing is complete, switch the active version:

Using the Version Selector Modal (UI):

  1. Click the version badge on the Context7 server card
  2. In the modal, click "Set Active" on version v2.0.0
  3. The modal closes and the card updates

Using the API:

curl -X PUT https://gateway.example.com/api/servers/context7/versions/default \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"version": "v2.0.0"}'

Using the CLI:

uv run python -m api.registry_management set-default-version \
  --path /context7 \
  --version v2.0.0

What Happens During the Switch

  1. The current active document (/context7, version v1.0.0) becomes an inactive document at _id: /context7:v1.0.0
  2. The target inactive document (/context7:v2.0.0) becomes the new active document at _id: /context7
  3. The other_version_ids array is updated to reference /context7:v1.0.0 instead of /context7:v2.0.0
  4. The FAISS search index is re-indexed with v2.0.0 metadata
  5. The nginx configuration is regenerated and reloaded
  6. A background health check is triggered for the newly active version
  7. The dashboard updates to show v2.0.0 as active

All traffic without the X-MCP-Server-Version header now routes to v2.0.0. Clients that still send X-MCP-Server-Version: v1.0.0 continue to reach the old version.


Workflow 3: Instant Rollback

Scenario: After promoting v2.0.0, you discover an issue and need to revert to v1.0.0.

# API
curl -X PUT https://gateway.example.com/api/servers/context7/versions/default \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"version": "v1.0.0"}'

# CLI
uv run python -m api.registry_management set-default-version \
  --path /context7 \
  --version v1.0.0

The switch is immediate:

  • All default traffic reverts to v1.0.0
  • Clients with X-MCP-Server-Version: v2.0.0 can still reach v2.0.0 for debugging
  • A health check runs automatically for the restored version

Workflow 4: Deprecate and Remove an Old Version

Mark as Deprecated

When registering the version, use --status deprecated:

uv run python -m api.registry_management register \
  --name "Context7 MCP Server" \
  --path /context7 \
  --version v1.0.0 \
  --status deprecated \
  --proxy-url "https://mcp.context7.com/mcp" \
  --transport streamable-http

The Version Selector Modal shows deprecated versions with an amber badge.

Remove a Version

# API
curl -X DELETE https://gateway.example.com/api/servers/context7/versions/v1.0.0 \
  -H "Authorization: Bearer <token>"

# CLI
uv run python -m api.registry_management remove-version \
  --path /context7 \
  --version v1.0.0

Constraints:

  • You cannot remove the currently active version. Switch to a different version first.
  • Removing a version deletes its document from the database and removes its nginx map entry.
  • Clients sending X-MCP-Server-Version: v1.0.0 will fall back to the default version after removal.

Workflow 5: List All Versions

# API
curl https://gateway.example.com/api/servers/context7/versions \
  -H "Authorization: Bearer <token>"

# CLI
uv run python -m api.registry_management list-versions --path /context7

Response:

{
  "path": "/context7",
  "default_version": "v2.0.0",
  "versions": [
    {
      "version": "v2.0.0",
      "proxy_pass_url": "https://mcp-v2.context7.com/mcp",
      "status": "stable",
      "is_default": true
    },
    {
      "version": "v1.0.0",
      "proxy_pass_url": "https://mcp.context7.com/mcp",
      "status": "deprecated",
      "is_default": false,
      "sunset_date": "2026-06-01"
    }
  ]
}

Workflow 6: Delete a Server (All Versions)

When you delete a server entirely, all version documents are cascade-deleted:

# CLI
uv run python -m api.registry_management delete --path /context7

# API
curl -X DELETE https://gateway.example.com/api/servers/context7 \
  -H "Authorization: Bearer <token>"

This removes:

  • The active version document at /context7
  • All inactive version documents matching /context7:*
  • The FAISS search index entry
  • The nginx location block and map entries

How Versioning Affects Search

Only the active version of each server appears in search results. Inactive versions are excluded at index time (they are never added to the FAISS vector index), so they do not consume result slots.

When you switch the active version, the search index is automatically re-indexed with the new active version's metadata (name, description, tags, tools). This means search results always reflect the currently active version.


How Versioning Affects Health Checks

Only the active version is health-checked. Inactive versions are skipped during the health check cycle. When you switch the active version, a health check for the newly active version is triggered immediately in the background.


Client Configuration for Version Pinning

Clients that need to pin to a specific version add the X-MCP-Server-Version header to their requests:

Claude Desktop / Roo Code / Other MCP Clients

{
  "mcpServers": {
    "context7": {
      "type": "streamable-http",
      "url": "https://gateway.example.com/context7",
      "headers": {
        "X-MCP-Server-Version": "v1.0.0"
      }
    }
  }
}

Programmatic Access

import httpx

response = httpx.post(
    "https://gateway.example.com/context7",
    headers={
        "X-MCP-Server-Version": "v1.0.0",
        "Content-Type": "application/json",
    },
    json={"jsonrpc": "2.0", "method": "tools/list", "id": 1},
)

Header Values

Value Behavior
Omitted Routes to active (default) version
latest Routes to active (default) version
v1.0.0 Routes to version v1.0.0 specifically
Unknown value Falls back to default backend URL