Skip to content
133 changes: 133 additions & 0 deletions docs/specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,8 @@
--8<-- "types/src/types.ts:AgentCapabilities"
```

- **`correlationIdRequired`**: When set to `true`, indicates that the agent requires clients to provide a `correlationId` field in `MessageSendParams` for all new task creation requests. This enables idempotent task creation to handle network failures and prevent duplicate operations. See [Section 7.1.2](#712-correlation-ids-and-idempotency) for detailed behavior.

#### 5.5.2.1. `AgentExtension` Object

Specifies an extension to the A2A protocol supported by the agent.
Expand Down Expand Up @@ -697,6 +699,7 @@
{
message: Message,
configuration?: MessageSendConfiguration,
correlationId?: string,
metadata?: { [key: string]: any }
}
```
Expand All @@ -721,6 +724,28 @@
--8<-- "types/src/types.ts:MessageSendConfiguration"
```

#### 7.1.2. Correlation IDs and Idempotency

A2A supports optional correlation IDs to enable idempotent task creation, addressing scenarios where network failures or client crashes could result in duplicate tasks with unintended side effects.

**Agent Requirements:**
- Agents **MAY** support correlation IDs by declaring `correlationIdRequired: true` in their `AgentCapabilities`.
- When `correlationIdRequired` is `true`, agents **MUST** require a `correlationId` field in `MessageSendParams` for all new task creation requests.
- When `correlationIdRequired` is `false` or absent, agents **MAY** ignore provided `correlationId` values.

**Server Behavior:**
- **Correlation ID scope**: Correlation IDs are scoped to the authenticated user/session to prevent cross-user conflicts.
- **Active task collision**: If a `correlationId` matches an existing task in a non-terminal state (`submitted`, `working`, `input-required`), the server **MUST** return a [`CorrelationIdAlreadyExistsError`](#82-a2a-specific-errors) (-32008).
- **Terminal task reuse**: If a `correlationId` matches a task in a terminal state (`completed`, `failed`, `canceled`, `rejected`), the server **MAY** allow creating a new task with the same correlation ID.
- **Time-based expiry**: Servers **MAY** implement time-based expiry for correlation IDs associated with terminal tasks.

**Client Responsibilities:**
- **Unique correlation IDs**: Clients **MUST** generate unique correlation IDs for each intended new task within their authenticated session.
- **Error handling**: When receiving `CorrelationIdAlreadyExistsError`, clients **SHOULD**:
1. Use the `existingTaskId` from the error data (if provided) to call `tasks/get`
2. OR generate a new `correlationId` and retry the request
- **Retry safety**: Clients can safely retry `message/send` requests with the same `correlationId` after network failures.

### 7.2. `message/stream`

Sends a message to an agent to initiate/continue a task AND subscribes the client to real-time updates for that task via Server-Sent Events (SSE). This method requires the server to have `AgentCard.capabilities.streaming: true`. Just like `message/send`, a task which has reached a terminal state (completed, canceled, rejected, or failed) can't be restarted. Sending a message to such a task will result in an error. For more information, refer to the [Life of a Task guide](./topics/life-of-a-task.md).
Expand Down Expand Up @@ -763,6 +788,7 @@
{
message: Message,
configuration?: MessageSendConfiguration,
correlationId?: string,
metadata?: { [key: string]: any }
}
```
Expand Down Expand Up @@ -1173,6 +1199,7 @@
| `-32005` | `ContentTypeNotSupportedError` | Incompatible content types | A [Media Type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types) provided in the request's `message.parts` (or implied for an artifact) is not supported by the agent or the specific skill being invoked. |
| `-32006` | `InvalidAgentResponseError` | Invalid agent response type | Agent generated an invalid response for the requested method |
| `-32007` | `AuthenticatedExtendedCardNotConfiguredError` | Authenticated Extended Card not configured | The agent does not have an Authenticated Extended Card configured.|
| `-32008` | `CorrelationIdAlreadyExistsError` | Correlation ID already exists for active task | The provided `correlationId` is already associated with an active task in a non-terminal state. The client should either retrieve the existing task using the provided `existingTaskId` or generate a new correlation ID. |

Servers MAY define additional error codes within the `-32000` to `-32099` range for more specific scenarios not covered above, but they **SHOULD** document these clearly. The `data` field of the `JSONRPCError` object can be used to provide more structured details for any error.

Expand Down Expand Up @@ -1869,6 +1896,112 @@
}
```

### 9.8. Idempotent Task Creation with Correlation IDs

**Scenario:** Client needs to ensure idempotent task creation to avoid duplicate operations in case of network failures.

1. **Client discovers agent requires correlation IDs from Agent Card:**

```json
{
"capabilities": {
"correlationIdRequired": true
}
}
```

2. **Client sends initial message with correlation ID:**

```json
{
"jsonrpc": "2.0",
"id": "req-008",
"method": "message/send",
"params": {
"message": {
"role": "user",
"parts": [
{
"kind": "text",
"text": "Process this critical financial transaction - charge card ending 1234 for $500"
}
],
"messageId": "a1b2c3d4-e5f6-7890-1234-567890abcdef"
},
"correlationId": "client-txn-20240315-001"

Check failure on line 1931 in docs/specification.md

View workflow job for this annotation

GitHub Actions / Check Spelling

`txn` is not a recognized word. (unrecognized-spelling)
}
}
```

3. **Server successfully creates task and responds:**

```json
{
"jsonrpc": "2.0",
"id": "req-008",
"result": {
"id": "server-task-uuid-12345",
"contextId": "ctx-67890",
"status": { "state": "working" }
}
}
```

4. **Network failure occurs - client retries with same correlation ID:**

```json
{
"jsonrpc": "2.0",
"id": "req-009",
"method": "message/send",
"params": {
"message": {
"role": "user",
"parts": [
{
"kind": "text",
"text": "Process this critical financial transaction - charge card ending 1234 for $500"
}
],
"messageId": "a1b2c3d4-e5f6-7890-1234-567890abcdef"
},
"correlationId": "client-txn-20240315-001"

Check failure on line 1968 in docs/specification.md

View workflow job for this annotation

GitHub Actions / Check Spelling

`txn` is not a recognized word. (unrecognized-spelling)

Check warning on line 1968 in docs/specification.md

View workflow job for this annotation

GitHub Actions / Check Spelling

`txn` is not a recognized word. (unrecognized-spelling)
}
}
```

5. **Server detects duplicate correlation ID and returns error:**

```json
{
"jsonrpc": "2.0",
"id": "req-009",
"error": {
"code": -32008,
"message": "Correlation ID already exists for active task",
"data": {
"correlationId": "client-txn-20240315-001",

Check failure on line 1983 in docs/specification.md

View workflow job for this annotation

GitHub Actions / Check Spelling

`txn` is not a recognized word. (unrecognized-spelling)

Check warning on line 1983 in docs/specification.md

View workflow job for this annotation

GitHub Actions / Check Spelling

`txn` is not a recognized word. (unrecognized-spelling)
"existingTaskId": "server-task-uuid-12345"
}
}
}
```

6. **Client retrieves existing task using the provided task ID:**

```json
{
"jsonrpc": "2.0",
"id": "req-010",
"method": "tasks/get",
"params": {
"id": "server-task-uuid-12345"
}
}
```

This pattern ensures that duplicate operations are prevented while allowing clients to safely recover from network failures.

These examples illustrate the flexibility of A2A in handling various interaction patterns and data types. Implementers should refer to the detailed object definitions for all fields and constraints.

## 10. Appendices
Expand Down
6 changes: 5 additions & 1 deletion specification/grpc/a2a.proto
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,8 @@ message AgentCapabilities {
bool push_notifications = 2;
// Extensions supported by this agent.
repeated AgentExtension extensions = 3;
// If the agent requires correlation IDs for idempotent task creation
bool correlation_id_required = 4;
}

// A declaration of an extension supported by an Agent.
Expand Down Expand Up @@ -620,7 +622,9 @@ message SendMessageRequest {
Message request = 1
[(google.api.field_behavior) = REQUIRED, json_name = "message"];
SendMessageConfiguration configuration = 2;
google.protobuf.Struct metadata = 3;
// Optional client-generated correlation ID for idempotent task creation
string correlation_id = 3;
google.protobuf.Struct metadata = 4;
}

message GetTaskRequest {
Expand Down
51 changes: 51 additions & 0 deletions specification/json/a2a.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 34 additions & 1 deletion types/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ export interface AgentCapabilities {
pushNotifications?: boolean;
/** Indicates if the agent provides a history of state transitions for a task. */
stateTransitionHistory?: boolean;
/** Indicates if the agent requires correlation IDs for idempotent task creation. */
correlationIdRequired?: boolean;
/** A list of protocol extensions supported by the agent. */
extensions?: AgentExtension[];
}
Expand Down Expand Up @@ -636,6 +638,12 @@ export interface MessageSendParams {
message: Message;
/** Optional configuration for the send request. */
configuration?: MessageSendConfiguration;
/**
* Optional client-generated correlation ID for idempotent task creation.
* If provided and the agent supports idempotency, this ID will be used to detect
* and handle duplicate requests within the authenticated session scope.
*/
correlationId?: string;
/** Optional metadata for extensions. */
metadata?: {
[key: string]: any;
Expand Down Expand Up @@ -1496,6 +1504,30 @@ export interface AuthenticatedExtendedCardNotConfiguredError
}
// --8<-- [end:AuthenticatedExtendedCardNotConfiguredError]

// --8<-- [start:CorrelationIdAlreadyExistsError]
/**
* An A2A-specific error indicating that the provided correlation ID already exists for an active task.
*/
export interface CorrelationIdAlreadyExistsError extends JSONRPCError {
/** The error code for when a correlation ID already exists for an active task. */
readonly code: -32008;
/**
* The error message.
* @default "Correlation ID already exists for active task"
*/
message: string;
/**
* Additional data that may include the existing task ID for client recovery.
*/
data?: {
/** The correlation ID that already exists. */
correlationId: string;
/** The ID of the existing task associated with this correlation ID. */
existingTaskId?: string;
};
}
// --8<-- [end:CorrelationIdAlreadyExistsError]

// --8<-- [start:A2AError]
/**
* A discriminated union of all standard JSON-RPC and A2A-specific error types.
Expand All @@ -1512,5 +1544,6 @@ export type A2AError =
| UnsupportedOperationError
| ContentTypeNotSupportedError
| InvalidAgentResponseError
| AuthenticatedExtendedCardNotConfiguredError;
| AuthenticatedExtendedCardNotConfiguredError
| CorrelationIdAlreadyExistsError;
// --8<-- [end:A2AError]
Loading