Skip to main content

Server and client library for A2A agent discovery via heartbeat registration

Project description

oo-a2a-registry

Fast and lean AI agent registry implementing the A2A protocol for agent discovery via heartbeat-based registration.

Supports A2A v0.3 (top-level url field, agent.json discovery) and A2A v1.0 (supportedInterfaces, agent-card.json discovery).

Overview

Component What it does
Server Exposes GET /.well-known/agents returning live A2A agent cards. Accepts POST /registry/heartbeat from clients. Verifies agents by fetching their well-known card (agent-card.json or agent.json). Evicts agents that miss more than 3 consecutive heartbeats.
Client Sends the agent's AgentCard to the registry on a configurable interval (default 60 s) as a background task.

Installation

pip install oo-a2a-registry                   # client + server (no ASGI server)
pip install "oo-a2a-registry[server]"         # includes uvicorn

Quick start

Standalone registry server

import uvicorn
from a2a_registry import AgentRegistryServer

server = AgentRegistryServer()
app = server.create_app()

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)

Mount into an existing FastAPI app

from fastapi import FastAPI
from a2a_registry import AgentRegistryServer

app = FastAPI()
registry = AgentRegistryServer()
registry.setup(app)          # adds /.well-known/agents and /registry/heartbeat

Agent client — A2A v0.3

import asyncio
from a2a_registry import AgentCard, RegistryClient

card = AgentCard(
    name="My Agent",
    url="http://my-agent:8080",   # agent must serve /.well-known/agent.json
    version="1.0.0",
)

async def main():
    async with RegistryClient("http://registry:8000", card, interval=30) as client:
        await asyncio.sleep(300)   # heartbeats run in the background

asyncio.run(main())

Agent client — A2A v1.0

import asyncio
from a2a_registry import AgentCard, AgentInterface, RegistryClient

card = AgentCard(
    name="My Agent",
    supportedInterfaces=[
        AgentInterface(
            url="http://my-agent:8080/jsonrpc",
            protocolBinding="json-rpc/2.0",
            protocolVersion="1.0",
        )
    ],
    version="1.0.0",
)

async def main():
    async with RegistryClient("http://registry:8000", card, interval=30) as client:
        await asyncio.sleep(300)

asyncio.run(main())

Send a single heartbeat

client = RegistryClient("http://registry:8000", card)
result = await client.send_once()
print(result.verified)

Examples

Runnable examples are in the examples/ directory:

File Description
registry_server.py Standalone registry on port 8000
hello_world_agent.py Hello World agent (A2A v0.3) on port 8001
hello_world_agent_v1.py Hello World agent (A2A v1.0) on port 8002
pip install "oo-a2a-registry[server]"
python examples/registry_server.py &
python examples/hello_world_agent.py &
curl -s http://localhost:8000/.well-known/agents | python -m json.tool

A2A version compatibility

The registry accepts agent cards from both protocol versions and auto-detects the discovery path.

A2A v0.3 A2A v1.0
Agent URL field top-level url supportedInterfaces[].url
Discovery path /.well-known/agent.json /.well-known/agent-card.json
Protocol version protocolVersion (card-level) supportedInterfaces[].protocolVersion

Cards without a top-level url must provide supportedInterfaces. The registry uses AgentCard.effective_url as the canonical key (returns url for v0.3, supportedInterfaces[0].url for v1.0).

When verifying an agent the registry tries the preferred discovery path first (based on which fields the heartbeat card has) and falls back to the other automatically.

How it works

Agent                          Registry Server
  |                                  |
  |-- POST /registry/heartbeat ----> |
  |   { agent_card, interval }       |
  |                                  |-- GET {origin}/.well-known/agent-card.json  (v1.0)
  |                                  |       — or —
  |                                  |-- GET {origin}/.well-known/agent.json       (v0.3)
  |                                  |   (once, to verify reachability)
  |                                  |-- store as "available"
  |<-- { status: "ok", verified } -- |
  |                                  |
  |-- POST /registry/heartbeat ----> |  (every `interval` seconds)
  |                                  |-- refresh last_heartbeat timestamp
  |                                  |
  |   (silence > 3 × interval)       |-- evict from registry

GET /.well-known/agents returns only agents with status == available.

Custom storage backend

Implement RegistryProvider to plug in Redis, a database, etc.:

from a2a_registry.server import RegistryProvider

class RedisRegistryProvider(RegistryProvider):
    async def upsert(self, agent_card, interval): ...
    async def get(self, agent_url): ...
    async def mark_verified(self, agent_url): ...
    async def unregister(self, agent_url): ...
    async def list_available(self): ...
    async def get_stale(self, multiplier=3): ...

server = AgentRegistryServer(provider=RedisRegistryProvider())

Configuration

Parameter Default Description
stale_multiplier 3 Evict agent after multiplier × interval seconds of silence
cleanup_interval 30 Seconds between cleanup sweeps
fetch_timeout 10.0 Timeout for fetching remote agent cards
interval (client) 60 Heartbeat interval in seconds

Development

pip install -e ".[dev]"
pytest

Publishing

pip install build twine
python -m build
twine upload dist/*

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

oo_a2a_registry-0.1.0.tar.gz (14.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

oo_a2a_registry-0.1.0-py3-none-any.whl (11.8 kB view details)

Uploaded Python 3

File details

Details for the file oo_a2a_registry-0.1.0.tar.gz.

File metadata

  • Download URL: oo_a2a_registry-0.1.0.tar.gz
  • Upload date:
  • Size: 14.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for oo_a2a_registry-0.1.0.tar.gz
Algorithm Hash digest
SHA256 20b5ca4b4fda94ce6d242f5b1ea46f6ef72150846ae118630adef1d43a5fbe89
MD5 ea0a50e1c3c3ec88cfeb64e7032f2c05
BLAKE2b-256 11a859350003762a470561fe4ac1c14f0af1fd628f6844fad8498812f56fe630

See more details on using hashes here.

File details

Details for the file oo_a2a_registry-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for oo_a2a_registry-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c6b0be09286a5c41ad5875c47c5691808b078936852029142f6be23622898121
MD5 2d4c60b3b15a858bbc382437ed98c141
BLAKE2b-256 3014ed61da952a5abdddb9e70baeb495501d1e7b454b984de91ac188e881dba6

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page