A2A Part: Text, File, and Structured Data Content

A2A Part: Text, File, and Structured Data Content

A Part is the smallest unit of content in A2A. Both Messages and Artifacts are lists of parts, which is what makes the protocol independent of the kind of content exchanged: plain text, a file, or a structured JSON value.

One part, one kind of content

In v1.0 there is a single Part type. It holds exactly one of four content fields, and the field name tells you the kind:

FieldContentIn JSON
textA string of textA string
rawThe bytes of a fileA base64-encoded string
urlA URL pointing to the file’s contentA string
dataAny structured value: an object, array, string, number, boolean, or nullA JSON value

Every part can also carry these fields, whatever its content kind:

FieldDescription
mediaTypeThe MIME type of the content, for example text/plain, application/json, or image/png
filenameAn optional file name, for example document.pdf
metadataA key-value object with extra information about the part

Text

A text part is the simplest case, and the one used in most requests:

{
  "text": "Hello, world!"
}

Files: inline bytes or a URL

Use raw to send a file inside the message, and url to point to a file stored elsewhere. The specification’s file exchange scenario shows both directions. The client uploads an image with raw:

POST /message:send HTTP/1.1
Host: agent.example.com
Content-Type: application/a2a+json
Authorization: Bearer token

{
  "message": {
    "role": "ROLE_USER",
    "parts": [
      {
        "text": "Analyze this image and highlight any faces."
      },
      {
        "raw": "iVBORw0KGgoAAAANSUhEUgAAAAUA...",
        "filename": "input_image.png",
        "mediaType": "image/png"
      }
    ],
    "messageId": "6dbc13b5-bd57-4c2b-b503-24e381b6c8d6"
  }
}

The example is adapted: the specification’s version is missing the comma after the raw value and has a trailing comma after mediaType, so it is not valid JSON as printed. The agent answers with an artifact whose part points to the result by url:

HTTP/1.1 200 OK
Content-Type: application/a2a+json

{
  "task": {
    "id": "43667960-d455-4453-b0cf-1bae4955270d",
    "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4",
    "status": {
      "state": "TASK_STATE_COMPLETED",
      "timestamp": "2024-03-15T12:05:00Z"
    },
    "artifacts": [
      {
        "artifactId": "9b6934dd-37e3-4eb1-8766-962efaab63a1",
        "name": "processed_image_with_faces.png",
        "parts": [
          {
            "url": "https://storage.example.com/processed/task-bbb/output.png?token=xyz",
            "filename": "output.png",
            "mediaType": "image/png"
          }
        ]
      }
    ]
  }
}

Which to use is a trade-off. raw keeps everything in one request but grows the payload by the base64 overhead. url keeps messages small, but the receiver must be able to fetch the URL. The URL in the example carries a token, so treat URLs like it as credentials.

Structured data

The specification’s structured data scenario asks for open tickets in a given JSON shape. The client describes the shape in the text part’s metadata:

POST /message:send HTTP/1.1
Host: agent.example.com
Content-Type: application/a2a+json
Authorization: Bearer token

{
  "message": {
    "role": "ROLE_USER",
    "parts": [
      {
        "text": "Show me a list of my open IT tickets",
        "metadata": {
          "mediaType": "application/json",
          "schema": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "ticketNumber": { "type": "string" },
                "description": { "type": "string" }
              }
            }
          }
        }
      }
    ],
    "messageId": "85b26db5-ffbb-4278-a5da-a7b09dea1b47"
  }
}

The specification’s response returns the list as a JSON string inside a text part:

{
  "artifactId": "c5e0382f-b57f-4da7-87d8-b85171fad17c",
  "parts": [
    {
      "text": "[{\"ticketNumber\":\"REQ12312\",\"description\":\"request for VPN access\"},{\"ticketNumber\":\"REQ23422\",\"description\":\"Add to DL - team-gcp-onboarding\"}]"
    }
  ]
}

The data field carries the same information as a native JSON value, with mediaType set to application/json. This is the same ticket list written as a data part, adapted from the response above:

{
  "artifactId": "c5e0382f-b57f-4da7-87d8-b85171fad17c",
  "parts": [
    {
      "data": [
        {"ticketNumber": "REQ12312", "description": "request for VPN access"},
        {"ticketNumber": "REQ23422", "description": "Add to DL - team-gcp-onboarding"}
      ],
      "mediaType": "application/json"
    }
  ]
}

Using data avoids escaping JSON inside a string, and the receiver gets a native JSON value.

Matching what the other side accepts

An agent declares the media types it works with in its Agent Card through defaultInputModes and defaultOutputModes, and each skill can override them. A client can send acceptedOutputModes in the send configuration so the agent tailors its output. If a part uses a media type the agent does not support, the agent returns ContentTypeNotSupportedError. See Agent Card .

Coming from v0.3?

Version 1.0 removed the kind discriminator. The member name now identifies the content, and the three part types became one:

v0.3v1.0
{"kind": "text", "text": "..."}{"text": "..."}
{"kind": "file", "file": {"name": "...", "mimeType": "...", "fileWithBytes": "..."}}{"raw": "...", "filename": "...", "mediaType": "..."}, or url instead of raw
{"kind": "data", "data": {...}}{"data": {...}, "mediaType": "application/json"}

The same change applies to streaming events: {"kind": "status-update", ...} became {"statusUpdate": {...}}, and {"kind": "artifact-update", ...} became {"artifactUpdate": {...}}. More in What’s New in A2A Protocol v1.0 .

Related pages

Source: A2A official specification: Part, File Exchange, Structured Data Exchange, and Appendix A.2.1