Handing Ticket Analysis to a Specialist Agent with A2A: A Complete Task Handoff

Handing Ticket Analysis to a Specialist Agent with A2A: A Complete Task Handoff

A support system has two agents: a coordinator agent facing the user, deciding “who should look at this ticket”; and a ticket-analysis agent, a specialist at finding root causes and proposing fixes. The handoff between them is A2A. Where the ticket data lives, which tools get called, and how permissions are granted happen inside the specialist (that side is MCP — see the boundary at the end); A2A governs what happens between the two agents: discovery, task state, messages, artifacts, and recovery.

Prerequisites: state and message semantics in the Task runtime guide , idempotency and recovery patterns in Task idempotency and recovery . This page walks them through one scenario.

Walkthrough overview

  sequenceDiagram
    participant U as User
    participant C as Coordinator (A2A client)
    participant S as Ticket analyst (A2A server)

    U->>C: "What is wrong with this ticket?"
    C->>S: fetch /.well-known/agent-card.json (discovery)
    C->>S: message/send (analysis request, messageId=m1)
    S-->>C: Task(SUBMITTED)
    S-->>C: Task(WORKING)
    S-->>C: Task(INPUT_REQUIRED): missing reproduction steps
    C->>S: message/send (supplement, messageId=m2, same contextId)
    S-->>C: Task(WORKING)
    S-->>C: Artifact: structured report (chunked, lastChunk)
    S-->>C: Task(COMPLETED) + concluding Message
    C-->>U: Root cause and recommendation

1. Discovery: select by skill, not by name

The coordinator reads /.well-known/agent-card.json and decides on three things:

What to checkThe judgment in this scenario
skillsIs there a ticket / root-cause-analysis skill? A skill’s tags are more reliable than its name
capabilitiesAt least one of streaming or pushNotifications is true, so a long analysis is not a blind wait
securitySchemes / securityRequirementsDo we hold credentials matching what the agent requires?

Discovery mechanics and card fields are in Agent discovery and the Agent Card implementation guide . The coordinator should cache the card and honor its cache window instead of rediscovering on every handoff.

2. Starting: put the idempotency key in the first message

{
  "jsonrpc": "2.0",
  "id": "req-101",
  "method": "message/send",
  "params": {
    "message": {
      "role": "role_user",
      "parts": [{ "kind": "text", "text": "Analyze ticket TCK-4821: customer reports order status not updating after checkout" }],
      "messageId": "coord-4f21-m1",
      "contextId": "ctx-tck4821"
    }
  }
}

Two decisions are made now and never changed:

  • messageId embeds the upstream identifier (the ticket number): on a timeout, resend verbatim with the same value; the server, with (contextId), recognizes the duplicate and does not re-run the analysis;
  • contextId is named after the ticket: every later task for this ticket (re-analysis, supplements, escalation) shares one context, keeping history and ownership traceable.

The server returns a Task (SUBMITTED or WORKING); record the taskId — every later operation targets it.

3. Tracking: each state maps to one coordinator action

Task stateWhat the coordinator does
SUBMITTED / WORKINGWait; receive passively if subscribed to the stream or push, otherwise GetTask with backoff
INPUT_REQUIREDAsk the user for information, then send it back to the same task as a new messageId
AUTH_REQUIREDComplete the authorization the status message describes (approver or out-of-band), per section 4 of Security implementation
COMPLETEDRead the artifacts and the final Message; hand them to the user
FAILED / CANCELED / REJECTEDDecide by reason: retry (new task, same contextId), switch agents, or escalate

The scenario pauses once at INPUT_REQUIRED: the analyst needs reproduction steps. The coordinator fetches them from the user and continues on the same task:

{
  "jsonrpc": "2.0",
  "id": "req-102",
  "method": "message/send",
  "params": {
    "message": {
      "role": "role_user",
      "parts": [{ "kind": "text", "text": "Reproduction: refresh the order page twice within 30 s of placing the order" }],
      "messageId": "coord-4f21-m2",
      "taskId": "task-9c31",
      "contextId": "ctx-tck4821"
    }
  }
}

The messageId is new (this is new input, not a resend); taskId and contextId are unchanged.

4. Receiving: the report is an Artifact, the conclusion is a Message

The result arrives in two layers:

  • Artifact (structured report): root cause, confidence, affected-order list, recommended actions. Streamed in chunks ending with lastChunk: true; the coordinator aggregates by artifactId and assumes nothing about chunk counts;
  • Message (final statement): a natural-language conclusion the coordinator relays to the user.
{
  "jsonrpc": "2.0",
  "id": "req-103",
  "method": "tasks/get",
  "params": { "taskId": "task-9c31" }
}

The report in Task.artifacts together with the conclusion in Task.status.message forms the complete delivery. Field details are on the Artifact object page and the Message object page .

5. Recovery: the coordinator’s checklist

Any step can break; check item by item:

ObservationAction
message/send timed out; delivery unknownResend verbatim (same messageId); the server returns the first result idempotently
Event stream dropped mid-wayReconnect with SubscribeToTask; when resume is unsupported, deduplicate repeated events by sequence
Polling onlyGetTask with backoff until terminal; a dropped stream is not a failed task
WORKING past the ticket’s time limitCancelTask, then start a new task under the same contextId (terminal tasks cannot restart)
More authorization needed along the chainMove your own task to AUTH_REQUIRED and delegate upward; bind credentials to the originator
Analyst unreachableRetry once on another interface from the cached card; if it still fails, switch agents per discovery and record the reason in a new contextId

The full recovery decision tree and probes are in Task idempotency and recovery ; hardening the push channel is in Push receiver hardening .

Boundary: the MCP side

Internally, the ticket-analysis agent uses MCP to read ticket-system data, call controlled tools (refund lookup, order-status API), and map permission failures to A2A’s AUTH_REQUIRED or FAILED. That is MCP’s side of the boundary — MCP Servers/Clients, tools and resources, least privilege, call failures and observable results — covered in the corresponding practice page on the MCP site . The division between the two protocols is summarized in A2A and MCP .

Related pages

Version and verification notes

Aligned with A2A v1.0.1. Message, task, and artifact fields follow the data structures ; the sequence diagram and checklist are composed of this scenario’s message flow and can serve directly as an acceptance checklist. The messageId and contextId value patterns are this site’s recommendations; the protocol requires only that the sender generates them.

Sources: A2A data structures , Life of a task .