Run an A2A inbound task listener that lets this OpenClaw instance receive tasks from other agents via the A2A API Gateway. Use when: (1) starting/stopping th...
---
name: a2a-server
description: 'Run an A2A inbound task listener that lets this OpenClaw instance receive tasks from other agents via the A2A API Gateway. Use when: (1) starting/stopping the A2A listener service, (2) receiving inbound A2A tasks from other agents, (3) checking if the listener is running. NOT for: sending tasks outbound (use a2a-client), registering this agent (use a2a-register).'
metadata: {"clawdbot":{"emoji":"📡","requires":{"anyBins":["python3"]},"os":["linux","darwin"]}}
---
# A2A Server — Inbound Task Listener
Run an A2A protocol listener that allows this OpenClaw instance to receive tasks routed from the A2A API Gateway. The listener runs as a background HTTP service.
## Sidecar Principle
This skill is strictly an **OpenClaw sidecar** — it receives tasks FROM the A2A API Gateway in whatever format the gateway sends them. The gateway's A2A protocol is the contract; OpenClaw's listener just speaks it. Never assume what the sending system looks like — it could be any agent that routes tasks through the gateway. The listener implements the gateway's expected endpoints as-is.
## Configuration
The listener reads configuration from a shared `a2a.conf` file (located in the `a2a-client` skill directory), with auto-detection fallbacks for local settings.
**Priority order:** CLI flags → env vars → `a2a.conf` → auto-detected defaults
| Setting | Auto-detected Default | Description |
|---------|-----------------------|-------------|
| Port | `8100` | Listen port (`LISTENER_PORT`) |
| Bind Address | Tailscale IP or first NIC | Local bind address (`BIND_ADDR`) |
| Agent Slug | `hostname -s` (lowercase) | Agent identifier (`AGENT_SLUG`) |
| Agent Name | Slug (capitalized) | Display name (`AGENT_NAME`) |
| Agent URL | `http://{bind_addr}:{port}` | Agent endpoint (`AGENT_URL`) |
| Capabilities | `chat,code,research` | Comma-separated (`AGENT_CAPABILITIES`) |
| Auth Type | `bearer` | Auth method (`AGENT_AUTH_TYPE`) |
| API Key | *empty* | Bearer token — **if empty, auth checks are disabled** (`A2A_GATEWAY_API_KEY`) |
| OpenClaw Command | *auto* | Shell command template; `{message}` and `{session_id}` placeholders (`A2A_OPENCLAW_COMMAND`) |
| OpenClaw URL | *empty* | HTTP API URL for chat completions (`A2A_OPENCLAW_URL`) |
| OpenClaw URL API Key | *empty* | Bearer token for HTTP API (`A2A_OPENCLAW_URL_API_KEY`) |
| OpenClaw Timeout | `60` | Max seconds to wait for response (`A2A_OPENCLAW_TIMEOUT`) |
Run `a2a-register/a2a-setup.sh` to configure interactively, or set env vars / create `a2a-client/a2a.conf`.
## When to Use
- **Start the listener** — When this OpenClaw instance needs to receive inbound A2A tasks
- **Stop the listener** — When shutting down or pausing inbound task reception
- **Check listener status** — When verifying the A2A service is running
## Endpoints
The listener (`a2a-listener.py`) handles these routes:
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/health` | Health check — returns `{"status": "ok", "agent": "<slug>"}` |
| `GET` | `/v1/a2a/agents/{slug}` | Returns the agent card for this OpenClaw instance |
| `POST` | `/v1/a2a/tasks/send` | Receives an inbound A2A task (requires Bearer auth if API key is set) |
## Auth
Inbound tasks (`POST /v1/a2a/tasks/send`) require a Bearer token matching the configured `A2A_GATEWAY_API_KEY`. If no API key is configured, auth checks are disabled (with a warning at startup). Health checks and agent card lookups are unauthenticated.
## Tools
### start.sh — Start the Listener
```bash
./start.sh [--port PORT] [--bind ADDR]
```
Starts the A2A listener in the background. All other settings are read from `a2a.conf` or env vars.
```bash
# Start with defaults (from a2a.conf or auto-detected)
./start.sh
# Custom port and bind address
./start.sh --port 8200 --bind 0.0.0.0
```
Saves the PID to `a2a-listener.pid` for management. Logs to `a2a-listener.log`.
### stop.sh — Stop the Listener
```bash
./stop.sh
```
Gracefully stops the A2A listener using the PID file.
### a2a-listener.py — The Listener Process
Python HTTP server that implements the A2A protocol endpoints. Started by `start.sh`. Can also be run directly:
```bash
# Run directly (foreground)
python3 a2a-listener.py
# With custom settings via env vars
LISTENER_PORT=8200 BIND_ADDR=0.0.0.0 A2A_GATEWAY_API_KEY=your-key python3 a2a-listener.py
```
All configuration is loaded from `a2a.conf`, env vars, or auto-detected — no hardcoded values.
## OpenClaw Invocation — Real Responses
When an inbound task arrives, the listener invokes the local OpenClaw instance to produce a real response. The invocation method is configurable, with automatic fallback:
**Priority:** `A2A_OPENCLAW_COMMAND` → `A2A_OPENCLAW_URL` → auto-detect `openclaw` CLI → error
| Env Var | Description | Example |
|---------|-------------|--------|
| `A2A_OPENCLAW_COMMAND` | Shell command template; `{message}` and `{session_id}` are replaced | `openclaw agent -m "{message}" --session-id {session_id} --json` |
| `A2A_OPENCLAW_URL` | HTTP API URL to POST the task to | (any HTTP chat/completions endpoint) |
| `A2A_OPENCLAW_URL_API_KEY` | Bearer token for the HTTP URL | (optional, only used with `A2A_OPENCLAW_URL`) |
| `A2A_OPENCLAW_TIMEOUT` | Max seconds to wait for a response | `60` (default) |
### Auto-Detection
If neither `A2A_OPENCLAW_COMMAND` nor `A2A_OPENCLAW_URL` is set, the listener checks if the `openclaw` CLI is on `PATH`. If found, it runs:
```bash
openclaw agent -m "<message>" --session-id <session_id> --json
```
This uses OpenClaw's built-in agent runtime and returns real AI responses. The `--session-id` flag ensures continuity within a conversation thread.
### Response Format
On success:
```json
{"id": "task-123", "status": "completed", "result": {"kind": "text", "content": "<actual AI response>"}}
```
On failure (invocation error, timeout, etc.):
```json
{"id": "task-123", "status": "failed", "result": {"kind": "text", "content": "<error message>"}}
```
### Configuration Error
If no invocation method is available at all, the task response will have `status: "failed"` with a message explaining how to configure OpenClaw access.
## Typical Workflow
1. **Configure** → Run `a2a-register/a2a-setup.sh` or create `a2a.conf`
2. **Register** → Use the `a2a-register` skill to register this instance in the A2A API Gateway
3. **Start** → `./start.sh` to begin listening for inbound tasks
4. **Verify** → `curl http://YOUR_IP:8100/health` to confirm it's running
5. **Receive** — The gateway routes tasks to this listener automatically
6. **Stop** → `./stop.sh` when done
don't have the plugin yet? install it then click "run inline in claude" again.
added explicit decision points for auth modes and openclaw invocation priority, documented edge cases (timeouts, malformed requests, missing dependencies), clarified configuration priority order in inputs, separated procedure into 7 numbered steps with explicit inputs and outputs, and added outcome signal section with verification commands.
run an a2a protocol listener that allows this openclaw instance to receive tasks routed from the a2a api gateway. the listener runs as a background http service and responds to inbound task requests by invoking the local openclaw instance. use this skill when: (1) starting or stopping the a2a listener service on this agent, (2) receiving inbound a2a tasks from other agents through the gateway, or (3) checking if the listener is running and healthy. do not use this for sending tasks outbound (use a2a-client instead) or for registering this agent with the gateway (use a2a-register instead).
environment and configuration
a2a.conf file in the a2a-client skill directory (optional; auto-detected defaults apply if missing)--port PORT, --bind ADDR (optional overrides for start.sh)LISTENER_PORT (default: 8100)BIND_ADDR (default: tailscale ip or first network interface)AGENT_SLUG (default: hostname lowercase)AGENT_NAME (default: slug capitalized)AGENT_URL (default: http://{bind_addr}:{port})AGENT_CAPABILITIES (default: chat,code,research)AGENT_AUTH_TYPE (default: bearer)A2A_GATEWAY_API_KEY (default: empty; auth disabled if not set)A2A_OPENCLAW_COMMAND (default: auto-detected)A2A_OPENCLAW_URL (default: empty)A2A_OPENCLAW_URL_API_KEY (default: empty)A2A_OPENCLAW_TIMEOUT (default: 60 seconds)external connections
A2A_GATEWAY_API_KEY if auth is enabled)configuration priority order cli flags (highest) → environment variables → a2a.conf file → auto-detected defaults (lowest)
input: system environment
python3 --versionoutput: confirmation that prerequisites are met or error message if python3 is missing
input: user choices for agent metadata and openclaw invocation
a2a-register/a2a-setup.sh to create or edit a2a.conf interactively, orexport LISTENER_PORT=8100), ora2a-client/a2a.conf by hand with key=value pairsoutput: a2a.conf file with configuration, or environment variables set in the current shell session
input: listener port (from config or cli flag), bind address (from config or cli flag), all other settings from a2a.conf or env vars
./start.sh [--port PORT] [--bind ADDR]output: a2a-listener.pid file containing the running process id, a2a-listener.log file with startup logs, listener process running in background
input: none (checking local state)
curl http://{BIND_ADDR}:{LISTENER_PORT}/health to test the health endpoint{"status": "ok", "agent": "<agent_slug>"}kill -0 $(cat a2a-listener.pid) 2>/dev/null && echo "running" || echo "stopped"output: http 200 response with health json, or error message if listener is not responding
input: none (this step is part of the a2a-register skill workflow)
output: agent registered in the gateway; subsequent inbound tasks will be routed to this listener
input: inbound http POST to /v1/a2a/tasks/send with json body containing task details and bearer token in authorization header
A2A_GATEWAY_API_KEY if configuredA2A_OPENCLAW_TIMEOUT seconds for a responseoutput: http 200 response json with task result, or http 401 (auth failed), 400 (invalid request), 500 (invocation error)
input: none (uses a2a-listener.pid)
./stop.shoutput: listener process terminated, a2a-listener.pid file deleted, log message confirming shutdown
if api key is configured (a2a_gateway_api_key is set)
else (a2a_gateway_api_key is empty)
for openclaw invocation, priority order is:
A2A_OPENCLAW_COMMAND is set, use it as a shell command template (replace {message} and {session_id})A2A_OPENCLAW_URL is set, http post the task json to that url (include bearer token from A2A_OPENCLAW_URL_API_KEY if set)openclaw binary is found on PATH, run openclaw agent -m "<message>" --session-id <session_id> --jsonif openclaw invocation times out (exceeds a2a_openclaw_timeout)
if openclaw invocation returns non-zero exit code or http error
if the inbound task json is malformed (missing message, session_id, or id)
if a2a-listener.pid does not exist when running stop.sh
if the bind address cannot be auto-detected (no tailscale, no network interfaces)
successful listener startup
successful task processing
failed task processing
successful listener shutdown
misconfiguration or missing dependencies
ps aux | grep a2a-listener.py and see the running process, or check that a2a-listener.pid exists and the pid is validcurl http://{BIND_ADDR}:{LISTENER_PORT}/health and receive http 200 with ok status