Elevator pitch
What are you proposing to change?ACP v2 should use a single
sessionUpdate: "tool_call_update" notification for both creating and updating tool calls. The payload is an upsert keyed by toolCallId: if the Client has not seen the ID before, it creates a new displayed tool call; otherwise, it patches the existing tool call.
Status quo
How do things work today and what problems does this cause? Why would we change things?ACP v1 has two related tool-call session updates:
tool_call_update notifications if that is all they receive. There also wasn’t a way to handle unsetting values, which we established with session_info_update.
What we propose to do about it
What are you proposing to improve the situation?ACP v2 should remove
sessionUpdate: "tool_call" and make sessionUpdate: "tool_call_update" the only tool-call session update.
The v2 ToolCallUpdate payload has the following semantics:
toolCallIdis required.title,kind,status,content,locations,rawInput, andrawOutputare patch fields.- An omitted patch field leaves the previous value unchanged.
nullexplicitly clears or unsets the field.- Any concrete value replaces the previous value.
contentandlocationsare replaced as whole arrays, not appended.[]andnullboth clear a collection field.- For a new
toolCallId, omitted fields use Client defaults. - Agents SHOULD include
titlethe first time they report atoolCallId.
session/request_permission method should also carry this same ToolCallUpdate shape in its toolCall field, so the permission UI receives the same patch/upsert payload shape as session updates.
Shiny future
How will things play out once this feature exists?There is a single way to update a tool call, removing lots of confusion from how v1 operated.
Implementation details and plan
Tell me more about your implementation. What is your detailed implementation plan?
- Remove the v2
sessionUpdate: "tool_call"variant from the schema. - Represent patch fields with a three-state type so implementations can distinguish omitted,
null, and concrete values.
Compatibility
When converting v1 to v2, both v1tool_call and v1 tool_call_update should map to v2 tool_call_update.
When converting v2 back to v1, adapters can represent a v2 update as a v1 ToolCallUpdate. This is lossy for some explicit clear operations because v1 update fields cannot represent every distinction between omitted and null. Collection clears can be represented as empty arrays, but scalar/raw-field clears may have to be omitted when targeting v1. This will be a best-effort conversion.
Frequently asked questions
What questions have arisen over the course of authoring this document or during subsequent discussions?
Why keep the tool_call_update name instead of tool_call?
Most of the other notification names end in “update”, and this was by far the most used, so it should help with the migration hopefully.
Why make null different from omission?
Tool-call updates need both operations. Omission means “I am not changing this field”; null means “clear the value.” Without the distinction, Agents cannot unset a previously supplied field without resending a full replacement object.