A2A Gateway
Let any A2A-compatible AI framework — including NVIDIA NeMo Agent Toolkit — discover and invoke your AgentStead agents over Tailscale.
The A2A Gateway is a small sidecar that runs alongside each of your agents. It speaks the Linux Foundation Agent-to-Agent (A2A) protocol — an open standard for AI agents to discover each other's capabilities and exchange tasks — so that external frameworks like NVIDIA NeMo Agent Toolkit can use your AgentStead agents as tools in their own workflows.
What it does
When an agent has Tailscale connected, the platform automatically starts an A2A gateway alongside it. The gateway:
- Advertises the agent via a standard Agent Card at
/.well-known/agent-card.json— this is how A2A clients discover what the agent can do - Accepts tasks sent by any A2A-compatible client (NeMo, another agent, a custom client) using the
message/sendJSON-RPC method - Forwards tasks to the agent runtime and streams the response back
- Lives on your tailnet at
http://<tailscale-ip>:8080— not exposed to the public internet
All communication goes over Tailscale's encrypted WireGuard tunnel, so no credentials or API keys flow through the public internet.
Prerequisites
- The agent must be running the OpenFang runtime
- Tailscale must be connected on the agent (see Networking & SSH)
- The client invoking the agent (NeMo, etc.) must be on the same Tailscale network
Support for OpenClaw, NanoBot, ZeroClaw, and NullClaw runtimes is in development. Those agents will start the gateway sidecar but return a not yet supported response until their adapters are ready.
Finding your A2A endpoints
Once an agent has a Tailscale IP, open Networking in the sidebar, select your workspace, and scroll down to the A2A Mesh section. It lists every agent in the workspace that has a gateway running, with its tailnet URL.
You can also fetch the list programmatically:
GET /api/steads/{steadId}/a2a/agents[
{
"agentId": "clx...",
"name": "researcher",
"agentType": "OPENFANG",
"tailscaleIp": "100.82.67.50",
"a2aUrl": "http://100.82.67.50:8080",
"cardUrl": "http://100.82.67.50:8080/.well-known/agent-card.json",
"status": "ONLINE"
}
]Connecting NeMo Agent Toolkit
The fastest way is the Copy NeMo config button in the A2A Mesh panel. It generates a ready-to-paste function_groups block for your NeMo workflow config.
Example output for a workspace with two agents:
function_groups:
researcher:
_type: a2a_client
url: http://100.82.67.50:8080
task_timeout: 60
coder:
_type: a2a_client
url: http://100.94.12.33:8080
task_timeout: 60Paste this into your NeMo workflow YAML alongside your workflow: block:
function_groups:
researcher:
_type: a2a_client
url: http://100.82.67.50:8080
task_timeout: 60
workflow:
_type: per_user_react_agent
tool_names:
- researcher
llm_name: nim_llmNeMo will automatically fetch the Agent Card, discover the agent's skills, and present them as callable tools to the LLM.
A2A clients in NeMo are per-user function groups. Your workflow must use per_user_react_agent or another per-user workflow type — react_agent (shared) cannot use A2A clients.
You can also generate the config via API:
GET /api/steads/{steadId}/a2a/nemo-configReturns text/plain YAML ready to paste.
Using the gateway directly
You can call the gateway from any HTTP client — no NeMo required.
Discover the agent card
curl http://<tailscale-ip>:8080/.well-known/agent-card.json | jq .{
"name": "researcher",
"description": "OpenFang agent",
"url": "http://100.82.67.50:8080",
"protocolVersion": "0.3.0",
"capabilities": { "streaming": false },
"skills": [...]
}Send a task
curl -X POST http://<tailscale-ip>:8080 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "message/send",
"params": {
"message": {
"role": "user",
"parts": [{ "type": "text", "text": "Summarise the latest AI research papers" }]
}
}
}'The response contains the agent's reply as an A2A Message object.
Health check
curl http://<tailscale-ip>:8080/healthz
# → 200 OKGateway endpoints reference
| Endpoint | Method | Description |
|---|---|---|
/.well-known/agent-card.json | GET | A2A Agent Card — capability discovery |
/ | POST | A2A message/send JSON-RPC endpoint |
/healthz | GET | Health check — returns 200 OK |
Runtime support
| Runtime | A2A Gateway | Status |
|---|---|---|
| OpenFang | ✅ Full support | Live |
| OpenClaw | 🔄 Stub (graceful error) | Coming soon |
| NanoBot | 🔄 Stub (graceful error) | Coming soon |
| ZeroClaw | 🔄 Stub (graceful error) | Coming soon |
| NullClaw | 🔄 Stub (graceful error) | Coming soon |
A stub gateway means the sidecar starts cleanly and the Agent Card is served, but sending a task returns a not yet supported error rather than crashing or looping.
How it works
The gateway is a compiled Go binary built with the official a2a-go SDK. It runs as an extra container in the agent's Docker Compose stack.
Docker Compose stack (per agent)
├── tailscale ← Tailscale sidecar (100.x.x.x IP)
├── runtime ← Agent runtime (OpenFang, OpenClaw, etc.)
├── a2a-gateway ← A2A gateway (shares tailscale network namespace)
└── browser / ssh ← Optional UI & access containersThe gateway shares the Tailscale container's network namespace, which means:
- It inherits the
100.x.x.xtailnet IP - It reaches the runtime at
http://runtime:<port>on the stack's internal Docker network - It is not reachable from the public internet — only from your tailnet
When a task arrives at the gateway:
- Extracts the text from the A2A
message/sendrequest - Translates it into the runtime's native internal API call
- Returns the response as an A2A
Messageartifact

