Skip to main content
MCP Deep Dive

Distribution: Registries and Discovery

Ravinder··5 min read
MCPModel Context ProtocolAIDistributionRegistryDiscoveryVersioning
Share:
Part 10 of 10
Distribution: Registries and Discovery

You have built an MCP server, tested it, and deployed it. Now your colleague in a different team wants to use it for their agent. They ask you for the endpoint URL. You send it in a Slack message. Three months later you change the deployment URL and six agents break silently. This is the distribution problem, and it is where most MCP efforts stall out at the organisation level.

Distribution is the set of practices that turn a single-team tool into a reusable platform capability: packaging, versioning, registering, and enabling discovery. Done well, another team finds your server, understands its capabilities, and integrates it in a day — without ever needing to message you.

The Distribution Stack

graph TD Dev["Developer builds\nMCP server"] Pkg["Packages it\n(npm / Docker / binary)"] Reg["Registers it\nin a registry"] Disc["Clients discover it\n(registry API / config)"] Use["Agents use it\n(well-known URL + capability negotiation)"] Dev --> Pkg --> Reg --> Disc --> Use

Each layer has concrete tooling choices. Let us go through them.

Packaging

npm for Node.js Servers

For TypeScript/Node MCP servers, npm is the natural packaging format. Structure your package so it exports both the server entrypoint and the type definitions:

{
  "name": "@your-org/taskflow-mcp",
  "version": "1.2.0",
  "description": "MCP server for Taskflow project management",
  "main": "dist/server.js",
  "types": "dist/server.d.ts",
  "bin": {
    "taskflow-mcp": "dist/cli.js"
  },
  "files": ["dist/", "README.md"],
  "engines": { "node": ">=20" }
}

A bin entry lets teams install and run your server as a CLI:

npx @your-org/taskflow-mcp --port 3000

This is exactly how stdio-based developer tools ship. Claude Desktop's MCP configuration references npm-packaged servers this way:

{
  "mcpServers": {
    "taskflow": {
      "command": "npx",
      "args": ["@your-org/taskflow-mcp"]
    }
  }
}

Docker for Network Servers

Network-deployed servers should ship as Docker images. Tag with semantic versions:

docker build -t ghcr.io/your-org/taskflow-mcp:1.2.0 .
docker push ghcr.io/your-org/taskflow-mcp:1.2.0
docker tag ghcr.io/your-org/taskflow-mcp:1.2.0 ghcr.io/your-org/taskflow-mcp:latest

Use a multi-stage build to keep the image lean:

FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
 
FROM node:22-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package.json .
ENV NODE_ENV=production
EXPOSE 3000
CMD ["node", "dist/server.js"]

Versioning

MCP uses semantic versioning for both the protocol and the server. Do not conflate them.

  • protocolVersion in the initialize handshake is the MCP spec version (e.g. "2024-11-05").
  • Your server's own version tracks its tool/resource/prompt API.

Version your tool schemas explicitly. When you remove or rename a tool, that is a breaking change — bump the major version.

// Communicate breaking changes in the server info
const server = new McpServer({
  name: "taskflow-mcp",
  version: "2.0.0"  // breaking: removed deprecated `get_task_legacy` tool
});

Deprecation Pattern

Never remove a tool silently. Add a deprecation notice in the description and keep the old name working for at least one major version:

{
  name: "get_task_legacy",        // old name
  description: "DEPRECATED: Use `get_task` instead. Will be removed in v3.0.",
  inputSchema: { /* ... */ }
}

Registries

Public: MCP.run and the Ecosystem

The emerging public registry is mcp.run, which lets you publish a server manifest that describes your server's capabilities, connection details, and auth requirements. The format is a JSON document:

{
  "name": "taskflow-mcp",
  "version": "1.2.0",
  "description": "MCP server for Taskflow project management",
  "endpoint": "https://mcp.taskflow.example.com/mcp",
  "transport": "streamable-http",
  "auth": {
    "type": "oauth2",
    "authorizationUrl": "https://auth.taskflow.example.com/authorize",
    "tokenUrl":         "https://auth.taskflow.example.com/token",
    "scopes": ["tickets:read", "tickets:write"]
  },
  "capabilities": {
    "tools":     true,
    "resources": true,
    "prompts":   false
  }
}

Private: Internal Registry

For enterprise use, a private registry is typically a simple service that stores these manifests and exposes a search API. Even a Git repository with a registry.json file works at small scale:

{
  "servers": [
    {
      "name": "taskflow-mcp",
      "team": "platform",
      "endpoint": "https://mcp-internal.example.com/taskflow/mcp",
      "version": "1.2.0",
      "tags": ["project-management", "tickets", "crm"]
    },
    {
      "name": "analytics-mcp",
      "team": "data",
      "endpoint": "https://mcp-internal.example.com/analytics/mcp",
      "version": "0.9.0",
      "tags": ["analytics", "reporting", "sql"]
    }
  ]
}

A discovery API wrapping this:

# GET /registry/search?q=project
from fastapi import FastAPI
import json
 
app = FastAPI()
registry = json.load(open("registry.json"))
 
@app.get("/registry/search")
def search(q: str = ""):
    results = [
        s for s in registry["servers"]
        if q.lower() in s["name"].lower()
        or any(q.lower() in tag for tag in s.get("tags", []))
    ]
    return {"results": results}

Dynamic Discovery via /.well-known

A server can self-describe at a well-known URL, enabling clients to discover capabilities without a separate registry:

app.get("/.well-known/mcp-server", (_, res) => {
  res.json({
    name:    "taskflow-mcp",
    version: "1.2.0",
    transport: "streamable-http",
    endpoint:  "/mcp",
    capabilities: {
      tools:     true,
      resources: true,
      prompts:   false
    }
  });
});

An agent or platform tool can GET /.well-known/mcp-server on any suspected MCP endpoint and immediately know what it is dealing with.

Discovery Flow in Practice

sequenceDiagram participant Dev as Developer / Agent Builder participant Reg as Internal Registry participant Server as MCP Server participant Agent as Agent Runtime Dev ->> Reg: GET /registry/search?q=tickets Reg -->> Dev: [{name: "taskflow-mcp", endpoint: "..."}] Dev ->> Server: GET /.well-known/mcp-server Server -->> Dev: {capabilities, auth, version} Dev ->> Agent: Configure endpoint + credentials Agent ->> Server: initialize handshake Server -->> Agent: capabilities negotiation Agent ->> Server: tools/list, resources/list Server -->> Agent: full capability manifest

Key Takeaways

  • Package npm-based servers with a bin entry so teams can run them via npx without any infrastructure.
  • Docker images should be tagged with semantic versions and pushed to a container registry alongside a latest tag.
  • Version your tool schemas independently of the MCP protocol version; a renamed tool is a breaking change.
  • Use a deprecation description field and a one-major-version grace period before removing any tool.
  • A /.well-known/mcp-server endpoint lets clients self-discover capabilities without a central registry.
  • An internal registry — even a versioned JSON file in Git — dramatically reduces the friction for teams to find and adopt your server.
Part 10 of 10
Share: