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.
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:
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.versionfield in the MCPinitializeresponse. - 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.
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:
- The next health check runs against the active version endpoint
- The health check reads
serverInfo.versionfrom the MCPinitializeresponse - The registry detects the version changed from
2.14.4to2.14.5 - 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
- A WARNING log message is emitted noting the version change
- The dashboard card shows
srv 2.14.5with a green dot indicator
What the admin sees:
- The
srvbadge 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.
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.
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,librariesUsing 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.jsonExample 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"]
}- The registry detects that
/context7already exists with versionv1.0.0 - A new inactive version document is created at
_id: /context7:v2.0.0 - The active version document at
_id: /context7is updated with/context7:v2.0.0added to itsother_version_idsarray - The nginx configuration is regenerated to include a version map entry for
v2.0.0 - Nginx is reloaded
The API response includes "is_new_version": true to confirm a version was added rather than a new server created.
- The existing Context7 server card now shows a version badge (e.g.,
v1.0.0with a dropdown arrow) - Clicking the badge opens the Version Selector Modal showing both versions
v1.0.0is marked asACTIVE(green badge)v2.0.0is marked asbeta(blue badge)
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.
Once testing is complete, switch the active version:
Using the Version Selector Modal (UI):
- Click the version badge on the Context7 server card
- In the modal, click "Set Active" on version
v2.0.0 - 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- The current active document (
/context7, versionv1.0.0) becomes an inactive document at_id: /context7:v1.0.0 - The target inactive document (
/context7:v2.0.0) becomes the new active document at_id: /context7 - The
other_version_idsarray is updated to reference/context7:v1.0.0instead of/context7:v2.0.0 - The FAISS search index is re-indexed with
v2.0.0metadata - The nginx configuration is regenerated and reloaded
- A background health check is triggered for the newly active version
- The dashboard updates to show
v2.0.0as 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.
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.0The switch is immediate:
- All default traffic reverts to
v1.0.0 - Clients with
X-MCP-Server-Version: v2.0.0can still reachv2.0.0for debugging - A health check runs automatically for the restored version
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-httpThe Version Selector Modal shows deprecated versions with an amber badge.
# 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.0Constraints:
- 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.0will fall back to the default version after removal.
# 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 /context7Response:
{
"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"
}
]
}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
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.
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.
Clients that need to pin to a specific version add the X-MCP-Server-Version header to their requests:
{
"mcpServers": {
"context7": {
"type": "streamable-http",
"url": "https://gateway.example.com/context7",
"headers": {
"X-MCP-Server-Version": "v1.0.0"
}
}
}
}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},
)| 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 |
