- Author(s): @michelTho, @nemtecl
- Champion: @benbrandt
Elevator pitch
Add amessageId field to agent_message_chunk, user_message_chunk, agent_thought_chunk session updates and session/prompt requests, and a userMessageId field to session/prompt responses, to uniquely identify individual messages within a conversation. Both clients and agents can generate message IDs using UUID format. This enables clients to distinguish between different messages beyond changes in update type and lays the groundwork for future capabilities like message editing and session deduplication.
Status quo
Currently, when an Agent sends message chunks viasession/update notifications, there is no explicit identifier for the message being streamed:
-
Ambiguous message boundaries - When the Agent sends multiple messages in sequence (e.g., alternating between agent and user messages, or multiple agent messages), Clients can only infer message boundaries by detecting a change in the
sessionUpdatetype. If an Agent sends consecutive messages of the same type, Clients cannot distinguish where one message ends and another begins. -
Non-standard workarounds - Currently, implementations rely on the
_metafield to work around this limitation. While functional, this approach is not standardized and each implementation may use different conventions. -
Limited future capabilities - Without stable message identifiers, it’s difficult to build features like:
- Message editing or updates
- Message-specific metadata or annotations
- Message threading or references
- Undo/redo functionality
What we propose to do about it
Add amessageId field to AgentMessageChunk, UserMessageChunk, AgentThoughtChunk session updates and PromptRequest, and a userMessageId field to PromptResponse. These fields would:
- Provide stable message identification - Each message gets a unique identifier that remains constant across all chunks of that message.
-
Enable reliable message boundary detection - Clients can definitively determine when a new message starts by observing a change in
messageId. - Create an extension point for future features - Message IDs can be referenced in future protocol enhancements.
Proposed Structure
When the Client sends a user message viasession/prompt, it can optionally include a messageId:
userMessageId in the response (or assigns one if the client didn’t provide it):
messageId:
user_message_chunk updates (e.g., during session/load), it uses the user message ID:
messageId field would be:
- Optional on
agent_message_chunk,user_message_chunk,agent_thought_chunkupdates andsession/promptrequests (asmessageId) - Optional in
session/promptresponses (asuserMessageId) - UUID format - Both clients and agents MUST use UUID format to ensure collision avoidance
- Unique per message within a session
- Stable across chunks - all chunks belonging to the same message share the same
messageId - Opaque - Implementations treat it as an identifier without parsing its structure
- Dual-origin - Clients generate IDs for user messages, agents generate IDs for agent messages
Message ID Acknowledgment
When a Client includesmessageId in a session/prompt request, the Agent’s response behavior indicates whether message IDs are supported:
- If the Agent supports message IDs and records the client-provided ID, it MUST include
userMessageIdin the response with the same value. - If the Agent assigns its own ID (when the client didn’t provide one), it SHOULD include
userMessageIdin the response so the client knows the assigned ID. - If the Agent does not support message IDs or chooses not to record it, it MUST omit
userMessageIdfrom the response.
userMessageId in the response as confirmation that the message ID was not recorded. Clients SHOULD NOT rely on that ID for subsequent operations (e.g., message editing, truncation, or forking) when userMessageId is absent.
This creates predictable behavior: the presence of userMessageId in the response is an explicit acknowledgment that the Agent recorded the ID and will recognize it in future operations.
Shiny future
Once this feature exists:- Clear message boundaries - Clients can reliably render distinct message bubbles in the UI, even when multiple messages of the same type are sent consecutively.
- Better streaming UX - Clients know exactly which message element to append chunks to, enabling smoother visual updates.
-
Foundation for editing - With stable message identifiers, future protocol versions could add:
message/edit- Agent updates the content of a previously sent messagemessage/delete- Agent removes a message from the conversationmessage/replace- Agent replaces an entire message with new content
-
Message metadata - Future capabilities could reference messages by ID:
- Annotations or reactions to specific messages
- Citation or cross-reference between messages
- Tool calls that reference which message triggered them
- Enhanced debugging - Implementations can trace message flow more easily with explicit IDs in logs and debugging tools.
Implementation details and plan
Phase 1: Core Protocol Changes
-
Update schema (
schema/schema.json):- Add optional
messageIdfield (type:string) toContentChunk(used byAgentMessageChunk,UserMessageChunk,AgentThoughtChunk) - Add optional
messageIdfield (type:string) toPromptRequest(client-provided) - Add optional
userMessageIdfield (type:string) toPromptResponse(echoed or agent-assigned)
- Add optional
-
Update Rust SDK (
rust/client.rsandrust/agent.rs):- Add
message_id: Option<String>field toContentChunkstruct - Add
message_id: Option<String>field toPromptRequeststruct - Add
user_message_id: Option<String>field toPromptResponsestruct - Update serialization to include
messageId/userMessageIdin JSON output when present
- Add
-
Update TypeScript SDK (if applicable):
- Add
messageIdfield to corresponding types
- Add
-
Update documentation (
docs/protocol/prompt-turn.mdx):- Document the
messageIdanduserMessageIdfields and their semantics - Clarify that clients can provide
messageIdfor user messages in prompt requests - Clarify that agents echo the ID as
userMessageIdin prompt responses - Clarify that agents generate
messageIdfor agent messages in chunks - Add examples showing message boundaries
- Explain that
messageIdchanges indicate new messages
- Document the
Phase 2: Reference Implementation
-
Update example agents:
- Modify example agents to generate and include
messageIdin chunks - Use simple ID generation (e.g., incrementing counter, UUID)
- Demonstrate consistent IDs across chunks of the same message
- Modify example agents to generate and include
-
Update example clients:
- Update clients to consume
messageIdfield - Use IDs to properly group chunks into messages
- Demonstrate clear message boundary rendering
- Update clients to consume
Backward Compatibility
ThemessageId field is optional to ensure this is a non-breaking change. Agents SHOULD include the messageId field, but it is not required for v1 compatibility. Features that rely on messageId (such as future message editing capabilities) will implicitly require the field to be present - Agents that don’t provide it simply won’t support those features.
Making this field required will be considered for a future v2 version of the protocol after wide adoption.
Frequently asked questions
What alternative approaches did you consider, and why did you settle on this one?
-
Continue using
_metafield - This is the current workaround but:- Not standardized across implementations
- Doesn’t signal semantic importance
- Easy to overlook or implement inconsistently
-
Detect message boundaries heuristically - Clients could infer boundaries from timing, content types, or session state:
- Unreliable and fragile
- Doesn’t work for all scenarios (e.g., consecutive same-type messages)
- Creates inconsistent behavior across implementations
-
Use explicit “message start/end” markers - Wrap messages with begin/end notifications:
- More complex protocol interaction
- Requires additional notifications
- More state to track on both sides
-
Agent-only message IDs - Have the Agent generate all IDs (including for user messages):
- Consistent with protocol patterns (
sessionId,terminalId,toolCallId) - But creates complexity for returning user message IDs (response comes after streaming)
- Not all agents support passing user message IDs through
- Clients may need IDs immediately for deduplication or multi-client scenarios
- Consistent with protocol patterns (
messageId is:
- Simple - Just one new field with clear semantics
- Flexible - Enables future capabilities without further protocol changes
- Practical - Clients generate IDs for their messages, agents for theirs
- Collision-safe - UUID format ensures uniqueness across both sides
Who generates message IDs?
Both clients and agents can generate message IDs, each for their own messages:- For user messages: The Client generates a UUID and includes it as
messageIdin thesession/promptrequest. The Agent echoes this ID asuserMessageIdin the response to confirm it was recorded. If the client doesn’t provide one, the Agent MAY assign one and return it in the response so the client knows the assigned ID. - For agent messages: The Agent generates the UUID when creating its response and includes it in session update chunks.
sessionId, terminalId, toolCallId) which are agent-generated, but provides practical benefits:
- Immediate availability - Clients have the ID as soon as they send the message, without waiting for a response
- Deduplication - Clients can use IDs to deduplicate messages on
session/loador when echoing to multiple clients - Collision-safe - UUID format ensures uniqueness without coordination
- Adapter-friendly - Adapters for agents that don’t support message IDs can simply not pass them through
Should this field be required or optional?
The field is optional for v1 to ensure backward compatibility. Agents SHOULD includemessageId, but it is not required. Features that depend on messageId (such as message editing) will implicitly require it - if an Agent doesn’t provide messageId, those features simply won’t be available.
Making this field required will be considered for a future v2 version of the protocol.
What format should message IDs use?
Both clients and agents MUST use UUID format for message IDs. This is required because both sides can generate IDs, and UUID format ensures:- No collisions - UUIDs are globally unique without coordination between client and agent
- Interoperability - Both sides use the same format, so either side can rely on uniqueness guarantees
- Simplicity - Standard libraries available in all languages
messageId values are UUIDs, implementations SHOULD treat them as opaque strings when reading/comparing them, and not parse or interpret their internal structure.
What about message IDs across session loads?
When a session is loaded viasession/load, the Agent may:
- Preserve original message IDs if replaying the conversation history
- Generate new message IDs if only exposing current state
Does this apply to other session updates like tool calls or plan updates?
This RFD addressesagent_message_chunk, user_message_chunk, and agent_thought_chunk updates.
Other session update types (like tool_call, plan) already have their own identification mechanisms:
- Tool calls use
toolCallId - Plan entries can be tracked by their position in the
entriesarray
messageId to other update types if use cases emerge.
Revision history
- 2026-02-17: Added “Message ID Acknowledgment” section to clarify that presence/absence of
userMessageIdin response indicates whether the Agent recorded the ID; clarified that UUID format is MUST (not SHOULD) since both sides generate IDs; renamed response field touserMessageIdfor clarity (request keepsmessageId) - 2026-01-29: Updated to allow both clients and agents to generate message IDs using UUID format
- 2025-11-09: Initial draft