PromptRails

A2A Protocol

Let PromptRails agents discover and hand work to other agents when one workflow needs more than one specialist.

Best for

Engineers building against the API, SDKs, CLI, MCP, or local tooling

Use A2A when one agent should hand work to another specialist instead of trying to do everything itself. PromptRails publishes agent capabilities, accepts incoming agent tasks, and records the handoff so teams can see which agent did what.

What is A2A?

A2A gives agents a shared contract for collaboration:

  • Discover other agents through agent cards
  • Request work from an agent that owns a specific skill
  • Track status while the other agent works
  • Receive results in a predictable format

This enables multi-agent architectures where specialized agents collaborate to solve complex problems, even when running on different platforms.

Agent Cards

An agent card is a machine-readable description of an agent's capabilities. It includes:

  • Agent name and description
  • Supported input/output formats
  • Available skills and capabilities
  • Authentication requirements
  • Endpoint URL

Agent cards enable automated discovery -- one agent can find and evaluate other agents based on their published capabilities.

# Get an agent's A2A card
card = client.a2a.get_agent_card("your-agent-id")
 
print(f"Name: {card.name}")
print(f"Description: {card.description}")
print(f"Skills: {[skill.name for skill in card.skills]}")
Technical detailsProtocol message format

JSON-RPC messaging

A2A communication uses JSON-RPC 2.0 as the transport format. Messages include:

{
  "jsonrpc": "2.0",
  "method": "tasks/send",
  "params": {
    "id": "task-id",
    "message": {
      "role": "user",
      "parts": [
        {
          "type": "text",
          "text": "Analyze this dataset and generate a report"
        }
      ]
    }
  }
}

Task lifecycle

A2A tasks follow a defined lifecycle:

submitted -> working -> completed
                     -> failed
                     -> canceled
                     -> rejected
          -> input_required -> (user provides input) -> working

Task Statuses

StatusDescription
submittedTask has been submitted to the agent
workingAgent is actively processing the task
input_requiredAgent needs additional input to continue
completedTask finished successfully
failedTask encountered an error
canceledTask was cancelled by the requester
rejectedAgent rejected the task (e.g., outside its capabilities)
Technical detailsSDK task operations

Creating and Managing Tasks

Send a Task

task = client.a2a.send_message(
    "your-agent-id",
    "Summarize the Q4 sales report",
    context_id="optional-conversation-context"
)
 
print(f"Task ID: {task.id}")
print(f"Status: {task.status.state}")

Get Task Status

task = client.a2a.get_task("task-id")
 
print(f"Status: {task.status.state}")
print(f"Messages: {len(task.messages)}")
print(f"Artifacts: {len(task.artifacts)}")

Cancel a Task

client.a2a.cancel_task("task-id")

Task Artifacts

When a task completes, the agent may produce artifacts -- structured outputs that represent the work product:

{
  "artifacts": [
    {
      "type": "text",
      "text": "The Q4 sales report shows a 15% increase..."
    },
    {
      "type": "file",
      "name": "report.pdf",
      "mimeType": "application/pdf",
      "data": "base64-encoded-content"
    }
  ]
}

Multi-Agent Coordination

A2A enables patterns like:

Sequential Processing

Agent A processes data, then passes results to Agent B for further analysis:

# Agent A: Data extraction
task_a = client.a2a.send_message(
    "data-extractor-agent",
    "Extract sales data from Q4"
)
 
# Wait for completion, then pass to Agent B
# Agent B: Analysis
task_b = client.a2a.send_message(
    "analyst-agent",
    f"Analyze: {task_a_result}",
    context_id=task_a.context_id
)

Parallel Processing

Multiple agents work on different aspects simultaneously:

import asyncio
 
async def multi_agent_analysis():
    async with AsyncPromptRails(api_key="your-key") as client:
        tasks = await asyncio.gather(
            client.a2a.send_message("sentiment-agent", "Analyze sentiment"),
            client.a2a.send_message("topic-agent", "Extract key topics"),
            client.a2a.send_message("summary-agent", "Summarize the conversation"),
        )
        return tasks

Input-Required Flow

An agent can request additional input during processing:

task = client.a2a.send_message("agent-id", initial_message)
 
# Check if the agent needs more input
if task.status and task.status.state == "input_required":
    # Provide additional input
    task = client.a2a.send_message(
        "agent-id",
        "Additional context...",
        task_id=task.id,
    )
Technical detailsTask field reference

Task Fields

FieldTypeDescription
idKSUIDUnique task identifier
context_idstringConversation context identifier
statusobjectCurrent task status payload
messagesarrayMessage history
artifactsarrayTask outputs
metadataJSONCustom metadata
created_attimestampCreation time
updated_attimestampLast update time