Skip to main content
Draft — not yet ratified. This document is published for review. It describes the intended 1.0 behaviour, its wording is not final, and nothing here is covered by a compatibility promise until a version is frozen. Do not cite it as a stable reference.
Nearly everything in the protocol carries an OPTIONAL metadata object — events, messages, tool calls, tools, interrupts, resume entries. It is the open channel for what the protocol does not model: provider names, latencies, finish reasons, token counts, trace keys, whatever an application wants to ride along with its data.

The value

metadata is open by key. Any JSON value is allowed under a key, including null — a null there is meaningful data. The object itself is either absent or an object and MUST NOT be null. Unknown metadata keys are protocol-legal, not unrecognised material: the processing model never strips them, on any key.

Keys

The ag-ui key is reserved for the protocol’s own use. Every other key is application space; producers SHOULD prefix keys they invent with a vendor or application name, so two parties extending the same stream do not collide.

Merging

A streamed item is assembled from several events, and the interesting values — token usage, a finish reason — are only known at the end. So metadata accumulates rather than being fixed when the item opens: a consumer MUST merge the metadata of every event making up an item, key by key, with the last write winning for each key. The merge MUST NOT recurse. A key’s value is replaced outright, so an object or array under a key is replaced wholesale rather than blended with what was there before.

Where each event’s metadata lands

Merging needs a target, and the target is the thing the event builds — not always the message:
Tool-call metadata landing on the call rather than its parent message is deliberate. One assistant message can own several tool calls, so folding their metadata into the parent would make the result depend on the order the calls happened to interleave. Each call keeping its own is what keeps it unambiguous.
An event that builds no durable item has no merge target: its metadata is the event’s own, visible to middleware and to whatever observes the stream, and the protocol assigns it nowhere further. A consumer MAY surface it — a run’s RUN_FINISHED metadata in its own bookkeeping, say — but no rule requires it.

Stored metadata

Messages, tool calls, tools, interrupts and resume entries carry metadata as a plain stored field of their own. Nothing merges into those except the events listed above; a tool’s metadata in run input, or an interrupt’s, is simply data travelling with its carrier.

Data Types

Metadata is defined by the schema reference, and every carrier’s entry there lists its metadata field.