Skip to main content

Overview

The Agent-to-Agent Protocol (A2A) is a standardized protocol developed by Google to enable structured communication between independent AI agents. Using JSON-RPC 2.0 over HTTP/HTTPS, A2A facilitates interoperability between different agent systems, allowing them to work together efficiently.
Official Protocol: A2A is maintained by Google and is available at google.github.io/A2A. The source code can be found in the GitHub repository.

Key Features

JSON-RPC 2.0

Protocol based on JSON-RPC 2.0 for structured and standardized communication

Multi-turn

Native support for multi-turn conversations using contextId

File Upload

File transfer via Base64 with support for different MIME types

Streaming

Asynchronous communication via Server-Sent Events (SSE) for real-time responses

Quick Overview

Key PointSummary
ObjectiveStandardize conversation between AI agents (regardless of vendor) using JSON-RPC 2.0
Base formatEach call is a JSON object with jsonrpc:"2.0", id, method and params
Main methodsmessage/sendmessage/streamtasks/gettasks/canceltasks/pushNotificationConfig/{set|get}tasks/resubscribeagent/authenticatedExtendedCard
Required IDsmessageId (UUID v4) within each message and id/taskId/callId to track request and task
First-class featuresMulti-turn conversations (contextId) • File upload (parts[] type file with Base64 + MIME) • Push notifications via pushNotificationConfig • Authentication via x-api-key or Authorization: Bearer
Task lifecyclesubmitted → working → completed / failed / canceled, reported in result.status.state

Protocol Methods

message/send (Synchronous HTTP)

The main method for sending messages synchronously.
POST /api/v1/a2a/AGENT-ID
Content-Type: application/json
x-api-key: YOUR_API_KEY

{
  "jsonrpc": "2.0",
  "id": "req-001",
  "method": "message/send",
  "params": {
    "message": {
      "role": "user",
      "parts": [{ "type": "text", "text": "Hello, who are you?" }],
      "messageId": "6dbc13b5-bd57-4c2b-b503-24e381b6c8d6"
    },
    "sessionId": "session-789",    // optional, for multi-turn
    "id": "task-456"               // optional taskId
  }
}
{
  "jsonrpc": "2.0",
  "result": {
    "id": "task-456",
    "status": {
      "state": "completed",
      "message": {
        "role": "agent",
        "parts": [{ "type": "text", "text": "I'm an A2A agent ready to help!" }]
      }
    },
    "final": true
  },
  "id": "req-001"
}

message/stream (Asynchronous SSE)

For real-time communication with streaming responses.
POST /api/v1/a2a/AGENT-ID
Content-Type: application/json
Accept: text/event-stream
x-api-key: YOUR_API_KEY

{
  "jsonrpc": "2.0",
  "id": "req-002",
  "method": "message/stream",
  "params": {
    "message": {
      "role": "user",
      "parts": [{ "type": "text", "text": "Generate a detailed report" }],
      "messageId": "uuid-here"
    }
  }
}
data: {"jsonrpc":"2.0","id":"req-002",
       "result":{"id":"task-789",
                 "status":{"state":"working"},
                 "final":false}}

data: {"jsonrpc":"2.0","id":"req-002",
       "result":{"id":"task-789",
                 "status":{"state":"completed",
                           "message":{"role":"agent",
                                      "parts":[{"type":"text","text":"Here's your report..."}]}},
                 "final":true}}

Implementation Examples

cURL

curl -X POST http://localhost:8000/api/v1/a2a/AGENT-ID \
  -H 'Content-Type: application/json' \
  -H 'x-api-key: YOUR_API_KEY' \
  -d '{
    "jsonrpc": "2.0",
    "id": "req-001",
    "method": "message/send",
    "params": {
      "message": {
        "role": "user",
        "parts": [{"type": "text", "text": "Hello!"}],
        "messageId": "6dbc13b5-bd57-4c2b-b503-24e381b6c8d6"
      }
    }
  }'

JavaScript / Fetch

const payload = {
  "jsonrpc": "2.0",
  "id": "req-001",
  "method": "message/send",
  "params": {
    "message": {
      "role": "user",
      "parts": [{"type": "text", "text": "Explain the A2A protocol"}],
      "messageId": crypto.randomUUID()
    }
  }
};

const response = await fetch('/api/v1/a2a/AGENT-ID', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'YOUR_API_KEY'
  },
  body: JSON.stringify(payload)
});

const result = await response.json();
console.log(result);

Python

import requests
import uuid
import json

payload = {
    "jsonrpc": "2.0",
    "id": str(uuid.uuid4()),
    "method": "message/send",
    "params": {
        "message": {
            "role": "user",
            "parts": [{"type": "text", "text": "Explain the A2A protocol"}],
            "messageId": str(uuid.uuid4())
        }
    }
}

response = requests.post(
    "http://localhost:8000/api/v1/a2a/AGENT-ID",
    headers={"x-api-key": "YOUR_API_KEY"},
    json=payload
)

print(json.dumps(response.json(), indent=2, ensure_ascii=False))

File Upload

The A2A protocol supports file upload through the file type in parts:
{
  "jsonrpc": "2.0",
  "id": "req-003",
  "method": "message/send",
  "params": {
    "message": {
      "role": "user",
      "parts": [
        {
          "type": "text",
          "text": "Analyze this image"
        },
        {
          "type": "file",
          "file": {
            "name": "image.png",
            "mimeType": "image/png",
            "bytes": "iVBORw0KGgoAAAANSUhEUgAAAAUA..." // Base64 without header
          }
        }
      ],
      "messageId": "uuid-here"
    }
  }
}
Base64 Format: Files must be encoded in Base64 without the data:mime/type;base64, header. Only the pure Base64 content should be included in the bytes field.

Multi-turn Conversations

To maintain context between multiple messages, use the contextId:
{
  "jsonrpc": "2.0",
  "id": "req-001",
  "method": "message/send",
  "params": {
    "message": {
      "role": "user",
      "parts": [{"type": "text", "text": "My name is John"}],
      "messageId": "msg-001"
    }
  }
}
Response:
{
  "jsonrpc": "2.0",
  "result": {
    "id": "task-001",
    "contextId": "ctx-abc123", // Save this ID
    "status": {
      "state": "completed",
      "message": {
        "role": "agent",
        "parts": [{"type": "text", "text": "Hello John! How can I help you?"}]
      }
    }
  }
}
{
  "jsonrpc": "2.0",
  "id": "req-002",
  "method": "message/send",
  "params": {
    "contextId": "ctx-abc123", // Use the previous contextId
    "message": {
      "role": "user",
      "parts": [{"type": "text", "text": "What is my name?"}],
      "messageId": "msg-002"
    }
  }
}
Response:
{
  "jsonrpc": "2.0",
  "result": {
    "id": "task-002",
    "contextId": "ctx-abc123",
    "status": {
      "state": "completed",
      "message": {
        "role": "agent",
        "parts": [{"type": "text", "text": "Your name is John!"}]
      }
    }
  }
}

Task States

The A2A protocol defines specific states for the task lifecycle:
StateDescription
submittedTask has been received and is in the queue
workingTask is being processed
completedTask completed successfully
failedTask failed during processing
canceledTask was canceled by the user

Authentication

The protocol supports different authentication methods:
  • API Key
  • Bearer Token
x-api-key: YOUR_API_KEY

Tips and Best Practices

  • Use UUID v4 for all required IDs (messageId, id, etc.)
  • Maintain consistency in IDs for proper tracking
  • Always save the returned contextId for continuous conversations
  • Send the contextId in all subsequent messages of the same conversation
  • Use Base64 without header in the bytes field
  • Always specify the correct mimeType
  • Consider the maximum size supported by the server
  • Handle ping events (: ping) appropriately
  • Close the EventSource connection when final: true
  • Implement reconnection handling for robustness
  • Configure CORS on the server (Access-Control-Allow-Origin: *) for browser testing
  • Use appropriate headers for production

Additional Resources


With the A2A protocol, you can quickly integrate any agent or front-end application into the Evo AI ecosystem, ensuring interoperability and standardized communication between different AI agent systems.