---
schema_version: "1"
id: a4a80850d1dedf02bb91b3ea41026c5ab8b0d476469dce8ef11e1f8e6969fd7d
title: Using A2A Protocol in Agent Networks
collection: mulesoft
version: current
locale: en-us
portal_url: https://docs.salesforce.com/mulesoft/current/en-us/agent-network/latest/af-a2a-agent-network.md
canonical_url: https://docs.mulesoft.com/agent-network/latest/af-a2a-agent-network
source_site: docs.mulesoft.com
content_type: documentation
content_hash: e7131b1bdbd6cef91db22d7044aad199080f10d6daaf51159e8d25e4e8fa2fad
description: MuleSoft Documentation Site
release: "2.0"
latest: true
---
---
product: Building Agent Networks
version: 2.0
is-latest-version: true
---

# Using A2A Protocol in Agent Networks

> For the full documentation index, see: https://docs.mulesoft.com/llms.txt

The Agent2Agent (A2A) Protocol governs agent-to-agent communication. This protocol powers
orchestration, observability, and governance features in agent networks. MuleSoft supports v1.0 of
the [A2A Protocol](https://a2a-protocol.org/latest).

Review the following about using A2A interactions in agent networks.

## Context and Task ID Scoping

In a multi-broker network, each broker generates its own `contextId` and `taskId` for the requests
it receives. These IDs define the state and scope of a specific conversation between two agents. A
`taskId` is always matched to a `contextId`, but a `contextId` can exist without a `taskId`.

When a client sends a request to Broker\_1, Broker\_1 generates the necessary IDs for that request.
When Broker\_1 sends a new request to the next broker or non-broker agent in line, that broker or
non-broker agent establishes a unique `contextId` and `taskId` for the new request.

> [!NOTE] Non-broker agents don’t have to generate a `contextId` and `taskId` when receiving
> requests from a client.

Consider a network with a client and two brokers (1 and 2):

- The IDs used between the client and Broker\_1 are independent of the IDs used between Broker\_1
  and Broker\_2.

- When Broker\_1 delegates a task to Broker\_2, Broker\_2 (acting as a server) generates its own
  `contextId` and `taskId`.

- Broker\_1 maintains a mapping between its own upstream `taskId` (used to respond to its client)
  and the downstream `taskId` it tracks with Broker\_2.

- If Broker\_2 requires more information (returns `status: input-required`), it provides a
  `contextId` and `taskId` to Broker\_1. Broker\_1 uses these IDs to provide the requested input to
  Broker\_2. The client never sees Broker\_2’s internal IDs.

<table><colgroup><col> <col> <col></colgroup><thead><tr><th>Relationship</th><th>Role</th><th>Logic</th></tr></thead><tbody><tr><td><p>Client → Broker_1</p></td><td><p>Broker_1 is server</p></td><td><p>Generates <code>contextId_1</code> and <code>taskId_1</code> for the client.</p></td></tr><tr><td><p>Agent A → Broker_2</p></td><td><p>Broker_2 is server</p></td><td><p>Generates <code>contextId_2</code> and <code>taskId_2</code> for Broker_1.</p></td></tr><tr><td><p>Network broker</p></td><td><p>Broker_1</p></td><td><p>Broker_1 maps <code>contextId_1</code> and <code>taskId_1</code> to <code>contextId_2</code> and <code>taskId_2</code>.</p></td></tr></tbody></table>

For more information, see
[Life of a Task - Group Related Interactions](https://a2a-protocol.org/latest/topics/life-of-a-task/).

## Streaming with Server-Sent Events (SSE)

Agent network brokers support serving streaming responses to clients using Server-Sent Events (SSE).
A client can call a broker with streaming enabled and receive incremental updates as the broker
processes the request.

However, brokers don’t use streaming when communicating with downstream brokers or agents. Outbound
requests from a broker to other brokers or agents use standard non-streaming communication.

## How Agent Brokers Handle Concurrent Requests

In an agent network, most of a request's time is spent waiting on an LLM, an MCP tool, or another
agent to respond. Rather than holding a thread idle during that wait, agent brokers use a
non-blocking execution engine that frees the broker to make progress on other in-flight requests.
Each request resumes as soon as its response arrives. As a result, a broker processes many requests
concurrently rather than one at a time.

## Creating Conversational Memory with `contextId`

Configure your broker to remember earlier turns in a conversation by reusing a `contextId` across
requests. When a caller reuses a `contextId`, the broker seeds each new task with the prior
conversation history so the agent remembers what was said earlier.

To enable conversational memory, you need an agent network with: \* A broker configured with an
ingress authentication policy that resolves a stable user identity \* A client that supports the A2A
v1.0 wire contract

### How `contextId` Works

A `contextId` identifies one logical conversation. A `taskId` identifies one turn within that
conversation. Every turn produces a new `taskId`, but all turns that share a `contextId` belong to
the same conversation.

Request 1 (no contextId) ──► broker mints contextId=C, taskId=T1 ──► returns C Request 2
(contextId=C) ──► new taskId=T2, seeded with T1's conversation Request 3 (contextId=C) ──► new
taskId=T3, seeded with T1+T2

> [!IMPORTANT] Reuse the `contextId`, not the `taskId`. The prior task is already in a terminal
> state (`TASK_STATE_COMPLETED`). Sending a message to a completed `taskId` returns an error.

### Requirements

All three requirements must be met for conversational memory to work.

1.  The broker resolves a stable user identity for the caller.

2.  The caller reuses the `contextId` across turns.

3.  The caller sends the correct A2A wire contract.

#### User Identity

Conversational memory is scoped to a user identity derived from authentication at the broker’s
ingress. Configure the broker with an ingress authentication policy that yields this identity.
Without this policy, tasks are created without an owner and memory doesn’t engage.

The same authenticated caller must be present on every request in a conversation. That identity is
the key the broker’s memory is stored under.

##### Example: Client ID Enforcement with User Context Propagation

A common way to produce this identity is a pair of inbound API Manager policies applied to the
broker:

1.  Client ID enforcement authenticates the caller’s `client_id` and `client_secret`.

2.  User context propagation derives the user identity from the authenticated client, using the
    expression `#[authentication.clientId]`.

Apply client ID enforcement before user context propagation. User context propagation reads
`#[authentication.clientId]`, which is populated only after Client ID enforcement authenticates the
request. If the policies are reversed, the identity resolves empty, tasks are created without an
owner, and conversational memory does not engage. No error is returned to the caller; a `ListTasks`
request for the context returns an empty result. Use reorder policies on the broker’s inbound policy
list to set the order.

Other authentication schemes can supply the user identity as well. This ordering requirement is
specific to the Client ID enforcement and user context propagation pairing.

### A2A Wire Contract

The following values must be exact in every request:

<table><colgroup><col> <col> <col></colgroup><thead><tr><th>Requirement</th><th>Value</th><th>Error if incorrect</th></tr></thead><tbody><tr><td><p>Version header</p></td><td><p><code>A2A-Version: 1.0</code></p></td><td><p><code>-32009 Unsupported A2A version: missing</code></p></td></tr><tr><td><p>Method</p></td><td><p><code>SendMessage</code></p></td><td><p><code>-32601 Method not found</code></p></td></tr><tr><td><p>Role</p></td><td><p><code>ROLE_USER</code> (enum, not <code>"user"</code>)</p></td><td><p><code>-32602 Invalid enum value user</code></p></td></tr></tbody></table>

### Starting a Conversation

To start a new conversation, send a request without a `contextId`. The broker generates and returns
a `contextId` in the response.

```bash
curl -s -X POST "https://<host>/<broker-path>/" \
  -H "Content-Type: application/json" \
  -H "A2A-Version: 1.0" \
  <auth headers for your ingress policy> \
  -d '{
    "jsonrpc": "2.0", "id": "1", "method": "SendMessage",
    "params": { "message": {
      "role": "ROLE_USER",
      "parts": [{ "text": "My favorite fruit is DRAGONFRUIT and my order number is ORD-4521. Please remember both." }],
      "messageId": "g1"
    }}
  }'
```

The response includes a `contextId` in `result.task.contextId`. Save this value for subsequent
requests:

```json
{
  "result": {
    "task": {
      "id": "…T1…",
      "contextId": "c740ccdf-b70a-4e81-b9e4-395845b4c6ab",
      "status": {
        "state": "TASK_STATE_COMPLETED",
        "message": {
          "parts": [
            {
              "text": "Acknowledged — I have recorded your favorite fruit (DRAGONFRUIT) and your order number (ORD-4521)…"
            }
          ]
        }
      }
    }
  }
}
```

### Continuing a Conversation

To continue an existing conversation, include the `contextId` from the first turn in subsequent
requests:

```bash
curl -s -X POST "https://<host>/<broker-path>/" \
  -H "Content-Type: application/json" \
  -H "A2A-Version: 1.0" \
  <auth headers for your ingress policy> \
  -d '{
    "jsonrpc": "2.0", "id": "2", "method": "SendMessage",
    "params": { "message": {
      "role": "ROLE_USER",
      "contextId": "c740ccdf-b70a-4e81-b9e4-395845b4c6ab",
      "parts": [{ "text": "What is my favorite fruit and my order number?" }],
      "messageId": "g2"
    }}
  }'
```

The broker recalls facts from prior turns:

```json
{
  "result": {
    "task": {
      "contextId": "c740ccdf-b70a-4e81-b9e4-395845b4c6ab",
      "status": {
        "message": { "parts": [{ "text": "Favorite fruit: DRAGONFRUIT. Order number: ORD-4521." }] }
      }
    }
  }
}
```

### Referencing Work from Another Conversation

Reusing `contextId` handles continuity within one conversation. To bring in work from a different
conversation, add `referenceTaskIds` to the message. This field accepts a list of prior task IDs
whose conversation is seeded into the current turn.

```json
{
  "jsonrpc": "2.0",
  "id": "3",
  "method": "SendMessage",
  "params": {
    "message": {
      "role": "ROLE_USER",
      "contextId": "<this-conversation>",
      "referenceTaskIds": ["<task-id-from-another-conversation>"],
      "parts": [{ "text": "Reuse the itinerary from my earlier booking." }],
      "messageId": "g3"
    }
  }
}
```

When using `referenceTaskIds`:

- Each referenced task must belong to the same authenticated user (`userId`). If a referenced task
  is missing or owned by another user, the request is rejected.

- Referencing a task already part of the current conversation doesn’t duplicate its content.

- Use `referenceTaskIds` only to cross conversation boundaries. Same-conversation continuity comes
  from reusing the `contextId`.

### Memory Scope and Limits

Conversational memory includes user and assistant messages (the text exchanged between the user and
the agent)

Conversational memory does not include tool calls and tool results. The agent remembers what was
discussed, not the intermediate tool invocations it made.

Additional limits include:

- Memory is capped at approximately the most recent 200 messages per conversation. Older turns roll
  off.

- Retention is bounded by the configured TTL. After the TTL expires, recall stops.

- Memory is shared across broker instances. Continuity does not depend on requests landing on the
  same replica, so sticky sessions or session affinity on your load balancer are not required.

#### Orchestrator Brokers

If a broker calls downstream A2A agents while handling a conversation, it keeps those downstream
conversations continuous automatically. For a given inbound `contextId`, the broker reuses the same
downstream conversation with each child agent across turns so the downstream agents recover their
own context on follow-ups. No extra caller action is required beyond reusing the inbound
`contextId`.

### Configuring Memory Retention

Set the following variable in `exchange.json` under `metadata.variables` to control how long
conversational memory persists:

<table><colgroup><col> <col> <col> <col></colgroup><thead><tr><th>Variable</th><th>Description</th><th>Default</th><th>Notes</th></tr></thead><tbody><tr><td><p><code>OBJECT_STORE_DEFAULT_TTL_MS</code></p></td><td><p>How long a <code>contextId</code> memory persists</p></td><td><p>24 hours (<code>86400000</code>)</p></td><td><p>Maximum 30 days (<code>2592000000</code>). After the TTL expires, recall stops.</p></td></tr></tbody></table>

### Verifying Your Setup

To confirm the ingress policy is active, send an unauthenticated request. A `401` response confirms
that identity is required:

```bash
curl -s -o /dev/null -w "%{http_code}\n" -X POST "https://<host>/<broker-path>/" \
  -H "Content-Type: application/json" -H "A2A-Version: 1.0" \
  -d '{"jsonrpc":"2.0","id":"0","method":"SendMessage","params":{"message":{"role":"ROLE_USER","parts":[{"text":"ping"}],"messageId":"p0"}}}'
```

After completing your first turn, use `ListTasks` to verify that memory is active. An empty result
means no user identity was resolved. If you use Client ID enforcement with user context propagation,
confirm that the policies are applied in that order:

```bash
curl -s -X POST "https://<host>/<broker-path>/" \
  -H "Content-Type: application/json" -H "A2A-Version: 1.0" \
  <auth headers for your ingress policy> \
  -d '{"jsonrpc":"2.0","id":"l","method":"ListTasks","params":{"contextId":"<contextId>"}}'
```

## See Also

- [Building Agent Networks for Agent Fabric](https://docs.mulesoft.com/agent-network/latest/af-agent-networks)

- [Agent Network 2.0 Project File Reference](https://docs.mulesoft.com/agent-network/latest/af-project-files)

- [A2A Protocol Specification](https://a2a-protocol.org/latest)
