Learn how agents find other agents: a service registry as the Agent Card store, an ecosystem catalog with dynamic discovery and heartbeat, and routing patterns that choose agents based on capabilities, rating, and latency like Twilio.

In episode 15 we built observability: task lifecycle tracing and audit logs. All of that assumes we already know which agent is called. The earlier question — how to find the right agent among dozens or hundreds of agents? — we haven't answered.
In episode 3 we learned about the Agent Card for one-by-one discovery: the client fetches the card at the standard /.well-known/agent-card.json endpoint. That works at small scale. Once the number of agents grows, holding a list of URLs manually in every client won't be sustainable. We need a registry: one centralized place that stores, validates, and serves Agent Cards.
This episode's roadmap: we start with the problem of manual discovery, then build a service registry as the Agent Card store, learn dynamic discovery with heartbeat, look at an ecosystem catalog with hundreds of agents, and close with routing patterns for choosing the best agent.
At small scale, the common pattern is hard-coded URLs: the orchestrator keeps a dictionary of agent id to URL and exports it from configuration.
AGENTS = {
"pricing": "https://pricing.example.com/.well-known/agent-card.json",
"shipping": "https://shipping.example.com/.well-known/agent-card.json",
"translation": "https://translation.example.com/.well-known/agent-card.json",
}This approach is fragile. Every time a new agent appears, an address changes, or a version is deployed, all clients must be updated. Dead agents keep being called until errors surface. This is where a service registry replaces static configuration with dynamic discovery.
A service registry is a server that stores Agent Cards from many agents and provides APIs to register, search, and revoke. Some registry implementations provide a discovery module directly inside the SDK, for example the python_a2a.discovery module, which provides AgentRegistry, DiscoveryClient, and the enable_discovery helper.
Its flow is simple: an agent registers its card to the registry on startup, the registry stores and serves it, and clients query the registry to find agents.
Common endpoints a registry exposes:
POST /registry/register
POST /registry/unregister
GET /registry/agents
POST /registry/heartbeat
GET /a2a/agentsExample of running a registry and registering an agent:
from python_a2a.discovery import AgentRegistry, run_registry
registry = AgentRegistry(name="Registry Internal A2A")
run_registry(registry, host="0.0.0.0", port=8000)from python_a2a import AgentCard, A2AServer, run_server
from python_a2a.discovery import enable_discovery
agent_card = AgentCard(
name="kalkulator",
description="Agent perhitungan harga dan ongkir.",
url="http://localhost:8001",
version="1.0.0",
)
agent = A2AServer(agent_card=agent_card)
enable_discovery(agent, registry_url="http://localhost:8000")
run_server(agent, host="0.0.0.0", port=8001)SDK installation:
pip install python-a2aInfo
The concept of an "agent registry" can even be realized as an A2A agent itself — an AgentCard store that other agents call over the same protocol. That way, discovery no longer needs a special protocol outside the A2A ecosystem.
A registry is only useful if its data is accurate. Agents can die at any time, and a registry must not serve agents that are no longer healthy. Two mechanisms keep it accurate:
Clients run discovery to get the list of active agents, or check directly with curl http://localhost:8000/registry/agents:
from python_a2a.discovery import DiscoveryClient
client = DiscoveryClient(agent_card=None)
client.add_registry("http://localhost:8000")
agents = client.discover()
for agent in agents:
print(f"{agent.name} at {agent.url} — {agent.description}")Dynamic discovery lets the network topology change without changing client code. Add a new agent, start its heartbeat, and within seconds other agents find it. A retiring agent just stops its heartbeat and automatically disappears from discovery results.
Registries aren't only for internal use. The A2A ecosystem also has public registries holding agents from many organizations — catalogs that host hundreds of partners with verified AgentCards. Some public registries even display metrics like uptime and latency per agent.
Public registry models usually provide:
Example of a search pattern in a public registry:
curl "https://a2aregistry.org/api/agents?skill=translation"The presence of a public catalog changes how the ecosystem works. Agents from different organizations can find each other without one-by-one negotiation — just register in the registry, publish a complete card, and other agents searching for the same capabilities will find you.
Finding an agent is only half the journey. Once you have the candidate list, you must choose the best one for a given task. Common criteria:
Twilio's pattern for real-time routing is worth copying: pick the node with the lowest latency among the healthy ones, with automatic failover to the second candidate if the first times out. Because latency changes, measurements are taken periodically, not once at startup.
A simple scoring implementation combining the three criteria:
def score(candidate, task):
if task.get("capability") not in candidate["capabilities"]:
return -1
if candidate["health"] != "healthy":
return -1
s = 50.0
s += candidate["rating"] * 20
s += max(0.0, 200.0 - candidate["latency_ms"]) * 0.1
return s
def pick_agent(candidates, task):
return max(candidates, key=lambda c: score(c, task))Success
Twilio's latency-aware pattern is actually simpler than it looks: measure each node's latency periodically, pick the fastest one that's still healthy, and have failover ready. Combine it with capabilities and rating-based scoring, and your routing is good enough for multi-agent production.
A good pattern also adds sticky routing: a task that continues a previous task is forwarded to the same agent so session state stays consistent, unless that agent goes down. And when routing fails, falling back to the second candidate prevents a single point of failure.
In this episode we resolved the discovery and routing question. The service registry becomes a centralized Agent Card store with registration, search, and heartbeat APIs to keep it accurate. Public registries open an ecosystem catalog of hundreds of agents with verification and metrics. And routing patterns based on capabilities, rating, and latency — inspired by Twilio's latency-aware pattern — ensure each task lands on the most appropriate agent.
Here's the core takeaway:
Now agents can find and select each other dynamically. But the tasks sent are still simple patterns. What if one task needs to be split across many agents, wait for human approval, or be retried when it fails?
In the next episode, episode 17, we'll discuss Advanced Task Patterns: fan-out and fan-in orchestration between agents, human-in-the-loop workflows with the input-required state and resubmission, and state management like idempotency, retry, long-running tasks, and checkpoint-resume. See you there!