Guide · MCP Registry Distribution

MCP server registry requirements

The four major MCP server registries — Smithery, Glama, PulseMCP, and MCP.so — each have their own submission processes and metadata formats, but they share a common set of technical requirements. A server that satisfies the universal requirements will pass all four registries' verification steps and maintain its listings over time. A server that meets only some requirements will fail at one or more registries and lose listings when re-scan cycles catch a regression. This guide consolidates the universal checklist and explains the reasoning behind each requirement.

TL;DR

Before submitting to any MCP registry: (1) verify your server responds to a real MCP initialize request with a valid protocolVersion, (2) confirm tools/list returns at least one tool with a name, description, and input schema, (3) check that your TLS certificate is valid and issued by a public CA, (4) write tool descriptions from the user's perspective (not the implementation's). Then set up AliveMCP monitoring before submitting so you know immediately if a post-launch regression would fail a registry's re-scan.

The universal registry checklist

RequirementSmitheryGlamaPulseMCPMCP.soWhy it matters
HTTPS with public CA certificateRequiredRequiredRequired for remoteRequired for remoteAll registry crawlers reject self-signed certs; agent frameworks refuse to connect without valid TLS
initialize handshake succeedsRequiredRequiredFor remoteFor remoteThe minimum bar for "MCP-compliant" — HTTP 200 alone is not enough
tools/list returns non-empty arrayRequiredRequiredPreferredPreferredA server with no tools provides no value; registries deprioritize empty tool lists
Each tool has name, description, inputSchemaRequiredRequiredPreferredPreferredMissing descriptions make tools unsearchable in directory; missing schemas break callers
README with tool table and install commandPreferredPreferredRequiredRequiredPulseMCP and MCP.so primarily surface README content; no README = incomplete listing
GitHub topics include mcp-serverHelpfulHelpfulRequiredRequiredPulseMCP and MCP.so use GitHub topics as primary discovery mechanism
smithery.yaml manifestRequiredNot requiredNot requiredNot requiredSmithery-specific; enables install-from-browser UI and config form generation
Server responds in <5 secondsRequiredRequiredFor remoteFor remoteCrawler timeouts at 5–30s; slow servers may be marked unavailable even if they eventually respond

Protocol compliance verification script

Run this script against your server before submitting to any registry. It replicates the verification sequence every major registry's crawler performs:

#!/bin/bash
# verify-mcp-registry-ready.sh — run before submitting to any MCP registry
SERVER_URL="${1:-https://your-server.example.com/mcp}"

echo "=== MCP Registry Readiness Check ==="
echo "Target: $SERVER_URL"
echo ""

# 1. TLS verification
echo "[1/4] TLS certificate check..."
CERT_CHECK=$(echo | openssl s_client -connect "$(echo $SERVER_URL | sed 's|https://||' | cut -d/ -f1):443" 2>&1 | grep "Verify return code")
if echo "$CERT_CHECK" | grep -q "0 (ok)"; then
  echo "  ✓ TLS certificate valid"
else
  echo "  ✗ TLS issue: $CERT_CHECK"
  exit 1
fi

# 2. HTTP reachability
echo "[2/4] HTTP reachability check..."
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" -X POST "$SERVER_URL" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":0,"method":"ping","params":{}}')
if [ "$HTTP_STATUS" != "000" ]; then
  echo "  ✓ Server reachable (HTTP $HTTP_STATUS)"
else
  echo "  ✗ Server unreachable"
  exit 1
fi

# 3. MCP initialize handshake
echo "[3/4] MCP initialize handshake..."
INIT_RESPONSE=$(curl -s -X POST "$SERVER_URL" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc":"2.0","id":1,"method":"initialize",
    "params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"registry-verifier","version":"1.0"}}
  }')

if echo "$INIT_RESPONSE" | grep -q '"protocolVersion"'; then
  PROTOCOL_VERSION=$(echo "$INIT_RESPONSE" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['result']['protocolVersion'])" 2>/dev/null)
  echo "  ✓ initialize succeeded (protocolVersion: $PROTOCOL_VERSION)"
else
  echo "  ✗ initialize failed. Response:"
  echo "    $INIT_RESPONSE"
  exit 1
fi

# 4. tools/list check
echo "[4/4] Tool list verification..."
TOOLS_RESPONSE=$(curl -s -X POST "$SERVER_URL" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}')

TOOL_COUNT=$(echo "$TOOLS_RESPONSE" | python3 -c "import sys,json; d=json.load(sys.stdin); print(len(d['result']['tools']))" 2>/dev/null)
if [ -n "$TOOL_COUNT" ] && [ "$TOOL_COUNT" -gt 0 ]; then
  echo "  ✓ tools/list returned $TOOL_COUNT tool(s)"
  echo ""
  echo "=== Registry Readiness: PASS ==="
  echo "Your server passes the universal MCP registry requirements."
else
  echo "  ✗ tools/list failed or returned empty tools array"
  echo "  Response: $TOOLS_RESPONSE"
  exit 1
fi

Save this script as scripts/verify-registry-ready.sh in your repository and run it as part of your pre-release checklist. A server that passes all four checks is ready to submit to any MCP registry. The script exits 0 on success and 1 on failure — add it to CI to catch regressions before they affect your live registry listings.

Common registry rejection reasons

The most frequent reasons MCP servers fail registry verification or lose their listings after approval:

FailureRoot causeFix
HTTP 200 but initialize failsReverse proxy returns "OK" for unrecognized paths without forwarding to MCP serverCheck proxy routing config; verify POST to /mcp reaches the MCP process
Self-signed TLS rejectedUsing a development certificate in productionUse Let's Encrypt via Caddy or Certbot; automatic renewal prevents expiry failures
Expired TLS certificateAuto-renewal disabled or failed silentlyEnable auto-renewal; add certificate expiry monitoring
Empty tools arrayTool registration happens in an async init that hasn't completed at list timeWait for async initialization before accepting requests; use a ready probe
Missing tool descriptionsDescriptions not set in server.tool() callsAdd descriptions to every tool — required by spec and all registries
Crawler timeoutCold start on serverless platform exceeds 5sUse keep-alive pings or minimum-instances=1 to prevent cold starts
Server healthy at submission, fails at re-scanDeployment broke MCP protocol layer after listing was createdAdd post-deploy verification; use AliveMCP to catch regressions between re-scans

Why uptime monitoring is essential for registry health

There is a fundamental gap in the registry lifecycle: registries verify your server at submission time and then re-check on a schedule. Between those checks, your server could be down for hours without your listing showing any indication of a problem. The consequences compound:

  1. Registry re-scan catches the failure — your verified badge is removed and your listing is deprioritized.
  2. Users try to connect during the outage — they fail, attribute the failure to your server quality, and move on to an alternative. They may never return.
  3. Search engines index the down state — if a registry page loads slowly or errors because your server appears unhealthy, it signals poor quality.

The economics of registry discoverability make uptime critically important. You may invest significant effort getting your server listed across four registries. That distribution work is continuously at risk if your server has reliability issues that you discover only after a registry flags them.

AliveMCP probes your endpoint every 60 seconds — the same initialize + tools/list sequence that registries run, but continuously. When a failure happens at 3am after a bad deployment, you know within 3 minutes (3 consecutive probe failures), not the next time you log in or the next time a registry crawls. This is the gap between registry monitoring and real-time protocol monitoring.

// Example: post-deploy health check that mirrors what registries verify
// Run this in your deployment pipeline after every push

async function verifyRegistryReadiness(serverUrl: string): Promise<void> {
  const client = new McpClient();
  const transport = new StreamableHTTPClientTransport(new URL(serverUrl));

  try {
    await client.connect(transport);
    // If connect() succeeds, initialize handshake completed

    const tools = await client.listTools();
    if (tools.tools.length === 0) {
      throw new Error('Server returned empty tools list — registry verification will fail');
    }

    const missingDescriptions = tools.tools.filter(t => !t.description?.trim());
    if (missingDescriptions.length > 0) {
      console.warn(`Warning: ${missingDescriptions.length} tools missing descriptions: ${missingDescriptions.map(t => t.name).join(', ')}`);
    }

    console.log(`✓ Registry readiness verified: ${tools.tools.length} tools registered`);
  } finally {
    await client.close();
  }
}

// In your CI/CD pipeline:
await verifyRegistryReadiness(process.env.MCP_SERVER_URL!);

Related questions

Do I need to meet all requirements for every registry simultaneously?

The GitHub topic and README requirements are relevant for PulseMCP and MCP.so but not for Smithery's direct submission flow. The smithery.yaml is required for Smithery but ignored by all other registries. However, the core protocol requirements (TLS, initialize handshake, non-empty tools list with descriptions) are universal — meeting them is necessary for all four registries, not just one. It is most efficient to meet all requirements before any submission, then submit to all registries in one pass.

My server uses SSE transport instead of streamable HTTP — does that affect registry compatibility?

Most MCP registries and their crawlers support both SSE transport (the older standard) and streamable HTTP transport (the newer standard introduced in MCP protocol version 2025-03-26). If you use SSE transport, ensure your crawler-visible endpoint URL accepts SSE connections with the correct Content-Type: text/event-stream response header. Some newer registry crawlers may prefer or default to streamable HTTP — if your listing shows as unverified despite your endpoint being reachable, check whether the crawler is sending streamable HTTP requests to an SSE-only endpoint.

How often do registries re-scan listed servers?

Re-scan frequency varies by registry and is not always publicly documented: Smithery re-crawls on a roughly weekly cadence with more frequent checks for servers with a history of instability. Glama re-verifies on a similar weekly schedule. PulseMCP's GitHub topic crawl runs weekly. MCP.so's re-verification cadence for remote servers is less documented but observed to be bi-weekly to monthly. The implication: a server can be down for a week before a registry flags it. This is why continuous external monitoring is not optional for maintaining registry listings — it fills the gap that registry re-scans leave.

What should my server's initialize response include for registries?

The initialize response serverInfo object should include a human-readable name (not an internal identifier like "mcp-service-v2-prod") and a version string following semver (e.g., "1.2.0"). Registries may display serverInfo.name as a fallback if the submission name is not set. The capabilities object should accurately reflect what your server supports — if you claim resources: {} capability but your server doesn't actually implement resources/list, some registry crawlers will flag the mismatch.

Further reading