Thomas Piacentini
← Workshop

Chapter

Message

Original text · English

A Message is one turn in a conversation, and its role and three ids say which way it travels and which task it continues.

The planner sends code-reviewer a diff to review. The reviewer asks which base branch to compare against, the planner answers main, and the reviewer finishes the task with two findings.

When you finish this section, you can build a valid message and set its ids to continue a task.

messageId, role, and parts

Message: one unit of communication between the client and the server {{proto Message}}. It has eight fields, and only messageId, role, and parts are REQUIRED {{proto Message}}. The parts list must contain at least one element {{spec §5.7}}, and Part explains what a part holds.

Figure sets three messages of that review side by side and names who sets each field.

Fig. 3.1who sets each field of a messagestructure
A message carries an id its sender mints, while its `contextId` and `taskId` come from the server and the client echoes them to continue.
A message carries an id its sender mints, while its contextId and taskId come from the server and the client echoes them to continue. Columns are three messages from the input-required capture, in the order they were sent. The right column names who sets each field. Ids shortened to 8 characters.

Original SVG

Original source
id: fig-message-fields
kind: structure
title: who sets each field of a message
claim: A message carries an id its sender mints, while its `contextId` and `taskId` come from the server and the client echoes them to continue.
caption: Columns are three messages from the input-required capture, in the order they were sent. The right column names who sets each field. Ids shortened to 8 characters.

The sender of the message creates messageId {{proto Message}}. The kit refuses a message without one, as Errors shows. Spec samples in §11.2 and §6.3 leave messageId out {{spec §11.2}} {{spec §6.3}}, so never copy a sample as it stands (conflict D23).

Role: the direction a message travels. The value ROLE_USER marks a message from the client to the server {{proto Role}}, and ROLE_AGENT marks one from the server to the client {{proto Role}}. The planner is an agent, yet its messages to code-reviewer carry ROLE_USER, because it is the client there.

The glossary in §2.2 still writes the roles as "user" and "agent" {{spec §2.2}} {{spec §5.5}} (conflict D35). The spec names no error for a client that sends ROLE_AGENT, and the kit answers -32602, as the kit README lists.

contextId and taskId

A client message with neither id starts something new. The server's messages then carry the contextId it minted, plus the taskId of any task it creates {{proto Message}}. The direct reply in 02-message-reply.http carries a contextId and no taskId, because the reviewer answered without creating a task:

a server message with a context and no taskjsonFull source
  "result": {
    "message": {
      "messageId": "3b4b1206-4b6f-467f-b735-2d7e4f7f4bcd",
      "contextId": "dc423c62-d5f4-48e5-82c0-4963b9bc4334",
      "role": "ROLE_AGENT",
      "parts": [
        {
          "text": "I review unified diffs in Python, TypeScript and Go. Send the diff as a text/x-diff part and name the base branch."
        }
      ]
    }
  }
The result of the only exchange in the file, complete.
Original source
title: a server message with a context and no task
source: capture/out/02-message-reply.http
lang: json
note: The result of the only exchange in the file, complete.
---
  "result": {
    "message": {
      "messageId": "3b4b1206-4b6f-467f-b735-2d7e4f7f4bcd",
      "contextId": "dc423c62-d5f4-48e5-82c0-4963b9bc4334",
      "role": "ROLE_AGENT",
      "parts": [
        {
          "text": "I review unified diffs in Python, TypeScript and Go. Send the diff as a text/x-diff part and name the base branch."
        }
      ]
    }
  }

To continue a task, the client copies both ids from the task into its next message, as the planner's answer does:

the planner's answer, with both ids copied from the taskjsonFull source
    "message": {
      "messageId": "096d3737-42f9-4039-8320-a4737c2b3abe",
      "contextId": "49c5a238-4158-455d-8d63-b2cb39c0ecac",
      "taskId": "e5c95b09-6d4b-4b3b-8693-91e81472d12f",
      "role": "ROLE_USER",
      "parts": [
        {
          "text": "main"
        }
      ]
    }
The message of exchange 2, complete.
Original source
title: the planner's answer, with both ids copied from the task
source: capture/out/06-input-required.http
lang: json
note: The message of exchange 2, complete.
---
    "message": {
      "messageId": "096d3737-42f9-4039-8320-a4737c2b3abe",
      "contextId": "49c5a238-4158-455d-8d63-b2cb39c0ecac",
      "taskId": "e5c95b09-6d4b-4b3b-8693-91e81472d12f",
      "role": "ROLE_USER",
      "parts": [
        {
          "text": "main"
        }
      ]
    }

Three rules govern those two ids:

  • A client taskId must name a live task. A taskId that a client puts in a message must refer to a task that exists {{spec §3.4.2}}. An unknown id gets TaskNotFoundError, and CancelTask and terminal states covers a finished task.
  • The two ids must agree. An agent must reject a message whose contextId and taskId do not match {{spec §3.4.3}}. The spec names no error for this, and both the kit and the reference SDK answer -32602 {{sdk src/a2a/server/request_handlers/default_request_handler_v2.py}}.
  • The context follows the task. When a message has only a taskId, the agent must take the contextId from that task {{spec §3.4.3}}. The kit does, and the table below says what each SDK does.

A message with a contextId and no taskId starts a new task in that conversation, as contextId shows.

What the SDKs do with the ids

Send Message operations are allowed to be idempotent, and an agent can use the messageId to find duplicate messages {{spec §3.3.1}}. So a retry of a send that timed out can reuse the same messageId. No SDK rejects a repeated id, and without a taskId each one creates another task for it.

SDK Message with only taskId Same messageId sent twice to one task
Python 1.2.2 Does not read the task, and stamps a fresh contextId on the message {{sdk src/a2a/server/agent_execution/context.py}} Accepted. The history skips a second copy, and the agent runs again {{sdk src/a2a/server/agent_execution/active_task.py}}
Go v2.6.0 The agent context gets the task's contextId, and the stored message stays as sent {{sdk-go a2asrv/agentexec.go at v2.6.0}} Accepted. The history skips a second copy, and the agent runs again {{sdk-go a2asrv/agentexec.go at v2.6.0}}
Java v1.4.0.Final The agent context and its events get the task's contextId, and the stored message stays as sent {{sdk-java server-common/src/main/java/org/a2aproject/sdk/server/requesthandlers/DefaultRequestHandler.java at v1.4.0.Final}} Accepted. The message is appended to history again, and the agent runs again {{sdk-java server-common/src/main/java/org/a2aproject/sdk/server/tasks/TaskManager.java at v1.4.0.Final}}
JS v1.3.0 The agent context and its copy of the message get the task's contextId, and the stored message stays as sent {{sdk-js src/server/request_handler/default_request_handler.ts at v1.3.0}} Accepted. The message is appended to history again, and the agent runs again {{sdk-js src/server/request_handler/default_request_handler.ts at v1.3.0}}
.NET v1.0.0-preview2 The agent context gets the task's contextId, and the stored message stays as sent {{sdk-dotnet src/A2A/Server/A2AServer.cs at v1.0.0-preview2}} Accepted. The message is appended to history again, and the handler runs again {{sdk-dotnet src/A2A/Server/A2AServer.cs at v1.0.0-preview2}}
Rust a2a-server-lf-v0.5.1 The executor context gets the task's contextId, and the stored message stays as sent {{sdk-rust a2a-server/src/handler.rs at a2a-server-lf-v0.5.1}} Accepted. The message is appended to history again, and the executor runs again {{sdk-rust a2a-server/src/handler.rs at a2a-server-lf-v0.5.1}}

Five SDKs infer the context for the agent and store the message as sent, and the reference SDK generates a new one. Send both ids, and the difference never reaches you.

Optional fields and the status message

No message in the kit's captures sets these three:

  • referenceTaskIds lists the ids of tasks that the message points to for more context {{proto Message}}.
  • extensions lists the URIs of the extensions that are present in the message or that added to it {{proto Message}}, and extensions covers them.
  • metadata is a free-form JSON object {{proto Message}}.

Status message: the one message a TaskStatus can hold, the agent's words inside a task {{proto TaskStatus}}. The reviewer's question was a status message, and the findings themselves arrive in the review.json artifact, as what A2A standardizes explains.

Original source
- Mint a fresh `messageId` for every message you send, and reuse it only to retry the same message.
- Send `ROLE_USER` from the client and `ROLE_AGENT` from the server, whatever the party is.
- Copy both `taskId` and `contextId` from the task into each follow-up, because the reference SDK does not infer the context.

Sources: proto Message, Role, TaskStatus (research/sources/a2a.proto); spec §2.2, §3.3.1, §3.4.2, §3.4.3, §5.5, §5.7, §6.3, §11.2 (research/sources/specification.md); sdk src/a2a/server/agent_execution/active_task.py, src/a2a/server/agent_execution/context.py, src/a2a/server/request_handlers/default_request_handler_v2.py; sdk-go a2asrv/agentexec.go at v2.6.0; sdk-java server-common/src/main/java/org/a2aproject/sdk/server/requesthandlers/DefaultRequestHandler.java, server-common/src/main/java/org/a2aproject/sdk/server/tasks/TaskManager.java at v1.4.0.Final; sdk-js src/server/request_handler/default_request_handler.ts at v1.3.0; sdk-dotnet src/A2A/Server/A2AServer.cs at v1.0.0-preview2; sdk-rust a2a-server/src/handler.rs at a2a-server-lf-v0.5.1; capture/README.md; capture/out/02-message-reply.http, 06-input-required.http

Code and artifacts 1
What you take away

Saved in your notebook on this device.

Sources and limits

Reading checkpoints are self-reported. Test results are recorded separately and apply to the executed cases.

AI Engineering from Scratch · 3f9715c · MIT

Download this file ↓