What is CreateMessageResult?
CreateMessageResult is the response envelope that a client returns to a server after completing a sampling request via the MCP createMessage capability. When a server invokes client-side sampling — asking the connected LLM client to generate a completion — the client executes the request against its own model and wraps the result in this structure before handing control back. It is the counterpart to CreateMessageRequest and closes the sampling round-trip. The interface carries four fields. The required role field is always "assistant", reflecting that the content was generated by the model rather than a human turn. The required content field holds the actual generated payload, which may be TextContent, ImageContent, or AudioContent depending on what the model produced. The required model field reports the exact model identifier that fulfilled the request — this may differ from any model hint the server suggested, since the client retains authority over model selection. The optional stopReason indicates why generation halted: common values are "endTurn" (natural completion), "maxTokens" (context or output limit reached), and "stopSequence" (a caller-specified stop string was hit). A key design principle here is that the server does not control which model runs; it can express preferences via model hints in the request, but CreateMessageResult's model field is the authoritative record of what actually executed. Servers should inspect stopReason before treating the response as complete — a "maxTokens" result may indicate a truncated response that requires a follow-up request or adjusted parameters.
When to use
Always, in response to a successful sampling request.
When NOT to use
If the user declined — return a JSON-RPC error instead.
Notes
stopReason drives server workflow logic
Do not assume a response is semantically complete just because a result was returned. When stopReason is 'maxTokens', the generated content has been cut off mid-stream and the server may need to issue a continuation request or surface a warning. A missing stopReason (null/undefined) should be treated the same as an unknown stop condition — defensively, not as a clean end-turn.
model field may differ from hints
The MCP spec gives clients full authority over model selection. A server that sends a model hint like 'claude-3-5-sonnet' may receive a result whose model field names a different variant or an entirely different provider. Servers should log or store the actual model value for auditability and should never hard-code assumptions about which model ran based on what was requested.
Content type is runtime-determined
The content field is a union of TextContent, ImageContent, and AudioContent. In practice, most sampling interactions return TextContent, but servers must be prepared to handle or reject non-text content gracefully. Assuming TextContent without a type-guard check is a common source of runtime errors, especially as multimodal clients become more prevalent.
Role is structurally fixed, not validated
The spec mandates role is 'assistant', but many SDK implementations do not enforce this at the type level beyond a string literal. If you are building a server that relays CreateMessageResult content into a conversation history, explicitly validate that role === 'assistant' before insertion to avoid corrupting the turn structure.
No streaming variant exists in base spec
CreateMessageResult represents a fully resolved, non-streaming response. The base MCP specification does not define a streaming counterpart for sampling results, so the entire completion must be buffered by the client before the result is returned. For very long generations this can introduce noticeable latency; server-side timeout handling on the createMessage call should account for the full generation time, not just network round-trip.
Fields
| Field | Type | Required | Purpose |
|---|---|---|---|
| role | 'assistant' | yes | Always 'assistant'. |
| content | TextContent | ImageContent | AudioContent | yes | Assistant response content. |
| model | string | yes | Identifier of the model that produced this output. |
| stopReason | string? | no | 'endTurn' | 'maxTokens' | 'stopSequence' | custom. |
Examples
Successful completion
{
"role": "assistant",
"content": { "type": "text", "text": "Here is the summary..." },
"model": "claude-sonnet-4-6",
"stopReason": "endTurn"
}
Common mistakes
❌ Returning role:'user'
✅ Sampling results are always role:'assistant'.