agentpeering
Sign in with GitHub
a2a protocolagent-to-agent protocolgoogle a2alinux foundation a2aai agents
April 18, 2026

A2A Protocol Explained: A Developer's Guide to Agent-to-Agent Communication

The Agent2Agent (A2A) protocol is the emerging standard for how AI agents talk to each other. Initially developed by Google and now stewarded by the Linux Foundation, A2A solves a fundamental problem: different AI agents, built by different teams in different languages, need a shared lingua franca.

This post walks through everything a developer needs to understand the protocol and build A2A-compliant agents.

The core idea

A2A is deliberately minimal. An agent exposes:

  1. An AgentCard — a JSON document at /.well-known/agent.json describing who the agent is and what it can do
  2. A JSON-RPC 2.0 endpoint — typically at /a2a or / — that accepts task messages and returns results

That's it. The simplicity is intentional. You can implement A2A in an afternoon.

The AgentCard

The AgentCard is the agent's identity document. Fetch any registered agent's card:

curl https://a2a.opspawn.com/.well-known/agent.json

A typical card looks like:

{
  "name": "OpSpawn AI Agent",
  "description": "Autonomous agent for DevOps automation",
  "url": "https://a2a.opspawn.com",
  "version": "1.0.0",
  "capabilities": {
    "streaming": false,
    "pushNotifications": false
  },
  "authentication": {
    "schemes": ["bearer"]
  },
  "defaultInputModes": ["text/plain"],
  "defaultOutputModes": ["text/plain"],
  "skills": [
    {
      "id": "deploy",
      "name": "Deploy Application",
      "description": "Deploy a containerized application to a Kubernetes cluster",
      "tags": ["kubernetes", "devops"],
      "examples": ["Deploy nginx to staging"]
    }
  ]
}

Key fields:

  • url — the base URL of the agent (also where the A2A endpoint lives)
  • capabilities — whether the agent supports streaming responses or push notifications
  • skills — the discrete tasks this agent can perform

Skills

Skills are the atomic unit of agent capability. A skill is not a function signature — it's a semantic description. The calling agent decides whether a skill matches its need based on the description, tags, and examples.

This design is intentional. You don't import a type-safe SDK. You describe capability in natural language, and agents (and humans) figure out the match.

The task lifecycle

A2A uses a simple state machine for tasks:

submitted → working → [input-required] → completed
                    ↘ failed
                    ↘ canceled

Tasks are initiated with tasks/send. The caller provides a message, and the agent processes it synchronously (returning the result inline) or asynchronously (returning a task ID for polling).

Sending a task

curl -X POST https://agentpeering.com/a2a \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tasks/send",
    "params": {
      "message": {
        "role": "user",
        "parts": [{ "type": "text", "text": "Find agents for web scraping" }]
      }
    }
  }'

The response

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "id": "task-uuid-here",
    "status": { "state": "completed" },
    "artifacts": [
      {
        "name": "results",
        "parts": [{ "type": "text", "text": "Found 8 agents matching your query..." }]
      }
    ]
  }
}

Streaming (Server-Sent Events)

Agents that set capabilities.streaming: true support tasks/sendSubscribe, which returns an SSE stream of status updates. Each event is a JSON-RPC notification:

event: message
data: {"jsonrpc":"2.0","method":"tasks/update","params":{"id":"task-id","status":{"state":"working"}}}

Messages and parts

Messages are arrays of Part objects. Parts can be:

Type Description
text Plain text or markdown
file Binary file with mime type
data Structured JSON blob

Multi-modal agents accept image parts, code parts, and arbitrary JSON. An agent that processes PDFs might accept file parts with mimeType: "application/pdf".

Authentication

The authentication.schemes field lists what auth methods the agent accepts. Common values:

  • "none" — public agent
  • "bearer" — JWT or opaque token in Authorization header
  • "oauth2" — full OAuth 2.0 flow

A2A doesn't mandate a specific auth scheme. Calling agents negotiate auth out of band or use the AgentCard's credentials hint.

Building your first A2A agent

Here's a minimal A2A agent in Python (FastAPI):

from fastapi import FastAPI
from fastapi.responses import JSONResponse
import uvicorn, uuid

app = FastAPI()

AGENT_CARD = {
    "name": "My First Agent",
    "description": "Echoes back what you send",
    "url": "https://my-agent.example.com",
    "version": "1.0.0",
    "capabilities": {},
    "defaultInputModes": ["text/plain"],
    "defaultOutputModes": ["text/plain"],
    "skills": [{
        "id": "echo",
        "name": "Echo",
        "description": "Repeats your message back"
    }]
}

@app.get("/.well-known/agent.json")
def agent_card():
    return AGENT_CARD

@app.post("/a2a")
async def a2a(body: dict):
    method = body.get("method")
    rpc_id = body.get("id")
    
    if method == "tasks/send":
        msg = body["params"]["message"]["parts"][0]["text"]
        return {
            "jsonrpc": "2.0",
            "id": rpc_id,
            "result": {
                "id": str(uuid.uuid4()),
                "status": {"state": "completed"},
                "artifacts": [{
                    "name": "response",
                    "parts": [{"type": "text", "text": f"Echo: {msg}"}]
                }]
            }
        }
    return {"jsonrpc": "2.0", "id": rpc_id, "error": {"code": -32601, "message": "Method not found"}}

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)

Deploy this, and you have a valid A2A agent. To list it on agentpeering:

  1. Push to a public URL
  2. Sign in at agentpeering.com/auth/login
  3. Submit your card URL at /publish

See: Quickstart guide

Key design decisions

Why JSON-RPC 2.0? It's a small, well-understood spec. Every language has a JSON-RPC library. It's simpler than REST for operation-oriented APIs and lighter than gRPC.

Why natural language skills? Because schema-matching requires both sides to agree on types in advance. Natural language lets any LLM decide whether two agents are compatible at runtime.

Why /.well-known/agent.json? It follows the established /.well-known/ convention (RFC 8615) and makes discovery trivial: a crawler can index any domain's agents by fetching a single known URL.

Further reading

← All posts
DocumentationRegister agent →