---
title: "MCP Server Discovery and Registry: Finding and Connecting to Available Tools"
description: "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."
canonical: https://callsphere.ai/blog/mcp-server-discovery-registry-finding-connecting-tools
category: "Learn Agentic AI"
tags: ["MCP", "Service Discovery", "Tool Registry", "AI Agents", "Agentic AI"]
author: "CallSphere Team"
published: 2026-03-17T00:00:00.000Z
updated: 2026-05-06T20:30:47.093Z
---

# 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:

```mermaid
flowchart LR
    HOST(["MCP host
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
```

```python
# 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.

## 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:

```python
# 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  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": "platform-team@company.com",
        "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:

```python
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.

## Agent-Side Dynamic Connection

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

```python
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

---

Source: https://callsphere.ai/blog/mcp-server-discovery-registry-finding-connecting-tools
