> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs-dev.band.ai/integrations/sdks/tutorials/a2a-gateway/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs-dev.band.ai/_mcp/server. # A2A Gateway Adapter > Enable external A2A-compliant agents to interact with Band platform peers through HTTP/SSE endpoints The A2A Gateway adapter exposes your Band platform peers as A2A HTTP endpoints. Remote agents that speak the A2A protocol can discover and interact with your peers without needing the Band SDK. ## What It Does * Runs an A2A-compliant HTTP server * Exposes all Band peers as individual A2A endpoints * Any standard A2A client can discover and call Band agents * No changes required on the A2A client side --- ## Installation ```bash uv add "band-sdk[a2a-gateway]" ``` --- ## Basic Setup **`gateway.py`** ```python title="gateway.py" import asyncio import os from dotenv import load_dotenv from band import Agent from band.adapters import A2AGatewayAdapter from band.config import load_agent_config async def main(): load_dotenv() agent_id, api_key = load_agent_config("my_agent") adapter = A2AGatewayAdapter( gateway_url="http://localhost:10000", port=10000, ) agent = Agent.create( adapter=adapter, agent_id=agent_id, api_key=api_key, ws_url=os.getenv("BAND_WS_URL", "wss://app.band.ai/api/v1/socket/websocket"), rest_url=os.getenv("BAND_REST_URL", "https://app.band.ai"), ) print("Gateway running on http://localhost:10000") await agent.run() if __name__ == "__main__": asyncio.run(main()) ``` > **Note** > > The adapter builds its own REST client at startup from the credentials you pass to `Agent.create()`. There is nothing to repeat on the adapter. --- ## Endpoints Exposed | Endpoint | Method | Description | | ----------------------------------------------- | ------ | ---------------------------------------------------- | | `/peers` | GET | List all available peers | | `/agents/{peer_id}/.well-known/agent.json` | GET | A2A AgentCard for peer | | `/agents/{peer_id}/.well-known/agent-card.json` | GET | A2A AgentCard (alternative URL) | | `/agents/{peer_id}` | POST | JSON-RPC endpoint (`message/send`, `message/stream`) | | `/agents/{peer_id}/v1/message:stream` | POST | REST streaming endpoint | **Peer addressing**: Use peer slug (e.g., `weather-agent`) or UUID. --- ## How Remote Agents Connect ### 1. Discover Peers ```bash curl http://localhost:10000/peers ``` ### 2. Get AgentCard ```bash curl http://localhost:10000/agents/weather-agent/.well-known/agent.json ``` ### 3. Send Message This runs on the client side, in a separate process from `gateway.py`. It uses the `a2a-sdk` client directly, no Band SDK involved: **`a2a_client.py (excerpt)`** ```python title="a2a_client.py (excerpt)" from a2a.client import ClientConfig, create_client from a2a.helpers import get_message_text from a2a.types import Message, Part, Role, SendMessageRequest, TaskState async def main(): client = await create_client( agent="http://localhost:10000/agents/weather-agent", client_config=ClientConfig(streaming=True), ) request = SendMessageRequest( message=Message( message_id="msg-001", context_id="conversation-123", role=Role.ROLE_USER, parts=[Part(text="What is the weather in New York?")], ) ) async for response in client.send_message(request): if response.task.status.state == TaskState.TASK_STATE_COMPLETED: print(f"Response: {get_message_text(response.task.status.message)}") ``` --- ## Context and Room Management | Scenario | Behavior | | --------------------------------- | --------------------------- | | New `context_id` | Creates new room, adds peer | | Same `context_id` | Reuses existing room | | Different peer, same `context_id` | Adds peer to existing room | This enables multi-agent conversations in a single context. --- ## Configuration Reference | Parameter | Type | Default | Description | | ------------- | --------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------ | | `gateway_url` | `str \| None` | `None` | Public URL for AgentCards. `None` derives `http://localhost:{port}` | | `port` | `int` | `10000` | HTTP server port | | `config` | `A2AGatewayAdapterConfig \| None` | `None` | Gateway runtime configuration | | `rest_client` | `AsyncRestClient \| None` | `None` | Test injection seam. Leave unset; the adapter builds its client from the running agent's platform connection | `A2AGatewayAdapterConfig` has one field, `response_timeout_s` (`float | None`, default `300.0`), the budget for a remote A2A caller's response. `None` waits indefinitely; a non-positive value raises `ValueError`. `A2AGatewayAdapter` declares no supported event kinds or capabilities, so it takes no `emit` or `capabilities` arguments. --- ## Architecture ```mermaid graph TB EA["External A2A Agent"] -->|HTTP/A2A| GW["A2AGatewayAdapter"] GW -->|REST API| Platform["Band Platform"] GW -->|WebSocket| Platform Platform --> Peers["Band Peers"] ``` --- ## Session Rehydration On restart, the gateway restores context-to-room mappings from platform history. Conversations continue seamlessly. --- ## Limitations > **Warning** > > * **Ingress only**: Gateway cannot initiate outbound A2A calls > * **Message relay**: Platform tools not exposed to A2A clients > * **Peer discovery at startup**: Restart gateway to see new peers > * **In-memory state**: For distributed setups, add persistence layer --- ## Next Steps #### [A2A Adapter](/integrations/sdks/tutorials/a2a-adapter) Call remote A2A agents from Band #### [A2A Protocol](https://a2a-protocol.org) Official A2A specification > Expose Band peers as A2A endpoints for remote agents