What is Annotations?
Annotations is a lightweight metadata object that can be attached to content blocks and resource references to give hosts contextual hints about how to handle that content. It is not a transport-level construct — it lives inside individual content items and is read by the host or client after a tool result, resource read, or prompt response is delivered. Think of it as an advisory layer: nothing in MCP enforces the annotations, but well-behaved clients use them to filter, sort, or visually distinguish content before presenting it to the end user. Within the MCP message flow, Annotations travel as an optional field on types like TextContent, ImageContent, and EmbeddedResource. The audience array (Role[]) lets a server declare whether a piece of content is addressed to the "user", the "assistant", or both — enabling a client to hide assistant-only reasoning traces from the chat UI without stripping them from the model context. The priority field (0.0–1.0) is a normalized importance rank; 1.0 is highest. lastModified accepts any ISO 8601 string and is purely informational for cache or freshness logic. All three fields are optional, so the minimal valid Annotations object is an empty dict. The type was stabilized in the 2024-11-05 spec revision alongside the broader content-block model; earlier drafts lacked the audience concept entirely. Because the fields are advisory, servers should not assume clients will act on them, and clients should degrade gracefully when any field is absent.
When to use
When you want the host to bias rendering — e.g., low-priority logs hidden by default.
When NOT to use
Never as access control — these are hints, not security.
Notes
audience filtering is client-enforced
Nothing in the protocol prevents a client from ignoring the audience field entirely. Production clients that rely on audience to gate visibility (e.g., hiding chain-of-thought blocks tagged assistant-only) must implement the check themselves. If you are building a thin client wrapper, test explicitly that assistant-targeted blocks do not surface in the end-user UI.
priority is a hint, not a sort key
The spec describes priority as a value between 0 and 1 inclusive, with higher values indicating greater importance, but it does not define what 'importance' means operationally. Some hosts use it to rank resource suggestions; others ignore it. Avoid encoding business logic in priority — treat it as a low-fidelity display hint and document your convention in your server's README.
lastModified has no canonicalized format enforcement
The field type is string, not a date object, so any ISO 8601 variant is technically valid. Clients that parse lastModified for cache-invalidation logic must handle timezone offsets, Z-suffix variants, and millisecond precision differences. Prefer UTC with milliseconds (e.g., 2025-01-15T10:30:00.000Z) for maximum interoperability with JavaScript Date.parse and Python datetime.fromisoformat.
empty vs. absent Annotations behave identically
Passing an empty Annotations object {} is semantically equivalent to omitting the annotations field altogether. SDK-generated stubs in the TypeScript and Python SDKs both normalize missing annotations to undefined/None rather than an empty object, so equality checks against {} will fail. Always use a null/None check rather than deep-equality when testing whether annotations are present.
Fields
| Field | Type | Required | Purpose |
|---|---|---|---|
| audience | Role[]? | no | Intended viewers — 'user' and/or 'assistant'. |
| priority | number? | no | 0–1; higher means more important. |
| lastModified | string? | no | ISO 8601 last-modified timestamp. |
Examples
User-facing high priority
{ "audience": ["user"], "priority": 0.9 }
Common mistakes
❌ Using priority > 1
✅ Range is 0–1 inclusive.