Skip to main content

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: a ServerCallContextBuilder subclass. Pass to a2a.server.routes.create_jsonrpc_routes. Propagates the verified bearer token onto ServerCallContext.state["access_token"] so executors can read it for delegated downstream calls.
  • build_agent_card_from_config(config): produces a 1.x protobuf AgentCard. Pass to a2a.server.routes.create_agent_card_routes and a2a.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.json with 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: provides AuthenticationMiddleware + KeycardAuthBackend and 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


Download files

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

Source Distribution

keycardai_a2a-0.3.0.tar.gz (21.6 kB view details)

Uploaded Source

Built Distribution

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

keycardai_a2a-0.3.0-py3-none-any.whl (15.8 kB view details)

Uploaded Python 3

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

Hashes for keycardai_a2a-0.3.0.tar.gz
Algorithm Hash digest
SHA256 8ca6ad44330345b121da78bc162423714476916819b91eed4504d3a4dd8876b5
MD5 68825bf37f44eea33fab211f2832b020
BLAKE2b-256 de7e5f7f2bc3e27278a7a0804510cf101f4275e568054c5c9200be5f2ee61c76

See more details on using hashes here.

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

Hashes for keycardai_a2a-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e98bfc6da2219be91c4a6fdf4f6d6fa3dfbd3b0a09f6c1851bd1a6fcfb989baf
MD5 6e9ec082f722a72b503e0159b69cb1ae
BLAKE2b-256 eb8ac978f8b713c70a0c1706c6c2cece9ca251f389c15cc14e5426fc896c4885

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