Skip to content
Learn Agentic AI
Learn Agentic AI13 min read8 views

MCP Server Discovery and Registry: Finding and Connecting to Available Tools

Learn how MCP clients discover available servers, understand server manifests and tool catalogs, and build a lightweight server registry that lets agents dynamically connect to the right tools.

The Discovery Problem

In a simple setup, an agent connects to a hardcoded list of MCP servers. But as organizations deploy dozens of MCP servers — one for databases, one for Slack, one for file systems, one for monitoring — the question becomes: how does an agent know which servers exist and which tools they offer?

This is the server discovery problem. It parallels service discovery in microservices architectures: you need a registry that catalogs available servers, their capabilities, and their connection details so that agents (or humans configuring agents) can find the right tools without manual configuration.

Static Configuration: The Starting Point

The simplest discovery mechanism is a JSON configuration file that lists available servers. Both Claude Desktop and the OpenAI Agents SDK support this pattern:

flowchart LR
    HOST(["MCP host<br/>Claude Desktop or IDE"])
    CLIENT["MCP client"]
    subgraph SERVERS["MCP Servers"]
        S1["Filesystem server"]
        S2["GitHub server"]
        S3["Postgres server"]
        SX["Custom tool server"]
    end
    LLM["LLM session"]
    OUT(["Grounded action"])
    HOST <--> CLIENT
    CLIENT <-->|stdio or HTTP+SSE| S1
    CLIENT <--> S2
    CLIENT <--> S3
    CLIENT <--> SX
    CLIENT --> LLM --> OUT
    style HOST fill:#f1f5f9,stroke:#64748b,color:#0f172a
    style CLIENT fill:#4f46e5,stroke:#4338ca,color:#fff
    style OUT fill:#059669,stroke:#047857,color:#fff
# mcp_servers.json — static server configuration
{
    "servers": {
        "database": {
            "transport": "stdio",
            "command": "python",
            "args": ["servers/db_server.py"],
            "description": "Query and manage application databases"
        },
        "filesystem": {
            "transport": "stdio",
            "command": "npx",
            "args": ["-y", "@modelcontextprotocol/server-filesystem", "/data"],
            "description": "Read and write files in the data directory"
        },
        "monitoring": {
            "transport": "http",
            "url": "https://mcp.internal.company.com/monitoring",
            "description": "Access application metrics and alerts"
        }
    }
}

This works for small teams but breaks down as the number of servers grows. Every time someone deploys a new MCP server, every agent configuration file needs to be updated.

Hear it before you finish reading

Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.

Try Live Demo →

Building a Server Registry

A server registry is a centralized catalog that MCP servers register with. Agents query the registry to discover available servers dynamically:

# registry_server.py — a simple MCP server registry
from mcp.server.fastmcp import FastMCP
import json
from datetime import datetime, timedelta

mcp_registry = FastMCP(name="MCPRegistry")

# In-memory registry (use a database in production)
_registry: dict[str, dict] = {}

@mcp_registry.tool()
async def register_server(
    name: str,
    description: str,
    transport: str,
    url: str | None = None,
    command: str | None = None,
    args: list[str] | None = None,
    tags: list[str] | None = None,
) -> str:
    """Register an MCP server in the discovery registry.

    Args:
        name: Unique server name.
        description: What this server does.
        transport: Transport type - 'stdio' or 'http'.
        url: Server URL (required for http transport).
        command: Command to launch (required for stdio transport).
        args: Command arguments (for stdio transport).
        tags: Searchable tags describing server capabilities.
    """
    _registry[name] = {
        "name": name,
        "description": description,
        "transport": transport,
        "url": url,
        "command": command,
        "args": args or [],
        "tags": tags or [],
        "registered_at": datetime.utcnow().isoformat(),
        "last_heartbeat": datetime.utcnow().isoformat(),
    }
    return json.dumps({"registered": name})

@mcp_registry.tool()
async def discover_servers(
    tag: str | None = None,
    transport: str | None = None,
) -> str:
    """Discover available MCP servers, optionally filtered by tag or transport.

    Args:
        tag: Filter servers by this tag.
        transport: Filter by transport type ('stdio' or 'http').
    """
    cutoff = datetime.utcnow() - timedelta(minutes=5)
    results = []

    for server in _registry.values():
        heartbeat = datetime.fromisoformat(server["last_heartbeat"])
        if heartbeat < cutoff:
            continue  # Skip stale servers

        if tag and tag not in server["tags"]:
            continue
        if transport and server["transport"] != transport:
            continue

        results.append(server)

    return json.dumps({
        "count": len(results),
        "servers": results,
    }, indent=2)

Server Manifests

Each MCP server can expose a manifest resource that describes itself in detail. This goes beyond what initialize returns — it includes documentation, examples, and dependency information:

# In your MCP server, expose a manifest resource
@mcp_server.resource("manifest://server/info")
async def get_manifest() -> str:
    """Return the server manifest with capability details."""
    import json
    return json.dumps({
        "name": "DatabaseServer",
        "version": "2.1.0",
        "description": "SQL query and management tools for PostgreSQL",
        "author": "[email protected]",
        "documentation_url": "https://docs.internal/mcp/database",
        "capabilities": {
            "tools": [
                {
                    "name": "query_db",
                    "category": "read",
                    "description": "Execute read-only SQL queries",
                },
                {
                    "name": "insert_record",
                    "category": "write",
                    "description": "Insert records into tables",
                },
            ],
            "resources": ["schema://tables", "metrics://db/stats"],
            "prompts": ["analyze_table", "debug_slow_query"],
        },
        "dependencies": {
            "requires_auth": True,
            "auth_method": "api_key",
            "rate_limit": "100 requests per minute",
        },
    }, indent=2)

Auto-Registration Pattern

Production MCP servers can register themselves with the registry on startup:

import httpx

async def auto_register(registry_url: str, server_info: dict):
    """Register this server with the central MCP registry."""
    async with httpx.AsyncClient() as client:
        response = await client.post(
            f"{registry_url}/mcp",
            json={
                "jsonrpc": "2.0",
                "id": 1,
                "method": "tools/call",
                "params": {
                    "name": "register_server",
                    "arguments": server_info,
                },
            },
        )
        print(f"Registration result: {response.json()}")

Combine auto-registration with periodic heartbeats, and the registry always reflects the current state of available servers. When a server goes down, its heartbeat expires and it disappears from discovery results.

Still reading? Stop comparing — try CallSphere live.

CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.

Agent-Side Dynamic Connection

On the agent side, query the registry before building the agent's server list:

from agents import Agent, Runner
from agents.mcp import MCPServerStreamableHTTP

async def build_agent_with_discovery(task_tags: list[str]):
    """Build an agent that connects to servers matching the task."""
    # Query the registry for relevant servers
    registry = MCPServerStreamableHTTP(
        name="Registry",
        params={"url": "http://registry:8000/mcp"},
    )

    async with registry:
        # Use the registry to find servers tagged for our task
        # Then connect the agent to those servers
        pass  # Implementation depends on your agent framework

FAQ

Is there a standard MCP server registry protocol?

As of early 2026, there is no official MCP registry specification. The patterns described here are community-developed approaches. The MCP specification focuses on the client-server protocol, leaving discovery as an implementation concern. Expect a standardized registry protocol to emerge as the ecosystem matures.

How do I handle version conflicts between servers?

Include version information in server manifests and registry entries. When two servers expose tools with the same name, use the server name as a namespace prefix. Your agent configuration should specify which server version to prefer when conflicts arise.

Should agents discover servers at runtime or at configuration time?

For most production deployments, discover at configuration time and cache the server list. Runtime discovery adds latency and introduces a dependency on the registry being available. Reserve runtime discovery for long-running agents that need to adapt to new servers appearing in the ecosystem.


#MCP #ServiceDiscovery #ToolRegistry #AIAgents #AgenticAI #LearnAI #AIEngineering

Share

Try CallSphere AI Voice Agents

See how AI voice agents work for your industry. Live demo available -- no signup required.

Related Articles You May Like

AI Infrastructure

MCP Registry Catalogs in 2026: Official Registry vs Smithery vs mcp.so

The Official MCP Registry hit API freeze v0.1. Smithery has 7,000+ servers, mcp.so has 19,700+, PulseMCP is hand-curated. We compare discovery, install, and security across the major catalogs.

AI Infrastructure

MCP Servers for SaaS Tools: A 2026 Registry Walkthrough for Voice Agent Teams

The public MCP registry crossed 9,400 servers in April 2026. Here is a curated walkthrough of the SaaS MCP servers CallSphere mounts in production, with OAuth 2.1 PKCE patterns.

Agentic AI

LangGraph State-Machine Architecture: A Principal-Engineer Deep Dive (2026)

How LangGraph's StateGraph, channels, and reducers actually work — with a working multi-step agent, eval hooks at every node, and the patterns that survive production.

Agentic AI

Multi-Agent Handoffs with the OpenAI Agents SDK: The Pattern That Actually Scales (2026)

Handoffs done right — when one agent should hand control to another, how to preserve context, and how to evaluate the handoff decision itself.

Agentic AI

Building Your First Agent with the OpenAI Agents SDK in 2026: A Hands-On Walkthrough

Step-by-step build of a working agent with the OpenAI Agents SDK — Agent class, tools, handoffs, tracing — plus an eval pipeline that catches regressions before merge.

Agentic AI

LangGraph Checkpointers in Production: Durable, Resumable Agents with Eval Replay

Use LangGraph's checkpointer to make agents resumable across crashes and human-in-the-loop pauses, then replay any checkpoint into your eval pipeline.