apcore-a2a (Python)
What is apcore-a2a?
apcore-a2a is the A2A (Agent-to-Agent) protocol adapter for the apcore ecosystem.
It solves a common problem: you've built AI capabilities with apcore modules, but you need them to talk to other AI agents over a standard protocol. apcore-a2a bridges that gap — it reads your existing module metadata (schemas, descriptions, examples) and automatically exposes them as a standards-compliant A2A server. No hand-written Agent Cards, no JSON-RPC boilerplate, no manual task lifecycle management.
In short: apcore modules + apcore-a2a = a fully functional A2A agent, ready to be discovered and invoked by any A2A-compatible client.
Also available in: TypeScript | Rust
Features
- One-call server — launch a compliant A2A server with
serve(registry) - Automatic Agent Card —
/.well-known/agent-card.jsongenerated from module metadata (the 0.3 alias/.well-known/agent.jsonis also served) - Skill mapping — apcore modules become A2A Skills with names, descriptions, tags, and examples;
metadata["display"]["a2a"]overrides surface-facing fields (§5.13) - Full task lifecycle — submitted, working, completed, failed, canceled, input-required
- SSE streaming —
message/streamwith real-time status and artifact updates - Push notifications — optional webhook delivery of task state changes
- JWT authentication — tokens bridged to apcore's Identity context
- A2A Explorer UI — browser UI for discovering and testing skills
- Built-in client —
A2AClientfor calling remote A2A agents - Pluggable storage — swap in Redis or PostgreSQL via the
TaskStoreprotocol - Observability —
/health,/metricsendpoints, structured logging - Dynamic registration — add/remove modules at runtime without restart
Requirements
- Python >= 3.11
apcore>= 0.31.0apcore-toolkit>= 0.12.0 (theopenapiextra additionally needsapcore-toolkit[http-proxy])
For Users: Getting Started
Installation
pip install apcore-a2a
Expose your modules as an A2A Agent
If you already have apcore modules, a few lines turn them into a discoverable agent:
from apcore import Executor, Registry
from apcore_a2a import serve
registry = Registry(extensions_dir="./extensions")
registry.discover()
serve(Executor(registry)) # Starts on http://0.0.0.0:8000
Your agent is now live at http://localhost:8000/.well-known/agent-card.json (the 0.3 alias /.well-known/agent.json is also served).
Serve an OpenAPI document instead
No apcore project required — point it at an OpenAPI 3.0/3.1 document and every operation becomes an A2A Skill, proxied over HTTP to the API that published it:
pip install "apcore-a2a[openapi]"
apcore-a2a serve --from-openapi https://petstore3.swagger.io/api/v3/openapi.json \
--openapi-prefix petstore
Or programmatically:
from apcore_a2a import openapi_backend, serve
registry = openapi_backend(
"https://petstore3.swagger.io/api/v3/openapi.json",
prefix="petstore",
)
serve(registry)
Try the Examples
Run all 5 example modules (3 class-based + 2 binding YAML) with the Explorer UI:
PYTHONPATH=./examples/binding_demo python examples/run.py
Open http://127.0.0.1:8000/explorer/ to browse skills, send messages, and stream responses.
See examples/README.md for more options (CLI, binding-only, JWT auth).
Call a remote A2A Agent
Use the built-in client to discover and invoke any A2A-compliant agent:
import asyncio
from apcore_a2a import A2AClient
async def main():
async with A2AClient("http://remote-agent:8000") as client:
# Discover what the agent can do
card = await client.discover()
print(f"Agent: {card['name']}, Skills: {len(card['skills'])}")
# Send a message (route to a skill via metadata.skillId)
message = {"role": "user", "parts": [{"kind": "text", "text": "Hello!"}]}
task = await client.send_message(
message,
metadata={"skillId": "my.skill"},
)
print(f"Result: {task['status']['state']}")
# Or stream the response
async for event in client.stream_message(message, metadata={"skillId": "my.skill"}):
print(event)
asyncio.run(main())
Add authentication
from apcore_a2a import serve
from apcore_a2a.auth.jwt import JWTAuthenticator, ClaimMapping
auth = JWTAuthenticator(
key="your-secret-key",
algorithms=["HS256"],
issuer="https://auth.example.com",
audience="my-agent",
claim_mapping=ClaimMapping(
id_claim="sub",
type_claim="type",
roles_claim="roles",
attrs_claims=["org", "dept"],
),
require_claims=["sub"],
)
serve(registry, auth=auth)
For Developers: API Reference
serve()
Blocking call — starts uvicorn and serves until SIGINT/SIGTERM.
from apcore_a2a import serve
serve(
registry_or_executor, # apcore Registry or Executor
*,
host="0.0.0.0",
port=8000,
name=None, # Agent name (fallback: registry config)
description=None, # Agent description
version=None, # Agent version
url=None, # Public URL (default: f"http://{host}:{port}")
auth=None, # Authenticator instance
task_store=None, # TaskStore instance (default: InMemoryTaskStore)
cors_origins=None, # List of allowed CORS origins
push_notifications=False,
explorer=False, # Enable A2A Explorer UI
explorer_prefix="/explorer",
cancel_on_disconnect=True,
shutdown_timeout=30,
execution_timeout=300,
log_level=None,
metrics=False, # Enable /metrics endpoint
sys_modules=False, # Register apcore sys.* modules (requires executor.use())
)
async_serve()
Returns the ASGI app without starting a server — use for embedding in larger applications.
from apcore_a2a import async_serve
app = await async_serve(registry_or_executor, **kwargs)
# app is a Starlette ASGI application
TaskStore
Default in-memory task store. Implement the TaskStore protocol for persistent backends (Redis, PostgreSQL, etc.).
from apcore_a2a.storage import InMemoryTaskStore
store = InMemoryTaskStore()
serve(registry, task_store=store)
Architecture
apcore-a2a acts as a thin protocol layer on top of apcore. The mapping is straightforward:
| A2A Concept | apcore Mapping |
|---|---|
| Agent Card | Derived from Registry configuration |
| Skill id | module_id |
| Skill name | metadata["display"]["a2a"]["alias"] or humanized module_id |
| Skill desc | metadata["display"]["a2a"]["description"] or module.description |
| Skill tags | metadata["display"]["tags"] or module.tags |
| Task | Managed execution of Executor.call_async() |
| Streaming | Wrapped Executor.stream() via SSE |
| Security | Bridged to apcore's Identity context |
Contributing
git clone https://github.com/aiperceivable/apcore-a2a-python.git
cd apcore-a2a-python
pip install -e ".[dev]"
pytest
Documentation
License
Apache 2.0 — see LICENSE.
Release files for apcore-a2a 0.8.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| apcore_a2a-0.8.0.tar.gz | 154.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| apcore_a2a-0.8.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 234.1 kB
Release files / apcore_a2a-0.8.0.tar.gz
| Download URL | apcore_a2a-0.8.0.tar.gz |
|---|---|
| Size | 154.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
685fec697133ed72efea5d54a518abde40d2af4b01c1355b62337a225c365388
|
|
BLAKE2b-256 checksum How to use checksums |
d41c135e423692ecf0169e16a2265546315058722490999924ce49a0d70e3234
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.10
|
Release files / apcore_a2a-0.8.0-py3-none-any.whl
| Download URL | apcore_a2a-0.8.0-py3-none-any.whl |
|---|---|
| Size | 80.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a87e16c9c4eceac3df4217aea52eee36128794338eb56056995f6940ea68a4cf
|
|
BLAKE2b-256 checksum How to use checksums |
d691c79a07907678e1f5e9794843d38ace153018a1130cdaf8b1da1563741b62
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.10
|