Sweep 22 Sep 2026 · 17:19Z Build v2.1.280 501 read Stable v2.1.267 Latest v2.1.280 Next v2.1.280 Feeds RSS JSON llms.txt Unofficial
One change · api

mid-conversation-system-messages changed

build-with-claude/mid-conversation-system-messages

Nearest release: v2.1.280, published under an hour before this site recorded the change. Shown because the two are within 24 hours of each other. Nothing here says the release caused the edit.

Recorded here
Lines+1,196added
Lines−26removed
From line 15 where the diff opens
First seen 14 Aug 2026 this site's first read of the page
Recorded edits8to this page, all time

### Define tools in a message (beta) ### Add an MCP server mid-conversation (beta)

The whole hunk

from line 15, old and new numbered
/
lines

The two sides of this change are more than 400 edits apart, too far apart to line up, so this is the differ's own diff of it and the words inside a line are not marked.

from line 15
1515<Note>
1616 Mid-conversation system messages are available on the Claude API, [Claude in Amazon Bedrock](https://platform.claude.com/docs/en/build-with-claude/claude-in-amazon-bedrock), and [Google Cloud](https://platform.claude.com/docs/en/build-with-claude/claude-on-vertex-ai).
1717 
18 This feature is available on Claude Fable 5.1, [Claude Mythos 5.1](https://anthropic.com/glasswing), Claude Fable 5, [Claude Mythos 5](https://anthropic.com/glasswing), Claude Opus 4.8, and Claude Opus 5. No beta header is required for mid-conversation system messages. This feature is not available on Claude Sonnet 5. Use the top-level `system` field there instead.
19 
20 [Mid-conversation tool changes](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages#mid-conversation-tool-changes) are in beta and require the `mid-conversation-tool-changes-2026-07-01` beta header. They are available on the same models, on the Claude API, Amazon Bedrock, and Google Cloud.
18 This feature is available on Claude Fable 5.1, [Claude Mythos 5.1](https://anthropic.com/glasswing), Claude Fable 5, [Claude Mythos 5](https://anthropic.com/glasswing), Claude Opus 5.5, Claude Opus 4.8, and Claude Opus 5. No beta header is required for mid-conversation system messages. This feature is not available on Claude Sonnet 5. Use the top-level `system` field there instead.
19 
20 [Mid-conversation tool changes](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages#mid-conversation-tool-changes) are in beta and require the `mid-conversation-tool-changes-2026-07-01` beta header. They are available on the same models, on the Claude API, Amazon Bedrock, and Google Cloud. [Defining a tool inside a `tool_addition` block](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages#define-tools-in-a-message-beta) uses the `inline-tools-2026-09-15` beta header in place of that one, and is available on the Claude API. [Adding an MCP server that way](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages#add-an-mcp-server-mid-conversation-beta) also needs the `mcp-client-2026-09-15` beta header.
2121 
2222 [Turn-scoped system messages](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages#turn-scoped-system-messages) (`clear_at`) are in beta and require the `mid-conversation-system-clear-at-2026-08-21` beta header, on the same models and platforms as mid-conversation system messages.
2323</Note>
from line 26
2626 
2727The `tools` array sits even earlier in the hashed request prefix than the top-level `system` field, so editing it invalidates the [prompt cache](https://platform.claude.com/docs/en/build-with-claude/prompt-caching) for the entire conversation. Mid-conversation tool changes are the tools counterpart to mid-conversation system messages. Instead of fixing the tool list for the lifetime of the conversation, you change which tools are offered to the model between turns: declare the full tool set in `tools` up front, then use `tool_addition` and `tool_removal` blocks to offer a tool to the model, or withdraw it, from a specific point in the conversation onward. The `tools` array itself never changes, so the cached prefix stays intact.
2828 
29`tool_addition` and `tool_removal` are content blocks in the `content` array of a `role: "system"` message, and they can be mixed with `text` blocks in the same message. The message follows the placement rules for any mid-conversation system message, with one extra restriction after a paused turn (see [Limitations](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages#limitations)), and the change applies from that point in the conversation onward. Each block's `tool` field references a tool rather than defining one: `{"type": "tool_reference", "name": "..."}` names a tool declared in the request's `tools` array, and [MCP connector](https://platform.claude.com/docs/en/agents-and-tools/mcp-connector) tools can be referenced individually with `mcp_tool_reference` (`server_name` and `name`) or as a whole toolset with `mcp_toolset_reference` (`server_name`). Referencing a name that is not declared in `tools` returns a 400 error (on the Claude API, with `error.details.error_code` set to `tool_reference_unresolved`).
29`tool_addition` and `tool_removal` are content blocks in the `content` array of a `role: "system"` message, and they can be mixed with `text` blocks in the same message. The message follows the placement rules for any mid-conversation system message, with one extra restriction after a paused turn (see [Limitations](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages#limitations)), and the change applies from that point in the conversation onward. Each block's `tool` field references a tool rather than defining one: `{"type": "tool_reference", "name": "..."}` names a tool declared in the request's `tools` array, and [MCP connector](https://platform.claude.com/docs/en/agents-and-tools/mcp-connector) tools can be referenced individually with `mcp_tool_reference` (`server_name` and `name`) or as a whole toolset with `mcp_toolset_reference` (`server_name`). Referencing a name that is not declared in `tools` returns a 400 error (on the Claude API, with `error.details.error_code` set to `tool_reference_unresolved`). With the `inline-tools-2026-09-15` beta header, a `tool_addition` block can instead [carry the tool's full definition](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages#define-tools-in-a-message-beta).
3030 
3131Every tool declared in `tools` is offered to the model from the start of the conversation unless it is declared with `defer_loading: true`, which keeps it withheld until a `tool_addition` block surfaces it. `tool_addition` also re-offers a tool that an earlier `tool_removal` withdrew.
3232 
from line 40
4040 -H "anthropic-version: 2023-06-01" \
4141 -H "anthropic-beta: mid-conversation-tool-changes-2026-07-01" \
4242 -d '{
43 "model": "claude-opus-5",
43 "model": "claude-opus-5-5",
4444 "max_tokens": 1024,
4545 "tools": [
4646 {
from line 76
7676 ```bash CLI
7777 ant beta:messages create --beta mid-conversation-tool-changes-2026-07-01 \
7878 --transform 'content.#(type=="text").text' --raw-output <<'YAML'
79 model: claude-opus-5
79 model: claude-opus-5-5
8080 max_tokens: 1024
8181 tools:
8282 - name: get_weather
from line 105
105105 client = anthropic.Anthropic()
106106 
107107 response = client.beta.messages.create(
108 model="claude-opus-5",
108 model="claude-opus-5-5",
109109 max_tokens=1024,
110110 betas=["mid-conversation-tool-changes-2026-07-01"],
111111 # The full tool set is declared up front and never changes, so the
from line 152
152152 const client = new Anthropic();
153153 
154154 const response = await client.beta.messages.create({
155 model: "claude-opus-5",
155 model: "claude-opus-5-5",
156156 max_tokens: 1024,
157157 betas: ["mid-conversation-tool-changes-2026-07-01"],
158158 // The full tool set is declared up front and never changes, so the
from line 205
205205 
206206 var response = await client.Beta.Messages.Create(new MessageCreateParams
207207 {
208 Model = Messages::Model.ClaudeOpus5,
208 Model = Messages::Model.ClaudeOpus5_5,
209209 MaxTokens = 1024,
210210 Betas = ["mid-conversation-tool-changes-2026-07-01"],
211211 // The full tool set is declared up front and never changes, so the
from line 259
259259 client := anthropic.NewClient()
260260 
261261 response, err := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{
262 Model: anthropic.ModelClaudeOpus5,
262 Model: anthropic.ModelClaudeOpus5_5,
263263 MaxTokens: 1024,
264264 Betas: []anthropic.AnthropicBeta{"mid-conversation-tool-changes-2026-07-01"},
265265 // The full tool set is declared up front and never changes, so the
from line 331
331331 .build();
332332 
333333 MessageCreateParams params = MessageCreateParams.builder()
334 .model(Model.CLAUDE_OPUS_5)
334 .model(Model.CLAUDE_OPUS_5_5)
335335 .maxTokens(1024)
336336 .addBeta("mid-conversation-tool-changes-2026-07-01")
337337 .addTool(weatherTool)
from line 358
358358 $client = new Client();
359359 
360360 $response = $client->beta->messages->create(
361 model: 'claude-opus-5',
361 model: 'claude-opus-5-5',
362362 maxTokens: 1024,
363363 betas: ['mid-conversation-tool-changes-2026-07-01'],
364364 // The full tool set is declared up front and never changes, so the
from line 407
407407 client = Anthropic::Client.new
408408 
409409 response = client.beta.messages.create(
410 model: "claude-opus-5",
410 model: "claude-opus-5-5",
411411 max_tokens: 1024,
412412 betas: ["mid-conversation-tool-changes-2026-07-01"],
413413 # The full tool set is declared up front and never changes, so the
from line 448
448448 ```
449449</CodeGroup>
450450 
451### Define tools in a message (beta)
452 
453With the `inline-tools-2026-09-15` beta header, a `tool_addition` block can define a tool by value, carrying its full definition, instead of naming it by reference. This lets you introduce a tool that is unknown at the start of the conversation, or whose schema changes later, by appending a `role: "system"` message. The `tools` array and every earlier message stay exactly as sent, so the prompt cache still hits and only the appended message is processed as new input. The one exception, a `tools` array with no non-deferred tool, is covered in the rules below. The header also covers adding and removing tools by reference, so you don't need to send `mid-conversation-tool-changes-2026-07-01` as well.
454 
455Wrap the definition in a `tool` object of type `tool_definition`. The `definition` is a `tools` entry, such as a custom tool or an Anthropic-defined client or server tool, with its usual configuration, including `cache_control` and `defer_loading`. During the beta, some tool types (the computer use tool among them) can't be defined in a message yet and return a 400 error that says so; declare those in `tools` and add them by reference. For example, to define a custom tool mid-conversation:
456 
457```json
458{
459 "role": "system",
460 "content": [
461 {
462 "type": "tool_addition",
463 "tool": {
464 "type": "tool_definition",
465 "definition": {
466 "name": "db_query",
467 "description": "Run a read-only SQL query against the analytics database.",
468 "input_schema": {
469 "type": "object",
470 "properties": { "sql": { "type": "string" } },
471 "required": ["sql"]
472 }
473 }
474 }
475 }
476 ]
477}
478```
479 
480From that position onward, the model can call the tool the same way it calls a tool declared in `tools`. Sending an identical definition again changes nothing, so a client can safely resend it, for example on a retry.
481 
482The following request keeps `get_weather` in `tools` and defines `db_query` after the first user turn:
483 
484<CodeGroup>
485 ```bash cURL
486 curl https://api.anthropic.com/v1/messages \
487 -H "content-type: application/json" \
488 -H "x-api-key: $ANTHROPIC_API_KEY" \
489 -H "anthropic-version: 2023-06-01" \
490 -H "anthropic-beta: inline-tools-2026-09-15" \
491 -d '{
492 "model": "claude-opus-5-5",
493 "max_tokens": 1024,
494 "tools": [
495 {
496 "name": "get_weather",
497 "description": "Get the current weather for a location.",
498 "input_schema": {
499 "type": "object",
500 "properties": {
501 "location": {"type": "string", "description": "City name"}
502 },
503 "required": ["location"]
504 }
505 }
506 ],
507 "messages": [
508 {
509 "role": "user",
510 "content": "How many orders shipped yesterday?"
511 },
512 {
513 "role": "system",
514 "content": [
515 {
516 "type": "tool_addition",
517 "tool": {
518 "type": "tool_definition",
519 "definition": {
520 "name": "db_query",
521 "description": "Run a read-only SQL query against the analytics database.",
522 "input_schema": {
523 "type": "object",
524 "properties": {"sql": {"type": "string"}},
525 "required": ["sql"]
526 }
527 }
528 }
529 }
530 ]
531 }
532 ]
533 }'
534 ```
535 
536 <MultiFileExample language="cli" label="CLI">
537 ```bash CLI
538 ant beta:messages create --beta inline-tools-2026-09-15 < request.yaml
539 ```
540 
541 <File filename="request.yaml">
542 ```yaml
543 model: claude-opus-5-5
544 max_tokens: 1024
545 # Keep at least one non-deferred tool in `tools`, so a tool defined
546 # later doesn't change the start of the rendered prompt.
547 tools:
548 - name: get_weather
549 description: Get the current weather for a location.
550 input_schema:
551 type: object
552 properties:
553 location:
554 type: string
555 description: City name
556 required:
557 - location
558 messages:
559 - role: user
560 content: How many orders shipped yesterday?
561 # Define db_query by value from this point onward. `tools` and the
562 # earlier messages stay exactly as sent, so the cache still hits.
563 - role: system
564 content:
565 - type: tool_addition
566 tool:
567 type: tool_definition
568 definition:
569 name: db_query
570 description: Run a read-only SQL query against the analytics database.
571 input_schema:
572 type: object
573 properties:
574 sql:
575 type: string
576 required:
577 - sql
578 ```
579 </File>
580 </MultiFileExample>
581 
582 ```python Python
583 client = anthropic.Anthropic()
584 
585 response = client.beta.messages.create(
586 model="claude-opus-5-5",
587 max_tokens=1024,
588 betas=["inline-tools-2026-09-15"],
589 # Keep at least one non-deferred tool in `tools`, so a tool defined
590 # later doesn't change the start of the rendered prompt.
591 tools=[
592 {
593 "name": "get_weather",
594 "description": "Get the current weather for a location.",
595 "input_schema": {
596 "type": "object",
597 "properties": {
598 "location": {"type": "string", "description": "City name"},
599 },
600 "required": ["location"],
601 },
602 },
603 ],
604 messages=[
605 {"role": "user", "content": "How many orders shipped yesterday?"},
606 # Define db_query by value from this point onward. `tools` and the
607 # earlier messages stay exactly as sent, so the cache still hits.
608 {
609 "role": "system",
610 "content": [
611 {
612 "type": "tool_addition",
613 "tool": {
614 "type": "tool_definition",
615 "definition": {
616 "name": "db_query",
617 "description": "Run a read-only SQL query against the analytics database.",
618 "input_schema": {
619 "type": "object",
620 "properties": {"sql": {"type": "string"}},
621 "required": ["sql"],
622 },
623 },
624 },
625 },
626 ],
627 },
628 ],
629 )
630 
631 for block in response.content:
632 if block.type == "tool_use":
633 print(block.name, block.input)
634 ```
635 
636 ```typescript TypeScript
637 const client = new Anthropic();
638 
639 const response = await client.beta.messages.create({
640 model: "claude-opus-5-5",
641 max_tokens: 1024,
642 betas: ["inline-tools-2026-09-15"],
643 // Keep at least one non-deferred tool in `tools`, so a tool defined
644 // later doesn't change the start of the rendered prompt.
645 tools: [
646 {
647 name: "get_weather",
648 description: "Get the current weather for a location.",
649 input_schema: {
650 type: "object",
651 properties: {
652 location: { type: "string", description: "City name" }
653 },
654 required: ["location"]
655 }
656 }
657 ],
658 messages: [
659 { role: "user", content: "How many orders shipped yesterday?" },
660 // Define db_query by value from this point onward. `tools` and the
661 // earlier messages stay exactly as sent, so the cache still hits.
662 {
663 role: "system",
664 content: [
665 {
666 type: "tool_addition",
667 tool: {
668 type: "tool_definition",
669 definition: {
670 name: "db_query",
671 description: "Run a read-only SQL query against the analytics database.",
672 input_schema: {
673 type: "object",
674 properties: { sql: { type: "string" } },
675 required: ["sql"]
676 }
677 }
678 }
679 }
680 ]
681 }
682 ]
683 });
684 
685 for (const block of response.content) {
686 if (block.type === "tool_use") {
687 console.log(block.name, JSON.stringify(block.input));
688 }
689 }
690 ```
691 
692 ```csharp C#
693 using Anthropic.Models.Beta;
694 using Anthropic.Models.Beta.Messages;
695 using Messages = Anthropic.Models.Messages;
696 
697 AnthropicClient client = new();
698 
699 var response = await client.Beta.Messages.Create(new MessageCreateParams
700 {
701 Model = Messages::Model.ClaudeOpus5_5,
702 MaxTokens = 1024,
703 Betas = [AnthropicBeta.InlineTools2026_09_15],
704 // Keep at least one non-deferred tool in `Tools`, so a tool defined
705 // later doesn't change the start of the rendered prompt.
706 Tools =
707 [
708 new BetaTool
709 {
710 Name = "get_weather",
711 Description = "Get the current weather for a location.",
712 InputSchema = new InputSchema
713 {
714 Properties = new Dictionary<string, JsonElement>
715 {
716 ["location"] = JsonSerializer.SerializeToElement(new { type = "string", description = "City name" }),
717 },
718 Required = ["location"],
719 },
720 },
721 ],
722 Messages =
723 [
724 new() { Role = Role.User, Content = "How many orders shipped yesterday?" },
725 // Define db_query by value from this point onward. `Tools` and the
726 // earlier messages stay exactly as sent, so the cache still hits.
727 new()
728 {
729 Role = Role.System,
730 Content = new(
731 [
732 new BetaRequestToolAdditionBlock
733 {
734 Tool = new BetaToolChangeToolDefinitionParam
735 {
736 Definition = new BetaTool
737 {
738 Name = "db_query",
739 Description = "Run a read-only SQL query against the analytics database.",
740 InputSchema = new InputSchema
741 {
742 Properties = new Dictionary<string, JsonElement>
743 {
744 ["sql"] = JsonSerializer.SerializeToElement(new { type = "string" }),
745 },
746 Required = ["sql"],
747 },
748 },
749 },
750 },
751 ]),
752 },
753 ],
754 });
755 
756 foreach (var block in response.Content)
757 {
758 if (block.TryPickToolUse(out var toolUse))
759 {
760 Console.WriteLine($"{toolUse.Name} {JsonSerializer.Serialize(toolUse.Input)}");
761 }
762 }
763 ```
764 
765 ```go Go
766 client := anthropic.NewClient()
767 
768 response, err := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{
769 Model: anthropic.ModelClaudeOpus5_5,
770 MaxTokens: 1024,
771 Betas: []anthropic.AnthropicBeta{anthropic.AnthropicBetaInlineTools2026_09_15},
772 // Keep at least one non-deferred tool in Tools, so a tool defined
773 // later doesn't change the start of the rendered prompt.
774 Tools: []anthropic.BetaToolUnionParam{
775 {OfTool: &anthropic.BetaToolParam{
776 Name: "get_weather",
777 Description: anthropic.String("Get the current weather for a location."),
778 InputSchema: anthropic.BetaToolInputSchemaParam{
779 Properties: map[string]any{
780 "location": map[string]any{
781 "type": "string",
782 "description": "City name",
783 },
784 },
785 Required: []string{"location"},
786 },
787 }},
788 },
789 Messages: []anthropic.BetaMessageParam{
790 anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("How many orders shipped yesterday?")),
791 // Define db_query by value from this point onward. Tools and the
792 // earlier messages stay exactly as sent, so the cache still hits.
793 {
794 Role: anthropic.BetaMessageParamRoleSystem,
795 Content: []anthropic.BetaContentBlockParamUnion{
796 anthropic.NewBetaToolAdditionBlock(anthropic.BetaToolChangeToolDefinitionParam{
797 Definition: anthropic.BetaToolUnionParam{OfTool: &anthropic.BetaToolParam{
798 Name: "db_query",
799 Description: anthropic.String("Run a read-only SQL query against the analytics database."),
800 InputSchema: anthropic.BetaToolInputSchemaParam{
801 Properties: map[string]any{
802 "sql": map[string]any{"type": "string"},
803 },
804 Required: []string{"sql"},
805 },
806 }},
807 }),
808 },
809 },
810 },
811 })
812 if err != nil {
813 log.Fatal(err)
814 }
815 
816 for _, block := range response.Content {
817 if toolUse, ok := block.AsAny().(anthropic.BetaToolUseBlock); ok {
818 fmt.Println(toolUse.Name, toolUse.Input)
819 }
820 }
821 ```
822 
823 ```java Java
824 import com.anthropic.models.beta.AnthropicBeta;
825 import com.anthropic.models.beta.messages.BetaContentBlockParam;
826 import com.anthropic.models.beta.messages.BetaMessage;
827 import com.anthropic.models.beta.messages.BetaRequestToolAdditionBlock;
828 import com.anthropic.models.beta.messages.BetaTool;
829 import com.anthropic.models.beta.messages.MessageCreateParams;
830 // ...
831 
832 void main() {
833 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
834 
835 BetaTool weatherTool = BetaTool.builder()
836 .name("get_weather")
837 .description("Get the current weather for a location.")
838 .inputSchema(BetaTool.InputSchema.builder()
839 .properties(BetaTool.InputSchema.Properties.builder()
840 .putAdditionalProperty("location", JsonValue.from(Map.of(
841 "type", "string",
842 "description", "City name")))
843 .build())
844 .addRequired("location")
845 .build())
846 .build();
847 
848 BetaTool dbQueryTool = BetaTool.builder()
849 .name("db_query")
850 .description("Run a read-only SQL query against the analytics database.")
851 .inputSchema(BetaTool.InputSchema.builder()
852 .properties(BetaTool.InputSchema.Properties.builder()
853 .putAdditionalProperty("sql", JsonValue.from(Map.of("type", "string")))
854 .build())
855 .addRequired("sql")
856 .build())
857 .build();
858 
859 MessageCreateParams params = MessageCreateParams.builder()
860 .model(Model.CLAUDE_OPUS_5_5)
861 .maxTokens(1024)
862 .addBeta(AnthropicBeta.INLINE_TOOLS_2026_09_15)
863 // Keep at least one non-deferred tool in `tools`, so a tool defined
864 // later doesn't change the start of the rendered prompt.
865 .addTool(weatherTool)
866 .addUserMessage("How many orders shipped yesterday?")
867 // Define db_query by value from this point onward. `tools` and the
868 // earlier messages stay exactly as sent, so the cache still hits.
869 .addSystemMessageOfBetaContentBlockParams(List.of(
870 BetaContentBlockParam.ofToolAddition(BetaRequestToolAdditionBlock.builder()
871 .definitionTool(dbQueryTool)
872 .build())))
873 .build();
874 
875 BetaMessage response = client.beta().messages().create(params);
876 response.content().stream()
877 .flatMap(block -> block.toolUse().stream())
878 .forEach(toolUse -> IO.println(toolUse.name() + " " + toolUse._input()));
879 }
880 ```
881 
882 ```php PHP
883 use Anthropic\Beta\AnthropicBeta;
884 use Anthropic\Beta\Messages\BetaToolUseBlock;
885 // ...
886 
887 $client = new Client();
888 
889 $response = $client->beta->messages->create(
890 model: Model::CLAUDE_OPUS_5_5,
891 maxTokens: 1024,
892 betas: [AnthropicBeta::INLINE_TOOLS_2026_09_15],
893 // Keep at least one non-deferred tool in `tools`, so a tool defined
894 // later doesn't change the start of the rendered prompt.
895 tools: [
896 [
897 'name' => 'get_weather',
898 'description' => 'Get the current weather for a location.',
899 'input_schema' => [
900 'type' => 'object',
901 'properties' => [
902 'location' => [
903 'type' => 'string',
904 'description' => 'City name',
905 ],
906 ],
907 'required' => ['location'],
908 ],
909 ],
910 ],
911 messages: [
912 ['role' => 'user', 'content' => 'How many orders shipped yesterday?'],
913 // Define db_query by value from this point onward. `tools` and the
914 // earlier messages stay exactly as sent, so the cache still hits.
915 [
916 'role' => 'system',
917 'content' => [
918 [
919 'type' => 'tool_addition',
920 'tool' => [
921 'type' => 'tool_definition',
922 'definition' => [
923 'name' => 'db_query',
924 'description' => 'Run a read-only SQL query against the analytics database.',
925 'input_schema' => [
926 'type' => 'object',
927 'properties' => ['sql' => ['type' => 'string']],
928 'required' => ['sql'],
929 ],
930 ],
931 ],
932 ],
933 ],
934 ],
935 ],
936 );
937 
938 foreach ($response->content as $block) {
939 if ($block instanceof BetaToolUseBlock) {
940 echo $block->name, ' ', json_encode($block->input), PHP_EOL;
941 }
942 }
943 ```
944 
945 ```ruby Ruby
946 client = Anthropic::Client.new
947 
948 response = client.beta.messages.create(
949 model: Anthropic::Model::CLAUDE_OPUS_5_5,
950 max_tokens: 1024,
951 betas: [Anthropic::AnthropicBeta::INLINE_TOOLS_2026_09_15],
952 # Keep at least one non-deferred tool in `tools`, so a tool defined
953 # later doesn't change the start of the rendered prompt.
954 tools: [
955 {
956 name: "get_weather",
957 description: "Get the current weather for a location.",
958 input_schema: {
959 type: "object",
960 properties: {
961 location: { type: "string", description: "City name" }
962 },
963 required: ["location"]
964 }
965 }
966 ],
967 messages: [
968 { role: "user", content: "How many orders shipped yesterday?" },
969 # Define db_query by value from this point onward. `tools` and the
970 # earlier messages stay exactly as sent, so the cache still hits.
971 {
972 role: "system",
973 content: [
974 {
975 type: "tool_addition",
976 tool: {
977 type: "tool_definition",
978 definition: {
979 name: "db_query",
980 description: "Run a read-only SQL query against the analytics database.",
981 input_schema: {
982 type: "object",
983 properties: { sql: { type: "string" } },
984 required: ["sql"]
985 }
986 }
987 }
988 }
989 ]
990 }
991 ]
992 )
993 
994 response.content.each do |block|
995 puts "#{block.name} #{block.input}" if block.is_a?(Anthropic::Beta::BetaToolUseBlock)
996 end
997 ```
998</CodeGroup>
999 
1000The response's `content` includes a `tool_use` block for the new tool, for example:
1001 
1002```json
1003{
1004 "type": "tool_use",
1005 "id": "toolu_01A09q90qw90lq917835lq9",
1006 "name": "db_query",
1007 "input": {
1008 "sql": "SELECT COUNT(*) FROM orders WHERE shipped_at::date = CURRENT_DATE - 1"
1009 }
1010}
1011```
1012 
1013To change a tool's schema, or to move a server tool to a newer version, send a different definition under the same name. The new definition replaces the earlier one from that position onward. A definition that reuses the name of a different type of tool returns a 400 error with `error.details.error_code` set to `tool_name_conflict`. A newer version of the same tool doesn't count as a different type. `tool_removal` still takes a reference, and a removed tool can be defined or re-offered again later.
1014 
1015A few rules follow from where the definition renders:
1016 
1017* **Declare what you know up front.** A tool you know about at the first request belongs in `tools`, with `defer_loading: true` and a later `tool_addition` reference if the model shouldn't see it yet. Define by value only what is unknown at the first request or changes later.
1018* **Keep at least one non-deferred tool in `tools`.** A conversation whose `tools` array has no non-deferred tool is accepted, but the first tool it defines by value changes the start of the rendered prompt, which costs one full cache miss on that request. A [tool search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-search-tool) counts as non-deferred.
1019* **Dated tool types keep their own beta headers.** If a server tool you define by value requires its own beta header, send that header on every later request in the conversation.
1020* **`cache_control` goes on the block or in the definition, not both,** and counts toward the request's breakpoint limit. A deferred definition can't carry `cache_control`.
1021 
1022A request returns a 400 error with `error.details.error_code` set to `available_tools_limit_exceeded` when any of these limits is exceeded:
1023 
1024* More than 10,000 deferred tools are available after any message.
1025* More than 10,000 tools defined after the first user message are available after any message.
1026* The tool definitions sent after the first user message that are still available after any message total more than 4 MB (4,194,304 bytes).
1027* The rendered tool text is larger than 4 MB (4,194,304 bytes).
1028 
1029### Add an MCP server mid-conversation (beta)
1030 
1031To add an [MCP connector](https://platform.claude.com/docs/en/agents-and-tools/mcp-connector) server partway through a conversation, send the `mcp-client-2026-09-15` beta header along with `inline-tools-2026-09-15`. The `definition` in a `tool_addition` block can then be an `mcp_toolset`, so the server's tools become available without editing `tools`. List the server's connection details in `mcp_servers` as usual, then append the toolset where the server became available:
1032 
1033```json
1034{
1035 "role": "system",
1036 "content": [
1037 {
1038 "type": "tool_addition",
1039 "tool": {
1040 "type": "tool_definition",
1041 "definition": { "type": "mcp_toolset", "mcp_server_name": "calendar" }
1042 }
1043 }
1044 ]
1045}
1046```
1047 
1048The `mcp_toolset` object is the same one you would put in `tools`, including `default_config` and `configs`. A `tool_addition` block never holds a server URL or token. Those stay in `mcp_servers`.
1049 
1050The following request keeps `get_weather` in `tools`, lists the calendar server in `mcp_servers`, and adds the server's toolset after the first user turn:
1051 
1052<CodeGroup>
1053 ```bash cURL
1054 curl https://api.anthropic.com/v1/messages \
1055 -H "content-type: application/json" \
1056 -H "x-api-key: $ANTHROPIC_API_KEY" \
1057 -H "anthropic-version: 2023-06-01" \
1058 -H "anthropic-beta: inline-tools-2026-09-15,mcp-client-2026-09-15" \
1059 -d '{
1060 "model": "claude-opus-5-5",
1061 "max_tokens": 1024,
1062 "mcp_servers": [
1063 {
1064 "type": "url",
1065 "url": "https://mcp.example.com/calendar",
1066 "name": "calendar",
1067 "authorization_token": "YOUR_TOKEN"
1068 }
1069 ],
1070 "tools": [
1071 {
1072 "name": "get_weather",
1073 "description": "Get the current weather for a location.",
1074 "input_schema": {
1075 "type": "object",
1076 "properties": {
1077 "location": {"type": "string", "description": "City name"}
1078 },
1079 "required": ["location"]
1080 }
1081 }
1082 ],
1083 "messages": [
1084 {
1085 "role": "user",
1086 "content": "What'\''s on my calendar tomorrow?"
1087 },
1088 {
1089 "role": "system",
1090 "content": [
1091 {
1092 "type": "tool_addition",
1093 "tool": {
1094 "type": "tool_definition",
1095 "definition": {
1096 "type": "mcp_toolset",
1097 "mcp_server_name": "calendar"
1098 }
1099 }
1100 }
1101 ]
1102 }
1103 ]
1104 }'
1105 ```
1106 
1107 ```bash CLI
1108 ant beta:messages create \
1109 --beta inline-tools-2026-09-15,mcp-client-2026-09-15 <<'YAML'
1110 model: claude-opus-5-5
1111 max_tokens: 1024
1112 mcp_servers:
1113 - type: url
1114 url: https://mcp.example.com/calendar
1115 name: calendar
1116 authorization_token: YOUR_TOKEN
1117 tools:
1118 - name: get_weather
1119 description: Get the current weather for a location.
1120 input_schema:
1121 type: object
1122 properties:
1123 location:
1124 type: string
1125 description: City name
1126 required:
1127 - location
1128 messages:
1129 - role: user
1130 content: What's on my calendar tomorrow?
1131 # Make the calendar server's tools available from this point onward.
1132 # The block names the server; it never holds a URL or token.
1133 - role: system
1134 content:
1135 - type: tool_addition
1136 tool:
1137 type: tool_definition
1138 definition:
1139 type: mcp_toolset
1140 mcp_server_name: calendar
1141 YAML
1142 ```
1143 
1144 ```python Python
1145 client = anthropic.Anthropic()
1146 
1147 response = client.beta.messages.create(
1148 model="claude-opus-5-5",
1149 max_tokens=1024,
1150 betas=["inline-tools-2026-09-15", "mcp-client-2026-09-15"],
1151 mcp_servers=[
1152 {
1153 "type": "url",
1154 "url": "https://mcp.example.com/calendar",
1155 "name": "calendar",
1156 "authorization_token": "YOUR_TOKEN",
1157 },
1158 ],
1159 tools=[
1160 {
1161 "name": "get_weather",
1162 "description": "Get the current weather for a location.",
1163 "input_schema": {
1164 "type": "object",
1165 "properties": {
1166 "location": {"type": "string", "description": "City name"},
1167 },
1168 "required": ["location"],
1169 },
1170 },
1171 ],
1172 messages=[
1173 {"role": "user", "content": "What's on my calendar tomorrow?"},
1174 # Make the calendar server's tools available from this point onward.
1175 # The block names the server; it never holds a URL or token.
1176 {
1177 "role": "system",
1178 "content": [
1179 {
1180 "type": "tool_addition",
1181 "tool": {
1182 "type": "tool_definition",
1183 "definition": {
1184 "type": "mcp_toolset",
1185 "mcp_server_name": "calendar",
1186 },
1187 },
1188 },
1189 ],
1190 },
1191 ],
1192 )
1193 
1194 # The response starts with an mcp_tool_listing block for the calendar server,
1195 # so check each block's type instead of reading content[0].
1196 for block in response.content:
1197 match block.type:
1198 case "mcp_tool_listing":
1199 print(block.mcp_server_name, [tool.name for tool in block.tools])
1200 case "text":
1201 print(block.text)
1202 ```
1203 
1204 ```typescript TypeScript
1205 const client = new Anthropic();
1206 
1207 const response = await client.beta.messages.create({
1208 model: "claude-opus-5-5",
1209 max_tokens: 1024,
1210 betas: ["inline-tools-2026-09-15", "mcp-client-2026-09-15"],
1211 mcp_servers: [
1212 {
1213 type: "url",
1214 url: "https://mcp.example.com/calendar",
1215 name: "calendar",
1216 authorization_token: "YOUR_TOKEN"
1217 }
1218 ],
1219 tools: [
1220 {
1221 name: "get_weather",
1222 description: "Get the current weather for a location.",
1223 input_schema: {
1224 type: "object",
1225 properties: {
1226 location: { type: "string", description: "City name" }
1227 },
1228 required: ["location"]
1229 }
1230 }
1231 ],
1232 messages: [
1233 { role: "user", content: "What's on my calendar tomorrow?" },
1234 // Make the calendar server's tools available from this point onward.
1235 // The block names the server; it never holds a URL or token.
1236 {
1237 role: "system",
1238 content: [
1239 {
1240 type: "tool_addition",
1241 tool: {
1242 type: "tool_definition",
1243 definition: { type: "mcp_toolset", mcp_server_name: "calendar" }
1244 }
1245 }
1246 ]
1247 }
1248 ]
1249 });
1250 
1251 // The response starts with an mcp_tool_listing block for the calendar server,
1252 // so check each block's type instead of reading content[0].
1253 for (const block of response.content) {
1254 switch (block.type) {
1255 case "mcp_tool_listing":
1256 console.log(
1257 block.mcp_server_name,
1258 block.tools.map((tool) => tool.name)
1259 );
1260 break;
1261 case "text":
1262 console.log(block.text);
1263 break;
1264 }
1265 }
1266 ```
1267 
1268 ```csharp C#
1269 using Anthropic.Models.Beta;
1270 using Anthropic.Models.Beta.Messages;
1271 using Messages = Anthropic.Models.Messages;
1272 
1273 AnthropicClient client = new();
1274 
1275 var response = await client.Beta.Messages.Create(new MessageCreateParams
1276 {
1277 Model = Messages::Model.ClaudeOpus5_5,
1278 MaxTokens = 1024,
1279 Betas = [AnthropicBeta.InlineTools2026_09_15, AnthropicBeta.McpClient2026_09_15],
1280 McpServers =
1281 [
1282 new BetaRequestMcpServerUrlDefinition
1283 {
1284 Url = "https://mcp.example.com/calendar",
1285 Name = "calendar",
1286 AuthorizationToken = "YOUR_TOKEN",
1287 },
1288 ],
1289 Tools =
1290 [
1291 new BetaTool
1292 {
1293 Name = "get_weather",
1294 Description = "Get the current weather for a location.",
1295 InputSchema = new InputSchema
1296 {
1297 Properties = new Dictionary<string, JsonElement>
1298 {
1299 ["location"] = JsonSerializer.SerializeToElement(new { type = "string", description = "City name" }),
1300 },
1301 Required = ["location"],
1302 },
1303 },
1304 ],
1305 Messages =
1306 [
1307 new() { Role = Role.User, Content = "What's on my calendar tomorrow?" },
1308 // Make the calendar server's tools available from this point onward.
1309 // The block names the server; it never holds a URL or token.
1310 new()
1311 {
1312 Role = Role.System,
1313 Content = new(
1314 [
1315 new BetaRequestToolAdditionBlock
1316 {
1317 Tool = new BetaToolChangeToolDefinitionParam
1318 {
1319 Definition = new BetaMcpToolset("calendar"),
1320 },
1321 },
1322 ]),
1323 },
1324 ],
1325 });
1326 
1327 // The response starts with an mcp_tool_listing block for the calendar server,
1328 // so check each block's type instead of reading Content[0].
1329 foreach (var block in response.Content)
1330 {
1331 if (block.TryPickMcpToolListing(out var listing))
1332 {
1333 Console.WriteLine($"{listing.McpServerName} {JsonSerializer.Serialize(listing.Tools.Select(tool => tool.Name))}");
1334 }
1335 else if (block.TryPickText(out var text))
1336 {
1337 Console.WriteLine(text.Text);
1338 }
1339 }
1340 ```
1341 
1342 ```go Go
1343 client := anthropic.NewClient()
1344 
1345 response, err := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{
1346 Model: anthropic.ModelClaudeOpus5_5,
1347 MaxTokens: 1024,
1348 Betas: []anthropic.AnthropicBeta{
1349 anthropic.AnthropicBetaInlineTools2026_09_15,
1350 anthropic.AnthropicBetaMCPClient2026_09_15,
1351 },
1352 MCPServers: []anthropic.BetaRequestMCPServerURLDefinitionParam{
1353 {
1354 URL: "https://mcp.example.com/calendar",
1355 Name: "calendar",
1356 AuthorizationToken: anthropic.String("YOUR_TOKEN"),
1357 },
1358 },
1359 Tools: []anthropic.BetaToolUnionParam{
1360 {OfTool: &anthropic.BetaToolParam{
1361 Name: "get_weather",
1362 Description: anthropic.String("Get the current weather for a location."),
1363 InputSchema: anthropic.BetaToolInputSchemaParam{
1364 Properties: map[string]any{
1365 "location": map[string]any{
1366 "type": "string",
1367 "description": "City name",
1368 },
1369 },
1370 Required: []string{"location"},
1371 },
1372 }},
1373 },
1374 Messages: []anthropic.BetaMessageParam{
1375 anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("What's on my calendar tomorrow?")),
1376 // Make the calendar server's tools available from this point onward.
1377 // The block names the server; it never holds a URL or token.
1378 {
1379 Role: anthropic.BetaMessageParamRoleSystem,
1380 Content: []anthropic.BetaContentBlockParamUnion{
1381 anthropic.NewBetaToolAdditionBlock(anthropic.BetaToolChangeToolDefinitionParam{
1382 Definition: anthropic.BetaToolUnionParam{OfMCPToolset: &anthropic.BetaMCPToolsetParam{
1383 MCPServerName: "calendar",
1384 }},
1385 }),
1386 },
1387 },
1388 },
1389 })
1390 if err != nil {
1391 log.Fatal(err)
1392 }
1393 
1394 // The response starts with an mcp_tool_listing block for the calendar server,
1395 // so check each block's type instead of reading Content[0].
1396 for _, block := range response.Content {
1397 switch variant := block.AsAny().(type) {
1398 case anthropic.BetaMCPToolListingBlock:
1399 var toolNames []string
1400 for _, tool := range variant.Tools {
1401 toolNames = append(toolNames, tool.Name)
1402 }
1403 fmt.Println(variant.MCPServerName, toolNames)
1404 case anthropic.BetaTextBlock:
1405 fmt.Println(variant.Text)
1406 }
1407 }
1408 ```
1409 
1410 ```java Java
1411 import com.anthropic.models.beta.AnthropicBeta;
1412 import com.anthropic.models.beta.messages.BetaContentBlockParam;
1413 import com.anthropic.models.beta.messages.BetaMcpTool;
1414 import com.anthropic.models.beta.messages.BetaMcpToolset;
1415 import com.anthropic.models.beta.messages.BetaMessage;
1416 import com.anthropic.models.beta.messages.BetaRequestMcpServerUrlDefinition;
1417 import com.anthropic.models.beta.messages.BetaRequestToolAdditionBlock;
1418 import com.anthropic.models.beta.messages.BetaTool;
1419 import com.anthropic.models.beta.messages.MessageCreateParams;
1420 // ...
1421 
1422 void main() {
1423 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
1424 
1425 BetaTool weatherTool = BetaTool.builder()
1426 .name("get_weather")
1427 .description("Get the current weather for a location.")
1428 .inputSchema(BetaTool.InputSchema.builder()
1429 .properties(BetaTool.InputSchema.Properties.builder()
1430 .putAdditionalProperty("location", JsonValue.from(Map.of(
1431 "type", "string",
1432 "description", "City name")))
1433 .build())
1434 .addRequired("location")
1435 .build())
1436 .build();
1437 
1438 MessageCreateParams params = MessageCreateParams.builder()
1439 .model(Model.CLAUDE_OPUS_5_5)
1440 .maxTokens(1024)
1441 .addBeta(AnthropicBeta.INLINE_TOOLS_2026_09_15)
1442 .addBeta(AnthropicBeta.MCP_CLIENT_2026_09_15)
1443 .addMcpServer(BetaRequestMcpServerUrlDefinition.builder()
1444 .url("https://mcp.example.com/calendar")
1445 .name("calendar")
1446 .authorizationToken("YOUR_TOKEN")
1447 .build())
1448 .addTool(weatherTool)
1449 .addUserMessage("What's on my calendar tomorrow?")
1450 // Make the calendar server's tools available from this point onward.
1451 // The block names the server; it never holds a URL or token.
1452 .addSystemMessageOfBetaContentBlockParams(List.of(
1453 BetaContentBlockParam.ofToolAddition(BetaRequestToolAdditionBlock.builder()
1454 .definitionTool(BetaMcpToolset.builder()
1455 .mcpServerName("calendar")
1456 .build())
1457 .build())))
1458 .build();
1459 
1460 BetaMessage response = client.beta().messages().create(params);
1461 
1462 // The response starts with an mcp_tool_listing block for the calendar server,
1463 // so check each block's type instead of reading the first block.
1464 for (var block : response.content()) {
1465 switch (block.type().value()) {
1466 case MCP_TOOL_LISTING -> {
1467 var listing = block.asMcpToolListing();
1468 var toolNames = listing.tools().stream().map(BetaMcpTool::name).toList();
1469 IO.println(listing.mcpServerName() + " " + toolNames);
1470 }
1471 case TEXT -> IO.println(block.asText().text());
1472 }
1473 }
1474 }
1475 ```
1476 
1477 ```php PHP
1478 use Anthropic\Beta\AnthropicBeta;
1479 use Anthropic\Beta\Messages\BetaMCPTool;
1480 use Anthropic\Beta\Messages\BetaMCPToolListingBlock;
1481 use Anthropic\Beta\Messages\BetaTextBlock;
1482 // ...
1483 
1484 $client = new Client();
1485 
1486 $response = $client->beta->messages->create(
1487 model: Model::CLAUDE_OPUS_5_5,
1488 maxTokens: 1024,
1489 betas: [
1490 AnthropicBeta::INLINE_TOOLS_2026_09_15,
1491 AnthropicBeta::MCP_CLIENT_2026_09_15,
1492 ],
1493 mcpServers: [
1494 [
1495 'type' => 'url',
1496 'url' => 'https://mcp.example.com/calendar',
1497 'name' => 'calendar',
1498 'authorization_token' => 'YOUR_TOKEN',
1499 ],
1500 ],
1501 tools: [
1502 [
1503 'name' => 'get_weather',
1504 'description' => 'Get the current weather for a location.',
1505 'input_schema' => [
1506 'type' => 'object',
1507 'properties' => [
1508 'location' => [
1509 'type' => 'string',
1510 'description' => 'City name',
1511 ],
1512 ],
1513 'required' => ['location'],
1514 ],
1515 ],
1516 ],
1517 messages: [
1518 ['role' => 'user', 'content' => "What's on my calendar tomorrow?"],
1519 // Make the calendar server's tools available from this point onward.
1520 // The block names the server; it never holds a URL or token.
1521 [
1522 'role' => 'system',
1523 'content' => [
1524 [
1525 'type' => 'tool_addition',
1526 'tool' => [
1527 'type' => 'tool_definition',
1528 'definition' => [
1529 'type' => 'mcp_toolset',
1530 'mcp_server_name' => 'calendar',
1531 ],
1532 ],
1533 ],
1534 ],
1535 ],
1536 ],
1537 );
1538 
1539 // The response starts with an mcp_tool_listing block for the calendar server,
1540 // so check each block's type instead of reading content[0].
1541 foreach ($response->content as $block) {
1542 switch (true) {
1543 case $block instanceof BetaMCPToolListingBlock:
1544 $toolNames = array_map(fn (BetaMCPTool $tool) => $tool->name, $block->tools);
1545 echo $block->mcpServerName, ' ', json_encode($toolNames), PHP_EOL;
1546 break;
1547 case $block instanceof BetaTextBlock:
1548 echo $block->text, PHP_EOL;
1549 break;
1550 }
1551 }
1552 ```
1553 
1554 ```ruby Ruby
1555 client = Anthropic::Client.new
1556 
1557 response = client.beta.messages.create(
1558 model: Anthropic::Model::CLAUDE_OPUS_5_5,
1559 max_tokens: 1024,
1560 betas: [
1561 Anthropic::AnthropicBeta::INLINE_TOOLS_2026_09_15,
1562 Anthropic::AnthropicBeta::MCP_CLIENT_2026_09_15
1563 ],
1564 mcp_servers: [
1565 {
1566 type: "url",
1567 url: "https://mcp.example.com/calendar",
1568 name: "calendar",
1569 authorization_token: "YOUR_TOKEN"
1570 }
1571 ],
1572 tools: [
1573 {
1574 name: "get_weather",
1575 description: "Get the current weather for a location.",
1576 input_schema: {
1577 type: "object",
1578 properties: {
1579 location: { type: "string", description: "City name" }
1580 },
1581 required: ["location"]
1582 }
1583 }
1584 ],
1585 messages: [
1586 { role: "user", content: "What's on my calendar tomorrow?" },
1587 # Make the calendar server's tools available from this point onward.
1588 # The block names the server; it never holds a URL or token.
1589 {
1590 role: "system",
1591 content: [
1592 {
1593 type: "tool_addition",
1594 tool: {
1595 type: "tool_definition",
1596 definition: { type: "mcp_toolset", mcp_server_name: "calendar" }
1597 }
1598 }
1599 ]
1600 }
1601 ]
1602 )
1603 
1604 # The response starts with an mcp_tool_listing block for the calendar server,
1605 # so check each block's type instead of reading content[0].
1606 response.content.each do |block|
1607 case block
1608 when Anthropic::Beta::BetaMCPToolListingBlock
1609 puts "#{block.mcp_server_name} #{block.tools.map(&:name)}"
1610 when Anthropic::Beta::BetaTextBlock
1611 puts block.text
1612 end
1613 end
1614 ```
1615</CodeGroup>
1616 
1617With `mcp-client-2026-09-15`, a response for which the API fetched a server's tool list starts with an `mcp_tool_listing` block, one for each server it fetched. If your code reads `content[0]`, skip these blocks. Send the assistant message back unchanged, this block included, and keep sending `mcp-client-2026-09-15` on every request that carries it. Later requests then use the recorded list instead of asking the server again. To pin a toolset yourself, copy that list into the `mcp_toolset`'s `tools` field, as described in [Pin an MCP server's tool list](https://platform.claude.com/docs/en/agents-and-tools/mcp-connector#pin-mcp-tool-list).
1618 
1619`mcp-client-2026-09-15` includes everything `mcp-client-2025-11-20` does, so you don't need to send both. These features are available on the Claude API. Requests that use the MCP connector keep its [data retention](https://platform.claude.com/docs/en/agents-and-tools/mcp-connector#data-retention) terms.
1620 
4511621## When to use a mid-conversation system message
4521622 
4531623[Prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching) hashes the request prefix in order: `tools`, then `system`, then `messages`. A cache hit requires the prefix to match a recent request exactly, byte for byte, up to the cache breakpoint.
from line 1643
4731643 
4741644You can still set the top-level `system` field for instructions that should apply to the entire conversation. Reserve mid-conversation system messages for instructions that only become relevant later, or that you want to add without invalidating the cached prefix.
4751645 
476A `role: "system"` message can also carry `output_config.effort` to change the [effort](https://platform.claude.com/docs/en/build-with-claude/effort) level from the next `user` turn on. This is in beta on Claude Fable 5.1, Claude Mythos 5.1, and Claude Opus 5 on the Claude API and Google Cloud, and requires the `mid-conversation-output-config-2026-07-01` beta header. See [Per-message effort](https://platform.claude.com/docs/en/build-with-claude/effort#change-effort-mid-conversation-beta).
1646A `role: "system"` message can also carry `output_config.effort` to change the [effort](https://platform.claude.com/docs/en/build-with-claude/effort) level from the next `user` turn on. This is in beta on Claude Fable 5.1, Claude Mythos 5.1, Claude Opus 5.5, and Claude Opus 5 on the Claude API and Google Cloud, and requires the `mid-conversation-output-config-2026-07-01` beta header. See [Per-message effort](https://platform.claude.com/docs/en/build-with-claude/effort#change-effort-mid-conversation-beta).
4771647 
4781648<CodeGroup>
4791649 ```bash cURL
from line 1652
4821652 -H "x-api-key: $ANTHROPIC_API_KEY" \
4831653 -H "anthropic-version: 2023-06-01" \
4841654 -d '{
485 "model": "claude-opus-5",
1655 "model": "claude-opus-5-5",
4861656 "max_tokens": 1024,
4871657 "cache_control": {"type": "ephemeral"},
4881658 "system": "You are a code review assistant. Be concise.",
from line 1679
5091679 
5101680 ```bash CLI
5111681 ant messages create --transform 'content.#(type=="text").text' --raw-output <<'YAML'
512 model: claude-opus-5
1682 model: claude-opus-5-5
5131683 max_tokens: 1024
5141684 cache_control:
5151685 type: ephemeral
from line 1702
5321702 client = anthropic.Anthropic()
5331703 
5341704 response = client.messages.create(
535 model="claude-opus-5",
1705 model="claude-opus-5-5",
5361706 max_tokens=1024,
5371707 # Automatic prompt caching: each request caches the conversation so far,
5381708 # and the next request reads the unchanged prefix from cache.
from line 1741
5711741 const client = new Anthropic();
5721742 
5731743 const response = await client.messages.create({
574 model: "claude-opus-5",
1744 model: "claude-opus-5-5",
5751745 max_tokens: 1024,
5761746 // Automatic prompt caching: each request caches the conversation so far,
5771747 // and the next request reads the unchanged prefix from cache.
from line 1783
6131783 
6141784 var parameters = new MessageCreateParams
6151785 {
616 Model = Model.ClaudeOpus5,
1786 Model = Model.ClaudeOpus5_5,
6171787 MaxTokens = 1024,
6181788 // Automatic prompt caching: each request caches the conversation so far,
6191789 // and the next request reads the unchanged prefix from cache.
from line 1826
6561826 client := anthropic.NewClient()
6571827 
6581828 response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
659 Model: anthropic.ModelClaudeOpus5,
1829 Model: anthropic.ModelClaudeOpus5_5,
6601830 MaxTokens: 1024,
6611831 // Automatic prompt caching: each request caches the conversation so far,
6621832 // and the next request reads the unchanged prefix from cache.
from line 1868
6981868 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
6991869 
7001870 MessageCreateParams params = MessageCreateParams.builder()
701 .model(Model.CLAUDE_OPUS_5)
1871 .model(Model.CLAUDE_OPUS_5_5)
7021872 .maxTokens(1024)
7031873 // Automatic prompt caching: each request caches the conversation so far,
7041874 // and the next request reads the unchanged prefix from cache.
from line 1910
7401910 // request is still read from cache.
7411911 ['role' => 'system', 'content' => 'From now on, every suggestion must include explicit type annotations.']
7421912 ],
743 model: 'claude-opus-5',
1913 model: 'claude-opus-5-5',
7441914 // Automatic prompt caching: each request caches the conversation so far,
7451915 // and the next request reads the unchanged prefix from cache.
7461916 cacheControl: CacheControlEphemeral::with(),
from line 1928
7581928 client = Anthropic::Client.new
7591929 
7601930 response = client.messages.create(
761 model: "claude-opus-5",
1931 model: "claude-opus-5-5",
7621932 max_tokens: 1024,
7631933 # Automatic prompt caching: each request caches the conversation so far,
7641934 # and the next request reads the unchanged prefix from cache.
from line 2001
8312001}
8322002```
8332003 
834The main use is a per-turn reminder in a tool loop. Append the reminder after the `tool_result` message each time you want the model to see it, and leave every earlier copy where it is. The model sees only the copies that come after the last user message, so the reminder never piles up. Nothing earlier in `messages` changes, so the [prompt cache](https://platform.claude.com/docs/en/build-with-claude/prompt-caching) keeps matching. On Claude Fable 5.1 this also keeps later [thinking blocks valid](https://platform.claude.com/docs/en/build-with-claude/thinking#preserved-in-conversation): deleting an earlier reminder would change the conversation before those blocks and fail the conversation check, while a cleared message stays in the array and leaves that conversation unchanged.
2004The main use is a per-turn reminder in a tool loop. Append the reminder after the `tool_result` message each time you want the model to see it, and leave every earlier copy where it is. The model sees only the copies that come after the last user message, so the reminder never piles up. Nothing earlier in `messages` changes, so the [prompt cache](https://platform.claude.com/docs/en/build-with-claude/prompt-caching) keeps matching. On Claude Fable 5.1 and Claude Opus 5.5 this also keeps later [thinking blocks valid](https://platform.claude.com/docs/en/build-with-claude/thinking#preserved-in-conversation): deleting an earlier reminder would change the conversation before those blocks and fail the conversation check, while a cleared message stays in the array and leaves that conversation unchanged.
8352005 
8362006The following request is a later step of an agent loop. `messages[3]` rendered on the earlier request, when it was the last message in the array. Once `messages[5]` (a later user message) exists, `messages[3]` is cleared: the cleared message stays in the array, so the conversation before the thinking block in `messages[4]` is unchanged, but the model no longer sees its text. `messages[6]` and `messages[7]` both render, in order.
8372007 
from line 2078
9082078 
9092079Rules for turn-scoped messages:
9102080 
911* **Re-send cleared messages verbatim.** A cleared message is still part of the conversation history. Rebuilding it from current state (a fresh token count, a timestamp), dropping it as redundant, or changing its `clear_at` value is an edit to an earlier message. The prompt cache misses from that point, and on Claude Fable 5.1 every thinking block produced after it fails the [conversation check](https://platform.claude.com/docs/en/build-with-claude/thinking#preserved-in-conversation).
2081* **Re-send cleared messages verbatim.** A cleared message is still part of the conversation history. Rebuilding it from current state (a fresh token count, a timestamp), dropping it as redundant, or changing its `clear_at` value is an edit to an earlier message. The prompt cache misses from that point, and on Claude Fable 5.1 and Claude Opus 5.5 every thinking block produced after it fails the [conversation check](https://platform.claude.com/docs/en/build-with-claude/thinking#preserved-in-conversation).
9122082* **Text only.** `content` is one or more `text` blocks (or a string). `tool_addition` and `tool_removal` blocks return a 400 error on a turn-scoped message, and so does `output_config`. Use a separate `role: "system"` message without `clear_at` for those.
9132083* **No `cache_control` on its blocks.** A cleared message is never part of a cache key, so a breakpoint on it could never match. Put the breakpoint on the last block of the preceding user turn instead, as the example does. The top-level [automatic caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#automatic-caching) field skips turn-scoped messages when it picks a breakpoint. On the request that clears a message, the reusable cached prefix ends at the user turn before it, so only the one assistant turn between that message and the new user message is reprocessed.
9142084* **Placement rules still apply**, cleared or not. A turn-scoped message must follow a `user` turn (or an `assistant` turn ending in a server tool result) and precede an `assistant` turn or end the array, like any mid-conversation system message. One that ends the array always renders. One followed directly by another `user` message is a 400 error, not a cleared message: put all of a tool round's results in one user message and the reminders after it.
from line 2335
11652335* **Append the system message after the breakpoint.** Because it comes after the cached prefix, it does not change the prefix hash and the cache still hits.
11662336* **A mid-conversation system message is itself cacheable.** Once it is in the conversation, it becomes part of the stable history. On the next turn you can move your cache breakpoint past it (or rely on [automatic caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#automatic-caching) to do so) and the system message is read from cache like any other turn.
11672337 
1168Avoid editing or removing a mid-conversation system message that has already been sent. Like any other change to earlier messages, that invalidates the cache from that point forward. On Claude Fable 5.1 it also invalidates the [thinking blocks](https://platform.claude.com/docs/en/build-with-claude/thinking#preserved-in-conversation) in every later assistant turn. For guidance that should apply to one turn only, use a [turn-scoped system message](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages#turn-scoped-system-messages) and leave it in place. If the instruction needs to evolve, append a new system message rather than rewriting the old one. Consecutive system messages are accepted and treated as a single system section, which follows the same placement rule as a whole.
2338Avoid editing or removing a mid-conversation system message that has already been sent. Like any other change to earlier messages, that invalidates the cache from that point forward. On Claude Fable 5.1 and Claude Opus 5.5 it also invalidates the [thinking blocks](https://platform.claude.com/docs/en/build-with-claude/thinking#preserved-in-conversation) in every later assistant turn. For guidance that should apply to one turn only, use a [turn-scoped system message](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages#turn-scoped-system-messages) and leave it in place. If the instruction needs to evolve, append a new system message rather than rewriting the old one. Consecutive system messages are accepted and treated as a single system section, which follows the same placement rule as a whole.
11692339 
11702340## Limitations
11712341 
11722342