A2A delegation SDK for Keycard: agent-to-agent token exchange, agent service hosting, and service discovery.
Project description
keycardai-a2a
Keycard auth primitives for a2a-sdk 1.x agent services. This package is glue, not a parallel server abstraction. Customers compose these primitives with a2a-sdk's standard route factories and request handler in their own Starlette / FastAPI app to get bearer token verification, OAuth metadata discovery, and OAuth 2.0 token exchange (RFC 8693) for downstream delegated calls.
Preview. This package is pre-1.0. APIs may change between minor versions.
What's in here
Server-side wiring:
KeycardServerCallContextBuilder: aServerCallContextBuildersubclass. Pass toa2a.server.routes.create_jsonrpc_routes. Propagates the verified bearer token ontoServerCallContext.state["access_token"]so executors can read it for delegated downstream calls.build_agent_card_from_config(config): produces a 1.x protobufAgentCard. Pass toa2a.server.routes.create_agent_card_routesanda2a.server.request_handlers.DefaultRequestHandler.
For the auth backend itself, use keycardai.starlette.KeycardAuthBackend(verifier, require_authentication=True) on the JSONRPC mount. The kwarg flips the default mixed-route behavior to "every path on this mount needs auth," which matches the JSONRPC dispatcher's lack of a per-route gate.
Outbound delegation:
DelegationClient,DelegationClientSync: server-to-server token exchange and JSONRPC invocation against another agent service.
Inbound discovery:
ServiceDiscovery: query a remote agent service's.well-known/agent-card.jsonwith caching.
Configuration:
AgentServiceConfig: service identity + Keycard credentials + agent card metadata.
Installation
pip install keycardai-a2a
This pulls in keycardai-oauth, keycardai-starlette, a2a-sdk[http-server]>=1.0.
Quick start
You already have an a2a-sdk server. Add the Keycard-protected A2A mount to your existing Starlette / FastAPI app:
from a2a.server.request_handlers import DefaultRequestHandler
from a2a.server.routes import create_agent_card_routes, create_jsonrpc_routes
from a2a.server.tasks import InMemoryTaskStore
from starlette.middleware import Middleware
from starlette.middleware.authentication import AuthenticationMiddleware
from starlette.routing import Mount
from keycardai.a2a import (
AgentServiceConfig,
KeycardServerCallContextBuilder,
build_agent_card_from_config,
)
from keycardai.oauth.server.credentials import ClientSecret
from keycardai.starlette import AuthProvider, KeycardAuthBackend, keycard_on_error
from keycardai.starlette.routers.metadata import (
well_known_authorization_server_route,
well_known_protected_resource_route,
)
config = AgentServiceConfig(
service_name="My Agent",
client_id="...",
client_secret="...",
identity_url="https://my-agent.example.com",
zone_id="your-zone-id",
capabilities=["chat"],
)
auth_provider = AuthProvider(
zone_url=config.auth_server_url,
server_name=config.service_name,
server_url=config.identity_url,
application_credential=ClientSecret((config.client_id, config.client_secret)),
)
verifier = auth_provider.get_token_verifier()
agent_card = build_agent_card_from_config(config)
request_handler = DefaultRequestHandler(
agent_executor=YourExecutor(), # subclass of a2a.server.agent_execution.AgentExecutor
task_store=InMemoryTaskStore(),
agent_card=agent_card,
)
# Add these routes to your existing Starlette / FastAPI app:
your_app.routes.extend(create_agent_card_routes(agent_card=agent_card))
your_app.routes.append(well_known_protected_resource_route(
issuer=config.auth_server_url,
resource="/.well-known/oauth-protected-resource{resource_path:path}",
))
your_app.routes.append(well_known_authorization_server_route(
issuer=config.auth_server_url,
resource="/.well-known/oauth-authorization-server{resource_path:path}",
))
your_app.routes.append(Mount(
"/a2a",
routes=create_jsonrpc_routes(
request_handler=request_handler,
rpc_url="/jsonrpc",
context_builder=KeycardServerCallContextBuilder(),
),
middleware=[
Middleware(
AuthenticationMiddleware,
backend=KeycardAuthBackend(verifier, require_authentication=True),
on_error=keycard_on_error,
),
],
))
Inside your AgentExecutor.execute(self, context, event_queue), read the bearer token via context.call_context.state["access_token"] and use it as the subject token in keycardai-oauth's TokenExchangeRequest for downstream API calls.
For a runnable greenfield example (no existing app), see examples/keycard_protected_server/.
Relationship to other Keycard packages
keycardai-oauth: OAuth 2.0 primitives used for token exchange and PKCE.keycardai-starlette: providesAuthenticationMiddleware+KeycardAuthBackendand the OAuth metadata route helpers used here.keycardai-mcp: sister package for MCP server protection. Same auth shape, different protocol.
History
This package was extracted from the original keycardai-agents package (KEP: Decompose keycardai-agents). The PKCE user-login client moved to keycardai-oauth; the CrewAI integration moves to a forthcoming keycardai-crewai; the keycardai-agents source directory is being archived.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file keycardai_a2a-0.3.0.tar.gz.
File metadata
- Download URL: keycardai_a2a-0.3.0.tar.gz
- Upload date:
- Size: 21.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8ca6ad44330345b121da78bc162423714476916819b91eed4504d3a4dd8876b5
|
|
| MD5 |
68825bf37f44eea33fab211f2832b020
|
|
| BLAKE2b-256 |
de7e5f7f2bc3e27278a7a0804510cf101f4275e568054c5c9200be5f2ee61c76
|
File details
Details for the file keycardai_a2a-0.3.0-py3-none-any.whl.
File metadata
- Download URL: keycardai_a2a-0.3.0-py3-none-any.whl
- Upload date:
- Size: 15.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e98bfc6da2219be91c4a6fdf4f6d6fa3dfbd3b0a09f6c1851bd1a6fcfb989baf
|
|
| MD5 |
6e9ec082f722a72b503e0159b69cb1ae
|
|
| BLAKE2b-256 |
eb8ac978f8b713c70a0c1706c6c2cece9ca251f389c15cc14e5426fc896c4885
|