---
title: Mensajes del sistema y cambios de herramientas a mitad de conversación
url: https://platform.claude.com/docs/es/build-with-claude/mid-conversation-system-messages
description: Cambia las instrucciones del sistema o la disponibilidad de herramientas a mitad de una conversación sin invalidar el prefijo en caché que las precede.
---

<Note>
  Para saber cómo se aplica la "zero data retention" (retención cero de datos), o ZDR, a esta función, consulta [API y retención de datos](https://platform.claude.com/docs/es/manage-claude/api-and-data-retention).
</Note>

Las instrucciones del sistema normalmente viven en el campo `system` de nivel superior, antes de cada mensaje de la conversación. Esa posición es excelente para el "prompt caching" ([almacenamiento en caché de prompts](https://platform.claude.com/docs/es/build-with-claude/prompt-caching)): la indicación del sistema forma parte del prefijo estable, por lo que los turnos posteriores aciertan en la caché. Es una mala posición para instrucciones que solo descubres que necesitas a mitad de una sesión, porque editar el campo `system` de nivel superior cambia el comienzo mismo del prompt e invalida la caché para todo lo que sigue.

Los mensajes del sistema a mitad de conversación cierran esa brecha. Agregas un mensaje `{"role": "system"}` en el punto de la conversación donde la nueva instrucción se vuelve relevante, en lugar de editar el campo `system` de nivel superior. El prefijo en caché permanece igual, por lo que la siguiente solicitud aún lo lee desde la caché, y la nueva instrucción se sigue aplicando como una instrucción del sistema en lugar de como texto de usuario ordinario.

<Note>
  Los mensajes del sistema a mitad de conversación están disponibles en la API de Claude, [Claude en Amazon Bedrock](https://platform.claude.com/docs/es/build-with-claude/claude-in-amazon-bedrock) y [Google Cloud](https://platform.claude.com/docs/es/build-with-claude/claude-on-vertex-ai).

  Esta función está disponible en Claude Fable 5.1, [Claude Mythos 5.1](https://platform.claude.com/docs/es/models/mythos-5-1/overview), Claude Fable 5, [Claude Mythos 5](https://platform.claude.com/docs/es/models/mythos-5/overview), Claude Opus 5.5, Claude Opus 4.8, Claude Opus 5, Claude Sonnet 5.5 y Claude Haiku 5.5. No se requiere ningún encabezado beta para los mensajes del sistema a mitad de la conversación. Esta función no está disponible en Claude Sonnet 5. En ese modelo, usa el campo `system` de nivel superior en su lugar.

  Los [cambios de herramientas a mitad de la conversación](https://platform.claude.com/docs/es/build-with-claude/mid-conversation-system-messages#mid-conversation-tool-changes) están en beta en los mismos modelos. En la API de Claude, envía el "beta header" (encabezado beta) `inline-tools-2026-09-15`, que también cubre [definir una herramienta dentro de un bloque `tool_addition`](https://platform.claude.com/docs/es/build-with-claude/mid-conversation-system-messages#define-tools-in-a-message-beta). [Agregar un servidor MCP de esa manera](https://platform.claude.com/docs/es/build-with-claude/mid-conversation-system-messages#add-an-mcp-server-mid-conversation-beta) requiere un segundo encabezado beta, `mcp-client-2026-09-15`, que está disponible en la API de Claude. El encabezado `mid-conversation-tool-changes-2026-07-01` funciona para cambios que nombran una herramienta por referencia, en la API de Claude, Amazon Bedrock y Google Cloud.

  Los [mensajes del sistema con alcance de turno](https://platform.claude.com/docs/es/build-with-claude/mid-conversation-system-messages#turn-scoped-system-messages) (`clear_at`) están en beta y requieren el encabezado beta `mid-conversation-system-clear-at-2026-08-21`, en los mismos modelos y plataformas que los mensajes del sistema a mitad de conversación.
</Note>

## Cambios de herramientas a mitad de la conversación

El array `tools` se ubica incluso antes que el campo `system` de nivel superior en el prefijo de la solicitud sobre el que se calcula el hash, por lo que editarlo invalida la [caché de prompts](https://platform.claude.com/docs/es/build-with-claude/prompt-caching) para toda la conversación. Los cambios de herramientas a mitad de la conversación son el equivalente, para las herramientas, de los mensajes del sistema a mitad de la conversación. En lugar de fijar la lista de herramientas durante toda la conversación, cambias qué herramientas se ofrecen al modelo entre turnos: declara el conjunto completo de herramientas en `tools` desde el principio y luego usa bloques `tool_addition` y `tool_removal` para ofrecer una herramienta al modelo, o retirarla, a partir de un punto específico de la conversación. El array `tools` en sí nunca cambia, por lo que el prefijo almacenado en caché permanece intacto. Los cambios de herramientas a mitad de la conversación están en beta y usan el encabezado beta `inline-tools-2026-09-15` en la API de Claude.

`tool_addition` y `tool_removal` son bloques de contenido en el array `content` de un mensaje `role: "system"`, y pueden combinarse con bloques `text` en el mismo mensaje. El mensaje sigue las reglas de ubicación de cualquier mensaje del sistema a mitad de la conversación, con una restricción adicional después de un turno pausado (consulta [Limitaciones](https://platform.claude.com/docs/es/build-with-claude/mid-conversation-system-messages#limitations)), y el cambio se aplica desde ese punto de la conversación en adelante. El campo `tool` de cada bloque hace referencia a una herramienta en lugar de definirla: `{"type": "tool_reference", "name": "..."}` nombra una herramienta declarada en el array `tools` de la solicitud, y las herramientas del [conector MCP](https://platform.claude.com/docs/es/agents-and-tools/mcp-connector) pueden referenciarse individualmente con `mcp_tool_reference` (`server_name` y `name`) o como un conjunto de herramientas completo con `mcp_toolset_reference` (`server_name`). Hacer referencia a un nombre que no está declarado en `tools` devuelve un error 400 (en la API de Claude, con `error.details.error_code` establecido en `tool_reference_unresolved`). Como alternativa, un bloque `tool_addition` puede [contener la definición completa de la herramienta](https://platform.claude.com/docs/es/build-with-claude/mid-conversation-system-messages#define-tools-in-a-message-beta), algo que el encabezado `mid-conversation-tool-changes-2026-07-01` no admite.

Cada herramienta declarada en `tools` se ofrece al modelo desde el inicio de la conversación, a menos que se declare con `defer_loading: true`, lo que la mantiene retenida hasta que un bloque `tool_addition` la haga visible. `tool_addition` también vuelve a ofrecer una herramienta que un `tool_removal` anterior retiró.

La siguiente solicitud declara `get_weather` en `tools` y luego la retira después del primer turno del usuario con un bloque `tool_removal`. La solicitud envía el encabezado beta `inline-tools-2026-09-15`.

<CodeGroup>
  ```bash cURL
  curl https://api.anthropic.com/v1/messages \
    -H "content-type: application/json" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: inline-tools-2026-09-15" \
    -d '{
      "model": "claude-opus-5-5",
      "max_tokens": 1024,
      "tools": [
        {
          "name": "get_weather",
          "description": "Get the current weather for a location.",
          "input_schema": {
            "type": "object",
            "properties": {
              "location": {"type": "string", "description": "City name"}
            },
            "required": ["location"]
          }
        }
      ],
      "messages": [
        {
          "role": "user",
          "content": "Say OK."
        },
        {
          "role": "system",
          "content": [
            {
              "type": "tool_removal",
              "tool": {"type": "tool_reference", "name": "get_weather"}
            }
          ]
        }
      ]
    }'
  ```

  ```bash CLI
  ant beta:messages create --beta inline-tools-2026-09-15 \
    --transform 'content.#(type=="text").text' --raw-output <<'YAML'
  model: claude-opus-5-5
  max_tokens: 1024
  tools:
    - name: get_weather
      description: Get the current weather for a location.
      input_schema:
        type: object
        properties:
          location:
            type: string
            description: City name
        required:
          - location
  messages:
    - role: user
      content: Say OK.
    - role: system
      content:
        - type: tool_removal
          tool:
            type: tool_reference
            name: get_weather
  YAML
  ```

  ```python Python
  client = anthropic.Anthropic()

  response = client.beta.messages.create(
      model="claude-opus-5-5",
      max_tokens=1024,
      betas=["inline-tools-2026-09-15"],
      # El conjunto completo de herramientas se declara desde el inicio y nunca cambia, así que el
      # prefijo en caché permanece intacto.
      tools=[
          {
              "name": "get_weather",
              "description": "Get the current weather for a location.",
              "input_schema": {
                  "type": "object",
                  "properties": {
                      "location": {"type": "string", "description": "City name"},
                  },
                  "required": ["location"],
              },
          },
      ],
      messages=[
          {
              "role": "user",
              "content": "Say OK.",
          },
          # Retira get_weather a partir de este punto. El bloque hace referencia a
          # la herramienta por nombre en lugar de editar `tools`, así los turnos anteriores quedan
          # idénticos byte a byte y la caché sigue acertando.
          {
              "role": "system",
              "content": [
                  {
                      "type": "tool_removal",
                      "tool": {"type": "tool_reference", "name": "get_weather"},
                  },
              ],
          },
      ],
  )

  for block in response.content:
      if block.type == "text":
          print(block.text)
  ```

  ```typescript TypeScript
  const client = new Anthropic();

  const response = await client.beta.messages.create({
    model: "claude-opus-5-5",
    max_tokens: 1024,
    betas: ["inline-tools-2026-09-15"],
    // El conjunto completo de herramientas se declara desde el inicio y nunca cambia, así que el
    // prefijo en caché permanece intacto.
    tools: [
      {
        name: "get_weather",
        description: "Get the current weather for a location.",
        input_schema: {
          type: "object",
          properties: {
            location: {
              type: "string",
              description: "City name"
            }
          },
          required: ["location"]
        }
      }
    ],
    messages: [
      { role: "user", content: "Say OK." },
      // Retira get_weather a partir de este punto. El bloque hace referencia a la
      // herramienta por nombre en lugar de editar `tools`, así los turnos anteriores quedan
      // idénticos byte a byte y la caché sigue acertando.
      {
        role: "system",
        content: [
          {
            type: "tool_removal",
            tool: { type: "tool_reference", name: "get_weather" }
          }
        ]
      }
    ]
  });

  for (const block of response.content) {
    if (block.type === "text") {
      console.log(block.text);
    }
  }
  ```

  ```csharp C#
  using Anthropic.Models.Beta;
  using Anthropic.Models.Beta.Messages;
  using Messages = Anthropic.Models.Messages;

  AnthropicClient client = new();

  var response = await client.Beta.Messages.Create(new MessageCreateParams
  {
      Model = Messages::Model.ClaudeOpus5_5,
      MaxTokens = 1024,
      Betas = [AnthropicBeta.InlineTools2026_09_15],
      // El conjunto completo de herramientas se declara de antemano y nunca cambia, así que el
      // prefijo en caché permanece intacto.
      Tools =
      [
          new BetaTool
          {
              Name = "get_weather",
              Description = "Get the current weather for a location.",
              InputSchema = new InputSchema
              {
                  Properties = new Dictionary<string, JsonElement>
                  {
                      ["location"] = JsonSerializer.SerializeToElement(new { type = "string", description = "City name" }),
                  },
                  Required = ["location"],
              },
          },
      ],
      Messages =
      [
          new() { Role = Role.User, Content = "Say OK." },
          // Retira get_weather a partir de este punto. El bloque hace referencia a
          // la herramienta por nombre en lugar de editar `Tools`, así que los turnos anteriores quedan
          // idénticos byte a byte y la caché sigue acertando.
          new()
          {
              Role = Role.System,
              Content = new(
              [
                  new BetaRequestToolRemovalBlock
                  {
                      Tool = new BetaToolChangeToolReference { Name = "get_weather" },
                  },
              ]),
          },
      ],
  });

  foreach (var block in response.Content)
  {
      if (block.TryPickText(out var text))
      {
          Console.WriteLine(text.Text);
      }
  }
  ```

  ```go Go
  client := anthropic.NewClient()

  response, err := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{
  	Model:     anthropic.ModelClaudeOpus5_5,
  	MaxTokens: 1024,
  	Betas:     []anthropic.AnthropicBeta{anthropic.AnthropicBetaInlineTools2026_09_15},
  	// El conjunto completo de herramientas se declara desde el inicio y nunca cambia, así que el
  	// prefijo en caché permanece intacto.
  	Tools: []anthropic.BetaToolUnionParam{
  		{OfTool: &anthropic.BetaToolParam{
  			Name:        "get_weather",
  			Description: anthropic.String("Get the current weather for a location."),
  			InputSchema: anthropic.BetaToolInputSchemaParam{
  				Properties: map[string]any{
  					"location": map[string]any{
  						"type":        "string",
  						"description": "City name",
  					},
  				},
  				Required: []string{"location"},
  			},
  		}},
  	},
  	Messages: []anthropic.BetaMessageParam{
  		anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("Say OK.")),
  		// Retira get_weather a partir de este punto. El bloque hace referencia a
  		// la herramienta por nombre en lugar de editar Tools, así los turnos anteriores quedan
  		// idénticos byte a byte y la caché sigue acertando.
  		{
  			Role: anthropic.BetaMessageParamRoleSystem,
  			Content: []anthropic.BetaContentBlockParamUnion{
  				anthropic.NewBetaToolRemovalBlock(anthropic.BetaToolChangeToolReferenceParam{
  					Name: "get_weather",
  				}),
  			},
  		},
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }

  for _, block := range response.Content {
  	if textBlock, ok := block.AsAny().(anthropic.BetaTextBlock); ok {
  		fmt.Println(textBlock.Text)
  	}
  }
  ```

  ```java Java
  import com.anthropic.models.beta.AnthropicBeta;
  import com.anthropic.models.beta.messages.BetaContentBlockParam;
  import com.anthropic.models.beta.messages.BetaMessage;
  import com.anthropic.models.beta.messages.BetaMessageParam;
  import com.anthropic.models.beta.messages.BetaRequestToolRemovalBlock;
  import com.anthropic.models.beta.messages.BetaTool;
  import com.anthropic.models.beta.messages.MessageCreateParams;
  // ...
      AnthropicClient client = AnthropicOkHttpClient.fromEnv();

      // El conjunto completo de herramientas se declara desde el inicio y nunca cambia, así que el
      // prefijo en caché permanece intacto.
      BetaTool weatherTool = BetaTool.builder()
          .name("get_weather")
          .description("Get the current weather for a location.")
          .inputSchema(BetaTool.InputSchema.builder()
              .properties(BetaTool.InputSchema.Properties.builder()
                  .putAdditionalProperty("location", JsonValue.from(Map.of(
                      "type", "string",
                      "description", "City name")))
                  .build())
              .addRequired("location")
              .build())
          .build();

      MessageCreateParams params = MessageCreateParams.builder()
          .model(Model.CLAUDE_OPUS_5_5)
          .maxTokens(1024)
          .addBeta(AnthropicBeta.INLINE_TOOLS_2026_09_15)
          .addTool(weatherTool)
          .addUserMessage("Say OK.")
          // Retira get_weather a partir de este punto. El bloque hace referencia a
          // la herramienta por nombre en lugar de editar `tools`, así los turnos anteriores quedan
          // idénticos byte a byte y la caché sigue acertando.
          .addMessage(BetaMessageParam.builder()
              .role(BetaMessageParam.Role.SYSTEM)
              .contentOfBetaContentBlockParams(List.of(
                  BetaContentBlockParam.ofToolRemoval(BetaRequestToolRemovalBlock.builder()
                      .referenceTool("get_weather")
                      .build())))
              .build())
          .build();

      BetaMessage response = client.beta().messages().create(params);
      response.content().stream()
          .flatMap(block -> block.text().stream())
          .forEach(textBlock -> IO.println(textBlock.text()));
  ```

  ```php PHP
  use Anthropic\Beta\AnthropicBeta;
  // ...

  $client = new Client();

  $response = $client->beta->messages->create(
      model: Model::CLAUDE_OPUS_5_5,
      maxTokens: 1024,
      betas: [AnthropicBeta::INLINE_TOOLS_2026_09_15],
      // El conjunto completo de herramientas se declara desde el inicio y nunca cambia, así que el
      // prefijo en caché permanece intacto.
      tools: [
          [
              'name' => 'get_weather',
              'description' => 'Get the current weather for a location.',
              'input_schema' => [
                  'type' => 'object',
                  'properties' => [
                      'location' => [
                          'type' => 'string',
                          'description' => 'City name',
                      ],
                  ],
                  'required' => ['location'],
              ],
          ],
      ],
      messages: [
          ['role' => 'user', 'content' => 'Say OK.'],
          // Retira get_weather a partir de este punto. El bloque hace referencia a
          // la herramienta por nombre en lugar de editar `tools`, así que los turnos anteriores quedan
          // idénticos byte a byte y la caché sigue acertando.
          [
              'role' => 'system',
              'content' => [
                  [
                      'type' => 'tool_removal',
                      'tool' => ['type' => 'tool_reference', 'name' => 'get_weather'],
                  ],
              ],
          ],
      ],
  );

  foreach ($response->content as $block) {
      if ($block->type === 'text') {
          echo $block->text, PHP_EOL;
      }
  }
  ```

  ```ruby Ruby
  client = Anthropic::Client.new

  response = client.beta.messages.create(
    model: Anthropic::Model::CLAUDE_OPUS_5_5,
    max_tokens: 1024,
    betas: [Anthropic::AnthropicBeta::INLINE_TOOLS_2026_09_15],
    # El conjunto completo de herramientas se declara desde el inicio y nunca cambia, así que el
    # prefijo en caché permanece intacto.
    tools: [
      {
        name: "get_weather",
        description: "Get the current weather for a location.",
        input_schema: {
          type: "object",
          properties: {
            location: { type: "string", description: "City name" }
          },
          required: ["location"]
        }
      }
    ],
    messages: [
      { role: "user", content: "Say OK." },
      # Retira get_weather a partir de este punto. El bloque hace referencia a
      # la herramienta por nombre en lugar de editar `tools`, así los turnos anteriores quedan
      # idénticos byte a byte y la caché sigue acertando.
      {
        role: "system",
        content: [
          {
            type: "tool_removal",
            tool: { type: "tool_reference", name: "get_weather" }
          }
        ]
      }
    ]
  )

  response.content.each do |block|
    puts block.text if block.type == :text
  end
  ```
</CodeGroup>

### Definir herramientas en un mensaje (beta)

Con el encabezado beta `inline-tools-2026-09-15`, un bloque `tool_addition` puede definir una herramienta por valor, con su definición completa, en lugar de nombrarla por referencia. Esto te permite introducir una herramienta que se desconoce al inicio de la conversación, o cuyo esquema cambia más adelante, agregando un mensaje `role: "system"`. El arreglo `tools` y todos los mensajes anteriores permanecen exactamente como se enviaron, por lo que la caché de prompts sigue acertando y solo el mensaje agregado se procesa como entrada nueva. La única excepción, un arreglo `tools` sin ninguna herramienta no diferida, se trata en las reglas a continuación. El encabezado también cubre agregar y quitar herramientas por referencia, por lo que no necesitas enviar también `mid-conversation-tool-changes-2026-07-01`.

Envuelve la definición en un objeto `tool` de tipo `tool_definition`. La `definition` es una entrada de `tools`, como una herramienta personalizada o una herramienta de cliente o de servidor definida por Anthropic, con su configuración habitual, incluidos `cache_control` y `defer_loading`. Durante la beta, algunos tipos de herramientas (entre ellos la herramienta de uso de computadora) todavía no pueden definirse en un mensaje y devuelven un error 400 que lo indica; decláralas en `tools` y agrégalas por referencia. Por ejemplo, para definir una herramienta personalizada a mitad de la conversación:

```json
{
  "role": "system",
  "content": [
    {
      "type": "tool_addition",
      "tool": {
        "type": "tool_definition",
        "definition": {
          "name": "db_query",
          "description": "Run a read-only SQL query against the analytics database.",
          "input_schema": {
            "type": "object",
            "properties": { "sql": { "type": "string" } },
            "required": ["sql"]
          }
        }
      }
    }
  ]
}
```

Desde esa posición en adelante, el modelo puede llamar a la herramienta de la misma manera en que llama a una herramienta declarada en `tools`. Enviar de nuevo una definición idéntica no cambia nada, por lo que un cliente puede reenviarla con seguridad, por ejemplo, en un reintento.

La siguiente solicitud mantiene `get_weather` en `tools` y define `db_query` después del primer turno del usuario:

<CodeGroup>
  ```bash cURL
  curl https://api.anthropic.com/v1/messages \
    -H "content-type: application/json" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: inline-tools-2026-09-15" \
    -d '{
      "model": "claude-opus-5-5",
      "max_tokens": 1024,
      "tools": [
        {
          "name": "get_weather",
          "description": "Get the current weather for a location.",
          "input_schema": {
            "type": "object",
            "properties": {
              "location": {"type": "string", "description": "City name"}
            },
            "required": ["location"]
          }
        }
      ],
      "messages": [
        {
          "role": "user",
          "content": "How many orders shipped yesterday?"
        },
        {
          "role": "system",
          "content": [
            {
              "type": "tool_addition",
              "tool": {
                "type": "tool_definition",
                "definition": {
                  "name": "db_query",
                  "description": "Run a read-only SQL query against the analytics database.",
                  "input_schema": {
                    "type": "object",
                    "properties": {"sql": {"type": "string"}},
                    "required": ["sql"]
                  }
                }
              }
            }
          ]
        }
      ]
    }'
  ```

  ```bash CLI
  ant beta:messages create --beta inline-tools-2026-09-15 <<'YAML'
  model: claude-opus-5-5
  max_tokens: 1024
  # Mantén al menos una herramienta no diferida en `tools`, para que una herramienta definida
  # más tarde no cambie el inicio del prompt renderizado.
  tools:
    - name: get_weather
      description: Get the current weather for a location.
      input_schema:
        type: object
        properties:
          location:
            type: string
            description: City name
        required:
          - location
  messages:
    - role: user
      content: How many orders shipped yesterday?
    # Define db_query por valor a partir de este punto. `tools` y los
    # mensajes anteriores quedan tal como se enviaron, así que la caché sigue acertando.
    - role: system
      content:
        - type: tool_addition
          tool:
            type: tool_definition
            definition:
              name: db_query
              description: Run a read-only SQL query against the analytics database.
              input_schema:
                type: object
                properties:
                  sql:
                    type: string
                required:
                  - sql
  YAML
  ```

  ```python Python
  client = anthropic.Anthropic()

  response = client.beta.messages.create(
      model="claude-opus-5-5",
      max_tokens=1024,
      betas=["inline-tools-2026-09-15"],
      # Mantén al menos una herramienta no diferida en `tools`, para que una herramienta definida
      # después no cambie el inicio del prompt renderizado.
      tools=[
          {
              "name": "get_weather",
              "description": "Get the current weather for a location.",
              "input_schema": {
                  "type": "object",
                  "properties": {
                      "location": {"type": "string", "description": "City name"},
                  },
                  "required": ["location"],
              },
          },
      ],
      messages=[
          {"role": "user", "content": "How many orders shipped yesterday?"},
          # Define db_query por valor a partir de aquí. `tools` y los
          # mensajes anteriores quedan tal como se enviaron, así que la caché sigue acertando.
          {
              "role": "system",
              "content": [
                  {
                      "type": "tool_addition",
                      "tool": {
                          "type": "tool_definition",
                          "definition": {
                              "name": "db_query",
                              "description": "Run a read-only SQL query against the analytics database.",
                              "input_schema": {
                                  "type": "object",
                                  "properties": {"sql": {"type": "string"}},
                                  "required": ["sql"],
                              },
                          },
                      },
                  },
              ],
          },
      ],
  )

  for block in response.content:
      if block.type == "tool_use":
          print(block.name, block.input)
  ```

  ```typescript TypeScript
  const client = new Anthropic();

  const response = await client.beta.messages.create({
    model: "claude-opus-5-5",
    max_tokens: 1024,
    betas: ["inline-tools-2026-09-15"],
    // Mantén al menos una herramienta no diferida en `tools`, para que una herramienta definida
    // más tarde no cambie el inicio del prompt renderizado.
    tools: [
      {
        name: "get_weather",
        description: "Get the current weather for a location.",
        input_schema: {
          type: "object",
          properties: {
            location: { type: "string", description: "City name" }
          },
          required: ["location"]
        }
      }
    ],
    messages: [
      { role: "user", content: "How many orders shipped yesterday?" },
      // Define db_query por valor a partir de este punto. `tools` y los
      // mensajes anteriores quedan tal como se enviaron, así que la caché sigue acertando.
      {
        role: "system",
        content: [
          {
            type: "tool_addition",
            tool: {
              type: "tool_definition",
              definition: {
                name: "db_query",
                description: "Run a read-only SQL query against the analytics database.",
                input_schema: {
                  type: "object",
                  properties: { sql: { type: "string" } },
                  required: ["sql"]
                }
              }
            }
          }
        ]
      }
    ]
  });

  for (const block of response.content) {
    if (block.type === "tool_use") {
      console.log(block.name, JSON.stringify(block.input));
    }
  }
  ```

  ```csharp C#
  using Anthropic.Models.Beta;
  using Anthropic.Models.Beta.Messages;
  using Messages = Anthropic.Models.Messages;

  AnthropicClient client = new();

  var response = await client.Beta.Messages.Create(new MessageCreateParams
  {
      Model = Messages::Model.ClaudeOpus5_5,
      MaxTokens = 1024,
      Betas = [AnthropicBeta.InlineTools2026_09_15],
      // Mantén al menos una herramienta no diferida en `Tools`, para que una herramienta definida
      // más adelante no cambie el inicio del prompt renderizado.
      Tools =
      [
          new BetaTool
          {
              Name = "get_weather",
              Description = "Get the current weather for a location.",
              InputSchema = new InputSchema
              {
                  Properties = new Dictionary<string, JsonElement>
                  {
                      ["location"] = JsonSerializer.SerializeToElement(new { type = "string", description = "City name" }),
                  },
                  Required = ["location"],
              },
          },
      ],
      Messages =
      [
          new() { Role = Role.User, Content = "How many orders shipped yesterday?" },
          // Define db_query por valor a partir de este punto. `Tools` y los
          // mensajes anteriores quedan tal como se enviaron, así que la caché sigue acertando.
          new()
          {
              Role = Role.System,
              Content = new(
              [
                  new BetaRequestToolAdditionBlock
                  {
                      Tool = new BetaToolChangeToolDefinitionParam
                      {
                          Definition = new BetaTool
                          {
                              Name = "db_query",
                              Description = "Run a read-only SQL query against the analytics database.",
                              InputSchema = new InputSchema
                              {
                                  Properties = new Dictionary<string, JsonElement>
                                  {
                                      ["sql"] = JsonSerializer.SerializeToElement(new { type = "string" }),
                                  },
                                  Required = ["sql"],
                              },
                          },
                      },
                  },
              ]),
          },
      ],
  });

  foreach (var block in response.Content)
  {
      if (block.TryPickToolUse(out var toolUse))
      {
          Console.WriteLine($"{toolUse.Name} {JsonSerializer.Serialize(toolUse.Input)}");
      }
  }
  ```

  ```go Go
  client := anthropic.NewClient()

  response, err := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{
  	Model:     anthropic.ModelClaudeOpus5_5,
  	MaxTokens: 1024,
  	Betas:     []anthropic.AnthropicBeta{anthropic.AnthropicBetaInlineTools2026_09_15},
  	// Mantén al menos una herramienta no diferida en Tools, para que una herramienta definida
  	// después no cambie el inicio del prompt renderizado.
  	Tools: []anthropic.BetaToolUnionParam{
  		{OfTool: &anthropic.BetaToolParam{
  			Name:        "get_weather",
  			Description: anthropic.String("Get the current weather for a location."),
  			InputSchema: anthropic.BetaToolInputSchemaParam{
  				Properties: map[string]any{
  					"location": map[string]any{
  						"type":        "string",
  						"description": "City name",
  					},
  				},
  				Required: []string{"location"},
  			},
  		}},
  	},
  	Messages: []anthropic.BetaMessageParam{
  		anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("How many orders shipped yesterday?")),
  		// Define db_query por valor a partir de aquí. Tools y los
  		// mensajes anteriores quedan tal como se enviaron, así que la caché sigue acertando.
  		{
  			Role: anthropic.BetaMessageParamRoleSystem,
  			Content: []anthropic.BetaContentBlockParamUnion{
  				anthropic.NewBetaToolAdditionBlock(anthropic.BetaToolChangeToolDefinitionParam{
  					Definition: anthropic.BetaToolUnionParam{OfTool: &anthropic.BetaToolParam{
  						Name:        "db_query",
  						Description: anthropic.String("Run a read-only SQL query against the analytics database."),
  						InputSchema: anthropic.BetaToolInputSchemaParam{
  							Properties: map[string]any{
  								"sql": map[string]any{"type": "string"},
  							},
  							Required: []string{"sql"},
  						},
  					}},
  				}),
  			},
  		},
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }

  for _, block := range response.Content {
  	if toolUse, ok := block.AsAny().(anthropic.BetaToolUseBlock); ok {
  		fmt.Println(toolUse.Name, toolUse.Input)
  	}
  }
  ```

  ```java Java
  import com.anthropic.models.beta.AnthropicBeta;
  import com.anthropic.models.beta.messages.BetaContentBlockParam;
  import com.anthropic.models.beta.messages.BetaMessage;
  import com.anthropic.models.beta.messages.BetaRequestToolAdditionBlock;
  import com.anthropic.models.beta.messages.BetaTool;
  import com.anthropic.models.beta.messages.MessageCreateParams;
  // ...

  void main() {
      AnthropicClient client = AnthropicOkHttpClient.fromEnv();

      BetaTool weatherTool = BetaTool.builder()
          .name("get_weather")
          .description("Get the current weather for a location.")
          .inputSchema(BetaTool.InputSchema.builder()
              .properties(BetaTool.InputSchema.Properties.builder()
                  .putAdditionalProperty("location", JsonValue.from(Map.of(
                      "type", "string",
                      "description", "City name")))
                  .build())
              .addRequired("location")
              .build())
          .build();

      BetaTool dbQueryTool = BetaTool.builder()
          .name("db_query")
          .description("Run a read-only SQL query against the analytics database.")
          .inputSchema(BetaTool.InputSchema.builder()
              .properties(BetaTool.InputSchema.Properties.builder()
                  .putAdditionalProperty("sql", JsonValue.from(Map.of("type", "string")))
                  .build())
              .addRequired("sql")
              .build())
          .build();

      MessageCreateParams params = MessageCreateParams.builder()
          .model(Model.CLAUDE_OPUS_5_5)
          .maxTokens(1024)
          .addBeta(AnthropicBeta.INLINE_TOOLS_2026_09_15)
          // Mantén al menos una herramienta no diferida en `tools`, para que una herramienta definida
          // más tarde no cambie el inicio del prompt renderizado.
          .addTool(weatherTool)
          .addUserMessage("How many orders shipped yesterday?")
          // Define db_query por valor a partir de este punto. `tools` y los
          // mensajes anteriores quedan tal como se enviaron, así que la caché sigue acertando.
          .addSystemMessageOfBetaContentBlockParams(List.of(
              BetaContentBlockParam.ofToolAddition(BetaRequestToolAdditionBlock.builder()
                  .definitionTool(dbQueryTool)
                  .build())))
          .build();

      BetaMessage response = client.beta().messages().create(params);
      response.content().stream()
          .flatMap(block -> block.toolUse().stream())
          .forEach(toolUse -> IO.println(toolUse.name() + " " + toolUse._input()));
  }
  ```

  ```php PHP
  use Anthropic\Beta\AnthropicBeta;
  use Anthropic\Beta\Messages\BetaToolUseBlock;
  // ...

  $client = new Client();

  $response = $client->beta->messages->create(
      model: Model::CLAUDE_OPUS_5_5,
      maxTokens: 1024,
      betas: [AnthropicBeta::INLINE_TOOLS_2026_09_15],
      // Mantén al menos una herramienta no diferida en `tools`, para que una herramienta definida
      // más tarde no cambie el inicio del prompt renderizado.
      tools: [
          [
              'name' => 'get_weather',
              'description' => 'Get the current weather for a location.',
              'input_schema' => [
                  'type' => 'object',
                  'properties' => [
                      'location' => [
                          'type' => 'string',
                          'description' => 'City name',
                      ],
                  ],
                  'required' => ['location'],
              ],
          ],
      ],
      messages: [
          ['role' => 'user', 'content' => 'How many orders shipped yesterday?'],
          // Define db_query por valor a partir de este punto. `tools` y los
          // mensajes anteriores quedan tal como se enviaron, así que la caché sigue acertando.
          [
              'role' => 'system',
              'content' => [
                  [
                      'type' => 'tool_addition',
                      'tool' => [
                          'type' => 'tool_definition',
                          'definition' => [
                              'name' => 'db_query',
                              'description' => 'Run a read-only SQL query against the analytics database.',
                              'input_schema' => [
                                  'type' => 'object',
                                  'properties' => ['sql' => ['type' => 'string']],
                                  'required' => ['sql'],
                              ],
                          ],
                      ],
                  ],
              ],
          ],
      ],
  );

  foreach ($response->content as $block) {
      if ($block instanceof BetaToolUseBlock) {
          echo $block->name, ' ', json_encode($block->input), PHP_EOL;
      }
  }
  ```

  ```ruby Ruby
  client = Anthropic::Client.new

  response = client.beta.messages.create(
    model: Anthropic::Model::CLAUDE_OPUS_5_5,
    max_tokens: 1024,
    betas: [Anthropic::AnthropicBeta::INLINE_TOOLS_2026_09_15],
    # Mantén al menos una herramienta no diferida en `tools`, para que una herramienta definida
    # después no cambie el inicio del prompt renderizado.
    tools: [
      {
        name: "get_weather",
        description: "Get the current weather for a location.",
        input_schema: {
          type: "object",
          properties: {
            location: { type: "string", description: "City name" }
          },
          required: ["location"]
        }
      }
    ],
    messages: [
      { role: "user", content: "How many orders shipped yesterday?" },
      # Define db_query por valor a partir de este punto. `tools` y los
      # mensajes anteriores quedan tal como se enviaron, así que la caché sigue acertando.
      {
        role: "system",
        content: [
          {
            type: "tool_addition",
            tool: {
              type: "tool_definition",
              definition: {
                name: "db_query",
                description: "Run a read-only SQL query against the analytics database.",
                input_schema: {
                  type: "object",
                  properties: { sql: { type: "string" } },
                  required: ["sql"]
                }
              }
            }
          }
        ]
      }
    ]
  )

  response.content.each do |block|
    puts "#{block.name} #{block.input}" if block.is_a?(Anthropic::Beta::BetaToolUseBlock)
  end
  ```
</CodeGroup>

El `content` de la respuesta incluye un bloque `tool_use` para la nueva herramienta, por ejemplo:

```json
{
  "type": "tool_use",
  "id": "toolu_01A09q90qw90lq917835lq9",
  "name": "db_query",
  "input": {
    "sql": "SELECT COUNT(*) FROM orders WHERE shipped_at::date = CURRENT_DATE - 1"
  }
}
```

Para cambiar el esquema de una herramienta, o para pasar una herramienta de servidor a una versión más reciente, envía una definición diferente con el mismo nombre. La nueva definición reemplaza a la anterior desde esa posición en adelante. Una definición que reutiliza el nombre de un tipo de herramienta diferente devuelve un error 400 con `error.details.error_code` establecido en `tool_name_conflict`. Una versión más reciente de la misma herramienta no cuenta como un tipo diferente. `tool_removal` sigue recibiendo una referencia, y una herramienta eliminada puede definirse u ofrecerse de nuevo más adelante.

De la posición donde se renderiza la definición se derivan algunas reglas:

* **Declara desde el principio lo que ya conoces.** Una herramienta que conoces en la primera solicitud pertenece a `tools`, con `defer_loading: true` y una referencia `tool_addition` posterior si el modelo todavía no debe verla. Define por valor solo lo que se desconoce en la primera solicitud o lo que cambia más adelante.
* **Mantén al menos una herramienta no diferida en `tools`.** Se acepta una conversación cuyo arreglo `tools` no tiene ninguna herramienta no diferida, pero la primera herramienta que define por valor cambia el inicio del prompt renderizado, lo que cuesta un fallo de caché completo en esa solicitud. Una [herramienta de búsqueda de herramientas](https://platform.claude.com/docs/es/agents-and-tools/tool-use/tool-search-tool) cuenta como no diferida.
* **Los tipos de herramientas con fecha conservan sus propios encabezados beta.** Si una herramienta de servidor que defines por valor requiere su propio encabezado beta, envía ese encabezado en cada solicitud posterior de la conversación.
* **`cache_control` va en el bloque o en la definición, no en ambos,** y cuenta para el límite de puntos de interrupción de la solicitud. Una definición diferida no puede llevar `cache_control`.

Una solicitud devuelve un error 400 con `error.details.error_code` establecido en `available_tools_limit_exceeded` cuando se supera cualquiera de estos límites:

* Hay más de 10,000 herramientas diferidas disponibles después de cualquier mensaje.
* Hay más de 10,000 herramientas definidas después del primer mensaje del usuario disponibles después de cualquier mensaje.
* Las definiciones de herramientas enviadas después del primer mensaje del usuario que siguen disponibles después de cualquier mensaje suman más de 4 MB (4,194,304 bytes).
* El texto de herramientas renderizado supera los 4 MB (4,194,304 bytes).

### Agregar un servidor MCP a mitad de la conversación (beta)

Para agregar un servidor del [conector MCP](https://platform.claude.com/docs/es/agents-and-tools/mcp-connector) a mitad de una conversación, envía el encabezado beta `mcp-client-2026-09-15` junto con `inline-tools-2026-09-15`. La `definition` en un bloque `tool_addition` puede entonces ser un `mcp_toolset`, de modo que las herramientas del servidor quedan disponibles sin editar `tools`. Enumera los detalles de conexión del servidor en `mcp_servers` como de costumbre y luego agrega el conjunto de herramientas donde el servidor pasó a estar disponible:

```json
{
  "role": "system",
  "content": [
    {
      "type": "tool_addition",
      "tool": {
        "type": "tool_definition",
        "definition": { "type": "mcp_toolset", "mcp_server_name": "calendar" }
      }
    }
  ]
}
```

El objeto `mcp_toolset` es el mismo que pondrías en `tools`, incluidos `default_config` y `configs`. Un bloque `tool_addition` nunca contiene una URL ni un token de servidor. Esos permanecen en `mcp_servers`.

La siguiente solicitud mantiene `get_weather` en `tools`, enumera el servidor de calendario en `mcp_servers` y agrega el conjunto de herramientas del servidor después del primer turno del usuario:

<CodeGroup>
  ```bash cURL
  curl https://api.anthropic.com/v1/messages \
    -H "content-type: application/json" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: inline-tools-2026-09-15,mcp-client-2026-09-15" \
    -d '{
      "model": "claude-opus-5-5",
      "max_tokens": 1024,
      "mcp_servers": [
        {
          "type": "url",
          "url": "https://mcp.example.com/calendar",
          "name": "calendar",
          "authorization_token": "YOUR_TOKEN"
        }
      ],
      "tools": [
        {
          "name": "get_weather",
          "description": "Get the current weather for a location.",
          "input_schema": {
            "type": "object",
            "properties": {
              "location": {"type": "string", "description": "City name"}
            },
            "required": ["location"]
          }
        }
      ],
      "messages": [
        {
          "role": "user",
          "content": "What'\''s on my calendar tomorrow?"
        },
        {
          "role": "system",
          "content": [
            {
              "type": "tool_addition",
              "tool": {
                "type": "tool_definition",
                "definition": {
                  "type": "mcp_toolset",
                  "mcp_server_name": "calendar"
                }
              }
            }
          ]
        }
      ]
    }'
  ```

  ```bash CLI
  ant beta:messages create \
    --beta inline-tools-2026-09-15,mcp-client-2026-09-15 <<'YAML'
  model: claude-opus-5-5
  max_tokens: 1024
  mcp_servers:
    - type: url
      url: https://mcp.example.com/calendar
      name: calendar
      authorization_token: YOUR_TOKEN
  tools:
    - name: get_weather
      description: Get the current weather for a location.
      input_schema:
        type: object
        properties:
          location:
            type: string
            description: City name
        required:
          - location
  messages:
    - role: user
      content: What's on my calendar tomorrow?
    # Haz que las herramientas del servidor de calendario estén disponibles a partir de este punto.
    # El bloque nombra el servidor; nunca contiene una URL ni un token.
    - role: system
      content:
        - type: tool_addition
          tool:
            type: tool_definition
            definition:
              type: mcp_toolset
              mcp_server_name: calendar
  YAML
  ```

  ```python Python
  client = anthropic.Anthropic()

  response = client.beta.messages.create(
      model="claude-opus-5-5",
      max_tokens=1024,
      betas=["inline-tools-2026-09-15", "mcp-client-2026-09-15"],
      mcp_servers=[
          {
              "type": "url",
              "url": "https://mcp.example.com/calendar",
              "name": "calendar",
              "authorization_token": "YOUR_TOKEN",
          },
      ],
      tools=[
          {
              "name": "get_weather",
              "description": "Get the current weather for a location.",
              "input_schema": {
                  "type": "object",
                  "properties": {
                      "location": {"type": "string", "description": "City name"},
                  },
                  "required": ["location"],
              },
          },
      ],
      messages=[
          {"role": "user", "content": "What's on my calendar tomorrow?"},
          # Haz que las herramientas del servidor de calendario estén disponibles a partir de este punto.
          # El bloque nombra el servidor; nunca contiene una URL ni un token.
          {
              "role": "system",
              "content": [
                  {
                      "type": "tool_addition",
                      "tool": {
                          "type": "tool_definition",
                          "definition": {
                              "type": "mcp_toolset",
                              "mcp_server_name": "calendar",
                          },
                      },
                  },
              ],
          },
      ],
  )

  # La respuesta comienza con un bloque mcp_tool_listing para el servidor de calendario,
  # así que verifica el tipo de cada bloque en lugar de leer content[0].
  for block in response.content:
      match block.type:
          case "mcp_tool_listing":
              print(block.mcp_server_name, [tool.name for tool in block.tools])
          case "text":
              print(block.text)
  ```

  ```typescript TypeScript
  const client = new Anthropic();

  const response = await client.beta.messages.create({
    model: "claude-opus-5-5",
    max_tokens: 1024,
    betas: ["inline-tools-2026-09-15", "mcp-client-2026-09-15"],
    mcp_servers: [
      {
        type: "url",
        url: "https://mcp.example.com/calendar",
        name: "calendar",
        authorization_token: "YOUR_TOKEN"
      }
    ],
    tools: [
      {
        name: "get_weather",
        description: "Get the current weather for a location.",
        input_schema: {
          type: "object",
          properties: {
            location: { type: "string", description: "City name" }
          },
          required: ["location"]
        }
      }
    ],
    messages: [
      { role: "user", content: "What's on my calendar tomorrow?" },
      // Haz que las herramientas del servidor de calendario estén disponibles a partir de este punto.
      // El bloque nombra el servidor; nunca contiene una URL ni un token.
      {
        role: "system",
        content: [
          {
            type: "tool_addition",
            tool: {
              type: "tool_definition",
              definition: { type: "mcp_toolset", mcp_server_name: "calendar" }
            }
          }
        ]
      }
    ]
  });

  // La respuesta comienza con un bloque mcp_tool_listing para el servidor de calendario,
  // así que verifica el tipo de cada bloque en lugar de leer content[0].
  for (const block of response.content) {
    switch (block.type) {
      case "mcp_tool_listing":
        console.log(
          block.mcp_server_name,
          block.tools.map((tool) => tool.name)
        );
        break;
      case "text":
        console.log(block.text);
        break;
    }
  }
  ```

  ```csharp C#
  using Anthropic.Models.Beta;
  using Anthropic.Models.Beta.Messages;
  using Messages = Anthropic.Models.Messages;

  AnthropicClient client = new();

  var response = await client.Beta.Messages.Create(new MessageCreateParams
  {
      Model = Messages::Model.ClaudeOpus5_5,
      MaxTokens = 1024,
      Betas = [AnthropicBeta.InlineTools2026_09_15, AnthropicBeta.McpClient2026_09_15],
      McpServers =
      [
          new BetaRequestMcpServerUrlDefinition
          {
              Url = "https://mcp.example.com/calendar",
              Name = "calendar",
              AuthorizationToken = "YOUR_TOKEN",
          },
      ],
      Tools =
      [
          new BetaTool
          {
              Name = "get_weather",
              Description = "Get the current weather for a location.",
              InputSchema = new InputSchema
              {
                  Properties = new Dictionary<string, JsonElement>
                  {
                      ["location"] = JsonSerializer.SerializeToElement(new { type = "string", description = "City name" }),
                  },
                  Required = ["location"],
              },
          },
      ],
      Messages =
      [
          new() { Role = Role.User, Content = "What's on my calendar tomorrow?" },
          // Haz que las herramientas del servidor de calendario estén disponibles a partir de este punto.
          // El bloque nombra el servidor; nunca contiene una URL ni un token.
          new()
          {
              Role = Role.System,
              Content = new(
              [
                  new BetaRequestToolAdditionBlock
                  {
                      Tool = new BetaToolChangeToolDefinitionParam
                      {
                          Definition = new BetaMcpToolset("calendar"),
                      },
                  },
              ]),
          },
      ],
  });

  // La respuesta comienza con un bloque mcp_tool_listing para el servidor de calendario,
  // así que verifica el tipo de cada bloque en lugar de leer Content[0].
  foreach (var block in response.Content)
  {
      if (block.TryPickMcpToolListing(out var listing))
      {
          Console.WriteLine($"{listing.McpServerName} {JsonSerializer.Serialize(listing.Tools.Select(tool => tool.Name))}");
      }
      else if (block.TryPickText(out var text))
      {
          Console.WriteLine(text.Text);
      }
  }
  ```

  ```go Go
  client := anthropic.NewClient()

  response, err := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{
  	Model:     anthropic.ModelClaudeOpus5_5,
  	MaxTokens: 1024,
  	Betas: []anthropic.AnthropicBeta{
  		anthropic.AnthropicBetaInlineTools2026_09_15,
  		anthropic.AnthropicBetaMCPClient2026_09_15,
  	},
  	MCPServers: []anthropic.BetaRequestMCPServerURLDefinitionParam{
  		{
  			URL:                "https://mcp.example.com/calendar",
  			Name:               "calendar",
  			AuthorizationToken: anthropic.String("YOUR_TOKEN"),
  		},
  	},
  	Tools: []anthropic.BetaToolUnionParam{
  		{OfTool: &anthropic.BetaToolParam{
  			Name:        "get_weather",
  			Description: anthropic.String("Get the current weather for a location."),
  			InputSchema: anthropic.BetaToolInputSchemaParam{
  				Properties: map[string]any{
  					"location": map[string]any{
  						"type":        "string",
  						"description": "City name",
  					},
  				},
  				Required: []string{"location"},
  			},
  		}},
  	},
  	Messages: []anthropic.BetaMessageParam{
  		anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("What's on my calendar tomorrow?")),
  		// Haz que las herramientas del servidor de calendario estén disponibles a partir de este punto.
  		// El bloque nombra el servidor; nunca contiene una URL ni un token.
  		{
  			Role: anthropic.BetaMessageParamRoleSystem,
  			Content: []anthropic.BetaContentBlockParamUnion{
  				anthropic.NewBetaToolAdditionBlock(anthropic.BetaToolChangeToolDefinitionParam{
  					Definition: anthropic.BetaToolUnionParam{OfMCPToolset: &anthropic.BetaMCPToolsetParam{
  						MCPServerName: "calendar",
  					}},
  				}),
  			},
  		},
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }

  // La respuesta comienza con un bloque mcp_tool_listing para el servidor de calendario,
  // así que verifica el tipo de cada bloque en lugar de leer Content[0].
  for _, block := range response.Content {
  	switch variant := block.AsAny().(type) {
  	case anthropic.BetaMCPToolListingBlock:
  		var toolNames []string
  		for _, tool := range variant.Tools {
  			toolNames = append(toolNames, tool.Name)
  		}
  		fmt.Println(variant.MCPServerName, toolNames)
  	case anthropic.BetaTextBlock:
  		fmt.Println(variant.Text)
  	}
  }
  ```

  ```java Java
  import com.anthropic.models.beta.AnthropicBeta;
  import com.anthropic.models.beta.messages.BetaContentBlockParam;
  import com.anthropic.models.beta.messages.BetaMcpTool;
  import com.anthropic.models.beta.messages.BetaMcpToolset;
  import com.anthropic.models.beta.messages.BetaMessage;
  import com.anthropic.models.beta.messages.BetaRequestMcpServerUrlDefinition;
  import com.anthropic.models.beta.messages.BetaRequestToolAdditionBlock;
  import com.anthropic.models.beta.messages.BetaTool;
  import com.anthropic.models.beta.messages.MessageCreateParams;
  // ...

  void main() {
      AnthropicClient client = AnthropicOkHttpClient.fromEnv();

      BetaTool weatherTool = BetaTool.builder()
          .name("get_weather")
          .description("Get the current weather for a location.")
          .inputSchema(BetaTool.InputSchema.builder()
              .properties(BetaTool.InputSchema.Properties.builder()
                  .putAdditionalProperty("location", JsonValue.from(Map.of(
                      "type", "string",
                      "description", "City name")))
                  .build())
              .addRequired("location")
              .build())
          .build();

      MessageCreateParams params = MessageCreateParams.builder()
          .model(Model.CLAUDE_OPUS_5_5)
          .maxTokens(1024)
          .addBeta(AnthropicBeta.INLINE_TOOLS_2026_09_15)
          .addBeta(AnthropicBeta.MCP_CLIENT_2026_09_15)
          .addMcpServer(BetaRequestMcpServerUrlDefinition.builder()
              .url("https://mcp.example.com/calendar")
              .name("calendar")
              .authorizationToken("YOUR_TOKEN")
              .build())
          .addTool(weatherTool)
          .addUserMessage("What's on my calendar tomorrow?")
          // Haz que las herramientas del servidor de calendario estén disponibles a partir de este punto.
          // El bloque nombra el servidor; nunca contiene una URL ni un token.
          .addSystemMessageOfBetaContentBlockParams(List.of(
              BetaContentBlockParam.ofToolAddition(BetaRequestToolAdditionBlock.builder()
                  .definitionTool(BetaMcpToolset.builder()
                      .mcpServerName("calendar")
                      .build())
                  .build())))
          .build();

      BetaMessage response = client.beta().messages().create(params);

      // La respuesta comienza con un bloque mcp_tool_listing para el servidor de calendario,
      // así que verifica el tipo de cada bloque en lugar de leer el primer bloque.
      for (var block : response.content()) {
          switch (block.type().value()) {
              case MCP_TOOL_LISTING -> {
                  var listing = block.asMcpToolListing();
                  var toolNames = listing.tools().stream().map(BetaMcpTool::name).toList();
                  IO.println(listing.mcpServerName() + " " + toolNames);
              }
              case TEXT -> IO.println(block.asText().text());
          }
      }
  }
  ```

  ```php PHP
  use Anthropic\Beta\AnthropicBeta;
  use Anthropic\Beta\Messages\BetaMCPTool;
  use Anthropic\Beta\Messages\BetaMCPToolListingBlock;
  use Anthropic\Beta\Messages\BetaTextBlock;
  // ...

  $client = new Client();

  $response = $client->beta->messages->create(
      model: Model::CLAUDE_OPUS_5_5,
      maxTokens: 1024,
      betas: [
          AnthropicBeta::INLINE_TOOLS_2026_09_15,
          AnthropicBeta::MCP_CLIENT_2026_09_15,
      ],
      mcpServers: [
          [
              'type' => 'url',
              'url' => 'https://mcp.example.com/calendar',
              'name' => 'calendar',
              'authorization_token' => 'YOUR_TOKEN',
          ],
      ],
      tools: [
          [
              'name' => 'get_weather',
              'description' => 'Get the current weather for a location.',
              'input_schema' => [
                  'type' => 'object',
                  'properties' => [
                      'location' => [
                          'type' => 'string',
                          'description' => 'City name',
                      ],
                  ],
                  'required' => ['location'],
              ],
          ],
      ],
      messages: [
          ['role' => 'user', 'content' => "What's on my calendar tomorrow?"],
          // Haz que las herramientas del servidor de calendario estén disponibles a partir de aquí.
          // El bloque nombra el servidor; nunca contiene una URL ni un token.
          [
              'role' => 'system',
              'content' => [
                  [
                      'type' => 'tool_addition',
                      'tool' => [
                          'type' => 'tool_definition',
                          'definition' => [
                              'type' => 'mcp_toolset',
                              'mcp_server_name' => 'calendar',
                          ],
                      ],
                  ],
              ],
          ],
      ],
  );

  // La respuesta comienza con un bloque mcp_tool_listing para el servidor de calendario,
  // así que verifica el tipo de cada bloque en lugar de leer content[0].
  foreach ($response->content as $block) {
      switch (true) {
          case $block instanceof BetaMCPToolListingBlock:
              $toolNames = array_map(fn (BetaMCPTool $tool) => $tool->name, $block->tools);
              echo $block->mcpServerName, ' ', json_encode($toolNames), PHP_EOL;
              break;
          case $block instanceof BetaTextBlock:
              echo $block->text, PHP_EOL;
              break;
      }
  }
  ```

  ```ruby Ruby
  client = Anthropic::Client.new

  response = client.beta.messages.create(
    model: Anthropic::Model::CLAUDE_OPUS_5_5,
    max_tokens: 1024,
    betas: [
      Anthropic::AnthropicBeta::INLINE_TOOLS_2026_09_15,
      Anthropic::AnthropicBeta::MCP_CLIENT_2026_09_15
    ],
    mcp_servers: [
      {
        type: "url",
        url: "https://mcp.example.com/calendar",
        name: "calendar",
        authorization_token: "YOUR_TOKEN"
      }
    ],
    tools: [
      {
        name: "get_weather",
        description: "Get the current weather for a location.",
        input_schema: {
          type: "object",
          properties: {
            location: { type: "string", description: "City name" }
          },
          required: ["location"]
        }
      }
    ],
    messages: [
      { role: "user", content: "What's on my calendar tomorrow?" },
      # Haz que las herramientas del servidor de calendario estén disponibles a partir de este punto.
      # El bloque nombra el servidor; nunca contiene una URL ni un token.
      {
        role: "system",
        content: [
          {
            type: "tool_addition",
            tool: {
              type: "tool_definition",
              definition: { type: "mcp_toolset", mcp_server_name: "calendar" }
            }
          }
        ]
      }
    ]
  )

  # La respuesta comienza con un bloque mcp_tool_listing para el servidor de calendario,
  # así que verifica el tipo de cada bloque en lugar de leer content[0].
  response.content.each do |block|
    case block
    when Anthropic::Beta::BetaMCPToolListingBlock
      puts "#{block.mcp_server_name} #{block.tools.map(&:name)}"
    when Anthropic::Beta::BetaTextBlock
      puts block.text
    end
  end
  ```
</CodeGroup>

Con `mcp-client-2026-09-15`, una respuesta para la cual la API obtuvo la lista de herramientas de un servidor comienza con un bloque `mcp_tool_listing`, uno por cada servidor consultado. Si tu código lee `content[0]`, omite estos bloques. Devuelve el mensaje del asistente sin cambios, incluido este bloque, y sigue enviando `mcp-client-2026-09-15` en cada solicitud que lo contenga. Las solicitudes posteriores usan entonces la lista registrada en lugar de volver a consultar al servidor. Para fijar tú mismo un conjunto de herramientas, copia esa lista en el campo `tools` del `mcp_toolset`, como se describe en [Fijar la lista de herramientas de un servidor MCP](https://platform.claude.com/docs/es/agents-and-tools/mcp-connector#pin-mcp-tool-list).

`mcp-client-2026-09-15` incluye todo lo que incluye `mcp-client-2025-11-20`, por lo que no necesitas enviar ambos. Estas funciones están disponibles en la API de Claude. Las solicitudes que usan el conector MCP mantienen sus condiciones de [retención de datos](https://platform.claude.com/docs/es/agents-and-tools/mcp-connector#data-retention).

## Cuándo usar un mensaje del sistema a mitad de conversación

El [almacenamiento en caché de prompts](https://platform.claude.com/docs/es/build-with-claude/prompt-caching) aplica hash al prefijo de la solicitud en orden: `tools`, luego `system`, luego `messages`. Un acierto de caché requiere que el prefijo coincida exactamente con una solicitud reciente, byte por byte, hasta el punto de interrupción de caché.

Ese orden significa que el campo `system` de nivel superior se ubica muy cerca del inicio del prefijo con hash. Cualquier cambio en él, incluso agregar una oración, produce un hash diferente, y la solicitud falla en la caché para la indicación del sistema y cada mensaje en caché posterior.

Los mensajes del sistema a mitad de conversación te permiten agregar la instrucción al **final** del historial de mensajes en su lugar. Todo lo anterior a la nueva instrucción permanece sin cambios, por lo que la entrada de caché existente aún coincide, y solo el nuevo mensaje se procesa como entrada nueva.

Algunas situaciones en las que esto importa:

* **Cambios de política o de persona a mitad de sesión.** Una sesión agéntica larga necesita una nueva restricción ("de ahora en adelante, escribe todo el SQL como consultas parametrizadas") después de docenas de turnos en caché. Agregarla al campo `system` de nivel superior volvería a procesar todo el historial.
* **Contexto por turno que debe ser autoritativo.** Quieres inyectar una nota de actualidad, una fecha límite de sesión o un cambio de disponibilidad de herramientas con peso de nivel de sistema, y cambia con demasiada frecuencia para vivir en el prefijo en caché.
* **Recordatorios por turno que no deberían acumularse.** Un arnés da un empujón al modelo después de cada lote de resultados de herramientas ("solicita las lecturas independientes juntas", "el usuario no ha sabido de ti en un rato") y quiere que el modelo vea solo la copia más reciente. Un [mensaje del sistema con alcance de turno](https://platform.claude.com/docs/es/build-with-claude/mid-conversation-system-messages#turn-scoped-system-messages) se renderiza durante un turno y luego no cuesta nada, sin eliminar nada del historial.
* **Cambios de estado que tu aplicación observa.** Tu aplicación nota algo que Claude debería tratar como un hecho de nivel de operador: archivos que cambiaron en disco, el usuario activó o desactivó una configuración de aprobación automática, las herramientas disponibles cambiaron o el presupuesto de tokens restante cayó por debajo de un umbral.
* **Entrada del usuario que no debería interrumpir un bucle agéntico.** Un usuario escribe un seguimiento mientras Claude todavía está ejecutando herramientas para la solicitud anterior. Transmitirlo como un mensaje del sistema después del siguiente resultado de herramienta permite que Claude incorpore la nueva entrada al trabajo que ya está haciendo, en lugar de tratarla como una solicitud nueva a la que cambiar. En Claude Sonnet 5.5 y Claude Haiku 5.5, agrega en su lugar las palabras del usuario como un bloque `text` después del último `tool_result` en el mismo mensaje `user`. Consulta [Ubicación después de los resultados de herramientas](https://platform.claude.com/docs/es/build-with-claude/mid-conversation-system-messages#placement-after-tool-results).
* **Cambios de modo que otorgan permisos permanentes.** Un modo de nivel de sesión puede usar un mensaje del sistema a mitad de conversación para otorgar consentimiento permanente a una capacidad costosa, como lanzar automáticamente flujos de trabajo multiagente, con un breve recordatorio cada varios turnos y un aviso de salida cuando el modo se desactiva. Para un ejemplo desarrollado, consulta [Construir un modo de orquestación](https://platform.claude.com/docs/es/build-with-claude/mid-conversation-effort-example).

En todos estos casos podrías poner la instrucción en un mensaje `user` normal, y Claude sí sigue las instrucciones que llegan en turnos de usuario. La diferencia es la prioridad: un mensaje `user` se trata como proveniente del usuario final, mientras que un mensaje `system` se trata como proveniente de ti, el operador de la aplicación. Cuando ambos entran en conflicto, las instrucciones del sistema tienen precedencia, así que usa el rol `system` para hechos y restricciones de nivel de operador que deben mantenerse incluso si el usuario final pide algo diferente. Un mensaje del sistema a mitad de conversación conserva esa prioridad de nivel de operador sin pagar el costo de fallo de caché de editar el campo `system` de nivel superior.

## Cómo funciona

Agrega un mensaje con `"role": "system"` al arreglo `messages`. Usa una cadena simple o bloques de contenido para `content`, igual que en un turno `user` o `assistant`. La instrucción se aplica desde ese punto de la conversación en adelante. Cuando las instrucciones entran en conflicto, los mensajes del sistema posteriores tienen prioridad sobre los anteriores, y los mensajes del sistema a mitad de la conversación tienen prioridad sobre el campo `system` de nivel superior para los turnos que les siguen.

Aún puedes establecer el campo `system` de nivel superior para instrucciones que deben aplicarse a toda la conversación. Reserva los mensajes del sistema a mitad de la conversación para instrucciones que solo se vuelven relevantes más adelante, o que quieres agregar sin invalidar el prefijo almacenado en caché.

Un mensaje `role: "system"` también puede llevar `output_config.effort` para cambiar el nivel de [esfuerzo](https://platform.claude.com/docs/es/build-with-claude/effort) a mitad de una conversación. Esto está en beta en Claude Fable 5.1, Claude Mythos 5.1, Claude Opus 5.5, Claude Opus 5, Claude Sonnet 5.5 y Claude Haiku 5.5 en la API de Claude y Google Cloud, y requiere el encabezado beta `mid-conversation-output-config-2026-07-01`. En Amazon Bedrock, está en beta en Claude Fable 5.1, Claude Mythos 5.1 y Claude Opus 5.5, con el mismo valor beta. Claude Opus 5 no lo admite en Amazon Bedrock. Consulta [Esfuerzo por mensaje](https://platform.claude.com/docs/es/build-with-claude/effort#change-effort-mid-conversation-beta).

<CodeGroup>
  ```bash cURL
  curl https://api.anthropic.com/v1/messages \
    -H "content-type: application/json" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -d '{
      "model": "claude-opus-5-5",
      "max_tokens": 1024,
      "cache_control": {"type": "ephemeral"},
      "system": "You are a code review assistant. Be concise.",
      "messages": [
        {
          "role": "user",
          "content": "Review process() in utils.py for performance issues."
        },
        {
          "role": "assistant",
          "content": "The list comprehension is fine for small inputs. For large inputs, consider a generator to avoid materializing the full list."
        },
        {
          "role": "user",
          "content": "Now review the calling code that invokes process()."
        },
        {
          "role": "system",
          "content": "From now on, every suggestion must include explicit type annotations."
        }
      ]
    }'
  ```

  ```bash CLI
  ant messages create --transform 'content.#(type=="text").text' --raw-output <<'YAML'
  model: claude-opus-5-5
  max_tokens: 1024
  cache_control:
    type: ephemeral
  system: You are a code review assistant. Be concise.
  messages:
    - role: user
      content: Review process() in utils.py for performance issues.
    - role: assistant
      content: >-
        The list comprehension is fine for small inputs. For large inputs,
        consider a generator to avoid materializing the full list.
    - role: user
      content: Now review the calling code that invokes process().
    - role: system
      content: From now on, every suggestion must include explicit type annotations.
  YAML
  ```

  ```python Python
  client = anthropic.Anthropic()

  response = client.messages.create(
      model="claude-opus-5-5",
      max_tokens=1024,
      # Almacenamiento automático en caché de prompts: cada solicitud guarda en caché la conversación hasta ahora,
      # y la siguiente solicitud lee desde la caché el prefijo sin cambios.
      cache_control={"type": "ephemeral"},
      system="You are a code review assistant. Be concise.",
      messages=[
          {
              "role": "user",
              "content": "Review process() in utils.py for performance issues.",
          },
          {
              "role": "assistant",
              "content": "The list comprehension is fine for small inputs. For large inputs, consider a generator to avoid materializing the full list.",
          },
          {
              "role": "user",
              "content": "Now review the calling code that invokes process().",
          },
          # A mitad de la sesión, el revisor se da cuenta de que todas las sugerencias deben
          # cumplir también la estricta política de tipado del equipo. Agregar la
          # instrucción aquí mantiene los turnos anteriores idénticos byte a byte, así que el
          # prefijo guardado en caché por la solicitud anterior se sigue leyendo desde la caché.
          {
              "role": "system",
              "content": "From now on, every suggestion must include explicit type annotations.",
          },
      ],
  )

  for block in response.content:
      if block.type == "text":
          print(block.text)
  ```

  ```typescript TypeScript
  const client = new Anthropic();

  const response = await client.messages.create({
    model: "claude-opus-5-5",
    max_tokens: 1024,
    // "Prompt caching" automático (almacenamiento en caché de prompts): cada solicitud guarda en caché la conversación hasta ahora,
    // y la siguiente solicitud lee el prefijo sin cambios desde la caché.
    cache_control: { type: "ephemeral" },
    system: "You are a code review assistant. Be concise.",
    messages: [
      {
        role: "user",
        content: "Review process() in utils.py for performance issues."
      },
      {
        role: "assistant",
        content:
          "The list comprehension is fine for small inputs. For large inputs, consider a generator to avoid materializing the full list."
      },
      {
        role: "user",
        content: "Now review the calling code that invokes process()."
      },
      // El revisor se da cuenta a mitad de la sesión de que todas las sugerencias también deben cumplir
      // la estricta política de tipado del equipo. Agregar la instrucción aquí mantiene
      // los turnos anteriores idénticos byte a byte, así que el prefijo guardado en caché por la solicitud
      // anterior se sigue leyendo desde la caché.
      {
        role: "system",
        content: "From now on, every suggestion must include explicit type annotations."
      }
    ]
  });

  const textBlock = response.content.find(
    (block): block is Anthropic.TextBlock => block.type === "text"
  );
  console.log(textBlock?.text);
  ```

  ```csharp C#
  AnthropicClient client = new();

  var parameters = new MessageCreateParams
  {
      Model = Model.ClaudeOpus5_5,
      MaxTokens = 1024,
      // Prompt caching (almacenamiento en caché de prompts) automático: cada solicitud guarda en caché la conversación hasta ahora,
      // y la siguiente solicitud lee el prefijo sin cambios desde la caché.
      CacheControl = new CacheControlEphemeral(),
      System = "You are a code review assistant. Be concise.",
      Messages =
      [
          new()
          {
              Role = Role.User,
              Content = "Review process() in utils.py for performance issues."
          },
          new()
          {
              Role = Role.Assistant,
              Content = "The list comprehension is fine for small inputs. For large inputs, consider a generator to avoid materializing the full list."
          },
          new()
          {
              Role = Role.User,
              Content = "Now review the calling code that invokes process()."
          },
          // El revisor se da cuenta a mitad de sesión de que todas las sugerencias también deben cumplir
          // la estricta política de tipado del equipo. Agregar la instrucción aquí mantiene
          // los turnos anteriores idénticos byte a byte, así que el prefijo guardado en caché por la solicitud
          // anterior se sigue leyendo desde la caché.
          new()
          {
              Role = Role.System,
              Content = "From now on, every suggestion must include explicit type annotations."
          }
      ]
  };

  var response = await client.Messages.Create(parameters);
  Console.WriteLine(response);
  ```

  ```go Go
  client := anthropic.NewClient()

  response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
  	Model:     anthropic.ModelClaudeOpus5_5,
  	MaxTokens: 1024,
  	// "Prompt caching" (almacenamiento en caché de prompts) automático: cada solicitud guarda en caché la conversación,
  	// y la siguiente solicitud lee el prefijo sin cambios desde la caché.
  	CacheControl: anthropic.NewCacheControlEphemeralParam(),
  	System: []anthropic.TextBlockParam{
  		{Text: "You are a code review assistant. Be concise."},
  	},
  	Messages: []anthropic.MessageParam{
  		anthropic.NewUserMessage(anthropic.NewTextBlock("Review process() in utils.py for performance issues.")),
  		anthropic.NewAssistantMessage(anthropic.NewTextBlock("The list comprehension is fine for small inputs. For large inputs, consider a generator to avoid materializing the full list.")),
  		anthropic.NewUserMessage(anthropic.NewTextBlock("Now review the calling code that invokes process().")),
  		// El revisor se da cuenta a mitad de la sesión de que todas las sugerencias también deben
  		// cumplir la política de tipado estricto del equipo. Añadir la instrucción
  		// aquí mantiene los turnos anteriores idénticos byte a byte, así que el prefijo en caché de
  		// la solicitud anterior se sigue leyendo desde la caché.
  		{
  			Role: anthropic.MessageParamRoleSystem,
  			Content: []anthropic.ContentBlockParamUnion{
  				anthropic.NewTextBlock("From now on, every suggestion must include explicit type annotations."),
  			},
  		},
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }
  for _, block := range response.Content {
  	if textBlock, ok := block.AsAny().(anthropic.TextBlock); ok {
  		fmt.Println(textBlock.Text)
  	}
  }
  ```

  ```java Java
  import com.anthropic.models.messages.CacheControlEphemeral;
  // ...
  import com.anthropic.models.messages.MessageParam;
  // ...
      AnthropicClient client = AnthropicOkHttpClient.fromEnv();

      MessageCreateParams params = MessageCreateParams.builder()
          .model(Model.CLAUDE_OPUS_5_5)
          .maxTokens(1024)
          // Almacenamiento automático en caché de prompts (prompt caching): cada solicitud guarda en caché la conversación hasta ahora,
          // y la siguiente solicitud lee el prefijo sin cambios desde la caché.
          .cacheControl(CacheControlEphemeral.builder().build())
          .system("You are a code review assistant. Be concise.")
          .addUserMessage("Review process() in utils.py for performance issues.")
          .addAssistantMessage("The list comprehension is fine for small inputs. For large inputs, consider a generator to avoid materializing the full list.")
          .addUserMessage("Now review the calling code that invokes process().")
          // El revisor se da cuenta a mitad de la sesión de que todas las sugerencias también deben cumplir
          // la estricta política de tipado del equipo. Agregar la instrucción aquí mantiene
          // los turnos anteriores idénticos byte a byte, así que el prefijo guardado en caché por la solicitud
          // anterior se sigue leyendo desde la caché.
          .addMessage(MessageParam.builder()
              .role(MessageParam.Role.SYSTEM)
              .content("From now on, every suggestion must include explicit type annotations.")
              .build())
          .build();

      Message response = client.messages().create(params);
      response.content().stream()
          .flatMap(block -> block.text().stream())
          .forEach(textBlock -> IO.println(textBlock.text()));
  ```

  ```php PHP
  use Anthropic\Messages\CacheControlEphemeral;
  // ...
  $client = new Client();

  $response = $client->messages->create(
      maxTokens: 1024,
      messages: [
          ['role' => 'user', 'content' => 'Review process() in utils.py for performance issues.'],
          ['role' => 'assistant', 'content' => 'The list comprehension is fine for small inputs. For large inputs, consider a generator to avoid materializing the full list.'],
          ['role' => 'user', 'content' => 'Now review the calling code that invokes process().'],
          // El revisor se da cuenta a mitad de la sesión de que todas las sugerencias también deben cumplir
          // la política de tipado estricto del equipo. Agregar la instrucción aquí mantiene
          // los turnos anteriores idénticos byte a byte, así que el prefijo almacenado en caché por la solicitud
          // anterior se sigue leyendo desde la caché.
          ['role' => 'system', 'content' => 'From now on, every suggestion must include explicit type annotations.']
      ],
      model: 'claude-opus-5-5',
      // Almacenamiento en caché de prompts automático: cada solicitud guarda en caché la conversación hasta ahora,
      // y la siguiente solicitud lee el prefijo sin cambios desde la caché.
      cacheControl: CacheControlEphemeral::with(),
      system: 'You are a code review assistant. Be concise.',
  );

  foreach ($response->content as $block) {
      if ($block->type === 'text') {
          echo $block->text, PHP_EOL;
      }
  }
  ```

  ```ruby Ruby
  client = Anthropic::Client.new

  response = client.messages.create(
    model: "claude-opus-5-5",
    max_tokens: 1024,
    # "Prompt caching" (almacenamiento en caché de prompts) automático: cada solicitud guarda en caché la conversación,
    # y la siguiente solicitud lee de la caché el prefijo sin cambios.
    cache_control: { type: "ephemeral" },
    system: "You are a code review assistant. Be concise.",
    messages: [
      { role: "user", content: "Review process() in utils.py for performance issues." },
      { role: "assistant", content: "The list comprehension is fine for small inputs. For large inputs, consider a generator to avoid materializing the full list." },
      { role: "user", content: "Now review the calling code that invokes process()." },
      # A mitad de la sesión, el revisor se da cuenta de que todas las sugerencias también deben cumplir
      # la estricta política de tipado del equipo. Añadir la instrucción aquí mantiene
      # los turnos anteriores idénticos byte a byte, así que el prefijo guardado en caché por la
      # solicitud anterior se sigue leyendo de la caché.
      { role: "system", content: "From now on, every suggestion must include explicit type annotations." }
    ]
  )

  response.content.each do |block|
    puts block.text if block.type == :text
  end
  ```
</CodeGroup>

Este ejemplo habilita el [almacenamiento en caché automático](https://platform.claude.com/docs/es/build-with-claude/prompt-caching#automatic-caching) con el campo `cache_control` de nivel superior. El almacenamiento en caché de prompts es opcional: si una solicitud no tiene un campo `cache_control` (automático o un [punto de interrupción explícito](https://platform.claude.com/docs/es/build-with-claude/prompt-caching#explicit-cache-breakpoints)), no se almacena nada en caché y cada solicitud paga el precio normal de tokens de entrada por toda la conversación. Con el almacenamiento en caché habilitado, agregar el mensaje del sistema deja sin cambios los turnos ya almacenados en caché, por lo que la solicitud que lleva la nueva instrucción sigue leyéndolos desde la caché en lugar de procesarlos de nuevo. El almacenamiento en caché también requiere que la conversación alcance la [longitud mínima de prompt almacenable en caché](https://platform.claude.com/docs/es/build-with-claude/prompt-caching#cache-limitations); un ejemplo tan corto como este queda por debajo de ella, por lo que `cache_creation_input_tokens` y `cache_read_input_tokens` permanecen en 0 hasta que la conversación crece.

Un mensaje del sistema a mitad de la conversación que lleva contenido (bloques `text`, `tool_addition` o `tool_removal`) debe seguir inmediatamente a un turno `user` (o a un turno `assistant` que termina en un resultado de herramienta de servidor), y debe ser la última entrada en `messages` o estar seguido inmediatamente por un turno `assistant`. Un mensaje `user` que lleva bloques `tool_result` cuenta: en un bucle agéntico puedes colocar el mensaje del sistema justo después de los resultados de herramientas, antes del siguiente turno de Claude. Cualquier otra posición para un mensaje que lleva contenido, incluida la que está entre un bloque `tool_use` de `assistant` y el `tool_result` que le responde, devuelve un error 400. Un mensaje con `content` vacío que solo establece [`output_config.effort`](https://platform.claude.com/docs/es/build-with-claude/effort#change-effort-mid-conversation-beta) se acepta en cualquier lugar de `messages`. Los mensajes `system` consecutivos se evalúan en conjunto, por lo que un mensaje que solo establece el esfuerzo junto a uno que lleva contenido sigue la regla de contenido.

### Ubicación después de los resultados de herramientas

En un [bucle agéntico](https://platform.claude.com/docs/es/agents-and-tools/tool-use/overview), el mensaje del sistema va después del mensaje `user` que entrega los resultados de herramientas. En la mayoría de los modelos, aquí es también donde tu aplicación puede transmitir la entrada que el usuario escribió mientras Claude trabajaba, de modo que el nuevo contexto se absorba sin reiniciar el turno (para Claude Sonnet 5.5 y Claude Haiku 5.5, consulta el final de esta sección):

```json
[
  { "role": "user", "content": "Run the test suite and fix any failures." },
  {
    "role": "assistant",
    "content": [{ "type": "tool_use", "id": "toolu_01", "name": "run_tests", "input": {} }]
  },
  {
    "role": "user",
    "content": [
      { "type": "tool_result", "tool_use_id": "toolu_01", "content": "12 passed, 0 failed" }
    ]
  },
  {
    "role": "system",
    "content": "The user sent the following message while you were working: also update the changelog before you finish."
  }
]
```

Redacta el contenido del sistema como contexto en lugar de como una orden que anula al usuario. Enuncia el hecho ("llegó nueva entrada del usuario: X", "el presupuesto de tokens restante ahora es Y") y deja que Claude actúe en consecuencia. Claude está entrenado para resistir instrucciones que parecen ir en contra del usuario, y esa protección sigue aplicándose al rol del sistema, por lo que un lenguaje como "ignora lo que dijo el usuario" es menos efectivo que enunciar qué cambió.

Este patrón es para transmitir entrada del propio usuario final de la conversación. No lo uses para pasar salida de herramientas, documentos recuperados u otro contenido de terceros; mantén ese contenido en bloques `tool_result` (consulta [Limitaciones](https://platform.claude.com/docs/es/build-with-claude/mid-conversation-system-messages#limitations)).

En Claude Sonnet 5.5 y Claude Haiku 5.5, entrega en su lugar las palabras del usuario en el turno del usuario. Estos modelos están entrenados para resistir la inyección de prompts a través de resultados de herramientas, por lo que pueden tratar un mensaje del sistema colocado justo después de un resultado de herramienta como texto no confiable e ignorarlo. Agrega las palabras del usuario como un bloque `text` después del último `tool_result` en el mismo mensaje `user`. Mantén los avisos del arnés, como los recordatorios, en un mensaje del sistema a mitad de la conversación separado después de ese mensaje `user`, y nunca pongas un aviso y las palabras del usuario en el mismo bloque:

```json
[
  { "role": "user", "content": "Run the test suite and fix any failures." },
  {
    "role": "assistant",
    "content": [{ "type": "tool_use", "id": "toolu_01", "name": "run_tests", "input": {} }]
  },
  {
    "role": "user",
    "content": [
      { "type": "tool_result", "tool_use_id": "toolu_01", "content": "12 passed, 0 failed" },
      { "type": "text", "text": "Also update the changelog before you finish." }
    ]
  }
]
```

Para obtener más información sobre este comportamiento, consulta [Cómo escribir prompts para Claude Sonnet 5.5](https://platform.claude.com/docs/es/build-with-claude/prompt-engineering/prompting-claude-sonnet-5-5#mid-turn-user-messages-and-task-budgets) y [Cómo escribir prompts para Claude Haiku 5.5](https://platform.claude.com/docs/es/build-with-claude/prompt-engineering/prompting-claude-haiku-5-5#mid-turn-user-messages).

### Mensajes del sistema con alcance de turno

Para limitar un mensaje `role: "system"` al turno actual, establece su campo `clear_at`. Acepta uno de dos valores:

* `"never"` (el valor predeterminado): el mensaje se renderiza en su posición en cada solicitud que lo incluye. Omitir el campo es idéntico.
* `"next_user_message"`: el mensaje tiene **alcance de turno**. Su texto se renderiza solo mientras no haya ningún mensaje `role: "user"` después de él en `messages`. Un mensaje de usuario que lleva solo bloques `tool_result` cuenta aquí como mensaje de usuario. Una vez que existe un mensaje de usuario posterior, el mensaje queda **borrado**: permanece en el arreglo, pero no renderiza nada y no cuesta tokens de entrada, en esa solicitud y en todas las posteriores.

Los mensajes del sistema con alcance de turno están en beta. Incluye el [encabezado beta](https://platform.claude.com/docs/es/api/beta-headers) `mid-conversation-system-clear-at-2026-08-21`. Sin él, `clear_at` se rechaza como campo desconocido.

```json
{
  "role": "system",
  "clear_at": "next_user_message",
  "content": "First privately list what you need next; then request every item that doesn't depend on another's result in this one response."
}
```

El uso principal es un recordatorio por turno en un bucle de herramientas. Agrega el recordatorio después del mensaje `tool_result` cada vez que quieras que el modelo lo vea, y deja cada copia anterior donde está. El modelo ve solo las copias que vienen después del último mensaje del usuario, por lo que el recordatorio nunca se acumula. Nada anterior en `messages` cambia, por lo que la [caché de prompts](https://platform.claude.com/docs/es/build-with-claude/prompt-caching) sigue coincidiendo. En Claude Fable 5.1, Claude Opus 5.5, Claude Sonnet 5.5 y Claude Haiku 5.5, esto también mantiene [válidos los bloques de pensamiento](https://platform.claude.com/docs/es/build-with-claude/thinking#preserved-in-conversation) posteriores: eliminar un recordatorio anterior cambiaría la conversación antes de esos bloques y haría fallar la verificación de la conversación, mientras que un mensaje borrado permanece en el arreglo y deja esa conversación sin cambios.

La siguiente solicitud es un paso posterior de un bucle de agente. `messages[3]` se renderizó en la solicitud anterior, cuando era el último mensaje del arreglo. Una vez que existe `messages[5]` (un mensaje de usuario posterior), `messages[3]` queda borrado: el mensaje borrado permanece en el arreglo, por lo que la conversación previa al bloque de pensamiento en `messages[4]` no cambia, pero el modelo ya no ve su texto. `messages[6]` y `messages[7]` se renderizan ambos, en orden.

```json
{
  "model": "claude-fable-5-1",
  "max_tokens": 16000,
  "messages": [
    { "role": "user", "content": "Fix the failing test." },
    {
      "role": "assistant",
      "content": [
        { "type": "thinking", "thinking": "", "signature": "..." },
        {
          "type": "tool_use",
          "id": "toolu_01",
          "name": "read_file",
          "input": { "path": "test_auth.py" }
        }
      ]
    },
    {
      "role": "user",
      "content": [{ "type": "tool_result", "tool_use_id": "toolu_01", "content": "..." }]
    },
    {
      "role": "system",
      "clear_at": "next_user_message",
      "content": "Request independent reads in one turn."
    },
    {
      "role": "assistant",
      "content": [
        { "type": "thinking", "thinking": "", "signature": "..." },
        {
          "type": "tool_use",
          "id": "toolu_02",
          "name": "read_file",
          "input": { "path": "auth.py" }
        },
        {
          "type": "tool_use",
          "id": "toolu_03",
          "name": "read_file",
          "input": { "path": "tokens.py" }
        }
      ]
    },
    {
      "role": "user",
      "content": [
        { "type": "tool_result", "tool_use_id": "toolu_02", "content": "..." },
        {
          "type": "tool_result",
          "tool_use_id": "toolu_03",
          "content": "...",
          "cache_control": { "type": "ephemeral" }
        }
      ]
    },
    {
      "role": "system",
      "clear_at": "next_user_message",
      "content": "Request independent reads in one turn."
    },
    {
      "role": "system",
      "clear_at": "next_user_message",
      "content": "The shell exited with status 137."
    }
  ]
}
```

Reglas para los mensajes con alcance de turno:

* **Reenvía los mensajes borrados textualmente.** Un mensaje borrado sigue siendo parte del historial de la conversación. Reconstruirlo a partir del estado actual (un recuento de tokens nuevo, una marca de tiempo), descartarlo por redundante o cambiar su valor `clear_at` es una edición de un mensaje anterior. La caché de prompts falla a partir de ese punto, y en Claude Fable 5.1, Claude Opus 5.5, Claude Sonnet 5.5 y Claude Haiku 5.5, cada bloque de pensamiento producido después de él no supera la [verificación de la conversación](https://platform.claude.com/docs/es/build-with-claude/thinking#preserved-in-conversation).
* **Solo texto.** `content` es uno o más bloques `text` (o una cadena). Los bloques `tool_addition` y `tool_removal` devuelven un error 400 en un mensaje con alcance de turno, y lo mismo ocurre con `output_config`. Para esos casos, usa un mensaje `role: "system"` separado sin `clear_at`.
* **Sin `cache_control` en sus bloques.** Un mensaje borrado nunca forma parte de una clave de caché, por lo que un punto de interrupción en él nunca podría coincidir. En su lugar, coloca el punto de interrupción en el último bloque del turno de usuario anterior, como hace el ejemplo. El campo de nivel superior de [almacenamiento en caché automático](https://platform.claude.com/docs/es/build-with-claude/prompt-caching#automatic-caching) omite los mensajes con alcance de turno al elegir un punto de interrupción. En la solicitud que borra un mensaje, el prefijo almacenado en caché reutilizable termina en el turno de usuario anterior a él, por lo que solo se vuelve a procesar el único turno del asistente que hay entre ese mensaje y el nuevo mensaje de usuario.
* **Las reglas de ubicación siguen aplicándose**, esté borrado o no. Un mensaje con alcance de turno debe seguir a un turno `user` (o a un turno `assistant` que termina en un resultado de herramienta de servidor) y preceder a un turno `assistant` o cerrar el array, como cualquier mensaje del sistema a mitad de la conversación. Uno que cierra el array siempre se renderiza. Uno seguido directamente por otro mensaje `user` produce un error 400, no un mensaje borrado: coloca todos los resultados de una ronda de herramientas en un solo mensaje de usuario y los recordatorios después de él.
* **Los turnos del asistente no lo borran.** Un turno del asistente prellenado o [pausado](https://platform.claude.com/docs/es/build-with-claude/handling-stop-reasons#pause-turn) después del mensaje, o un bucle de herramientas del lado del servidor, no agrega ningún mensaje de usuario, por lo que el mensaje sigue renderizándose en esa continuación. Para mantener un recordatorio visible durante un bucle de herramientas del lado del cliente, agrégalo de nuevo después de cada mensaje `tool_result`.
* **El recuento de tokens refleja lo que se renderiza.** Un mensaje borrado no agrega nada a `usage.input_tokens` ni a un [recuento de tokens](https://platform.claude.com/docs/es/build-with-claude/token-counting).
* **Historial importado.** En una transcripción que construyes en un solo paso (ejemplos few-shot, una conversación migrada), un mensaje con alcance de turno que ya tiene después un turno del asistente y un mensaje de usuario se borra desde la primera solicitud y nunca se renderiza. Ese es el estado correcto para un recordatorio por turno que estás trasladando. Deja `clear_at` sin establecer solo en los mensajes que el modelo deba ver en cada solicitud.

Los errores de validación son:

```text wrap
messages.3.clear_at: Extra inputs are not permitted
messages.3.clear_at: clear_at is only permitted on role 'system' messages
messages.3.clear_at: Input should be 'next_user_message' or 'never'
messages.3: a turn-scoped system message supports text blocks only (clear_at: 'next_user_message')
messages.3: output_config is not permitted on a turn-scoped system message (clear_at: 'next_user_message')
messages.3.content.0: cache_control is not permitted on a turn-scoped system message (clear_at: 'next_user_message')
```

El primero es el error que se devuelve sin el encabezado beta. En Google Cloud y [Claude en Amazon Bedrock](https://platform.claude.com/docs/es/build-with-claude/claude-in-amazon-bedrock), envía el valor beta en el encabezado `anthropic-beta`, como en la API de Claude (consulta [Encabezados beta](https://platform.claude.com/docs/es/api/beta-headers)). Con la [API InvokeModel](https://platform.claude.com/docs/es/build-with-claude/claude-on-amazon-bedrock-legacy) de Amazon Bedrock, colócalo en cambio en el arreglo `anthropic_beta` del cuerpo de la solicitud.

A través del SDK, establece `clear_at` (csharp, go: `ClearAt`; java: `.clearAt()`; php: `clearAt`) en la entrada `role: "system"` de `messages` y envía el encabezado beta. El siguiente ejemplo agrega un recordatorio con alcance de turno después del turno del usuario; en la siguiente solicitud, una vez que existe un mensaje del usuario posterior, el recordatorio permanece en el arreglo pero ya no se renderiza:

<CodeGroup>
  ```bash cURL
  curl https://api.anthropic.com/v1/messages \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: mid-conversation-system-clear-at-2026-08-21" \
    -H "content-type: application/json" \
    -d '{
      "model": "claude-fable-5-1",
      "max_tokens": 4096,
      "messages": [
        {"role": "user", "content": "Draft a short status update on the database migration for the team channel."},
        {"role": "system", "clear_at": "next_user_message", "content": "The reader is on call: keep this reply under 50 words."}
      ]
    }'
  ```

  ```bash CLI
  ant beta:messages create --beta mid-conversation-system-clear-at-2026-08-21 \
    --transform 'content.#(type=="text").text' --raw-output <<'YAML'
  model: claude-fable-5-1
  max_tokens: 4096
  messages:
    - role: user
      content: Draft a short status update on the database migration for the team channel.
    # Recordatorio limitado al turno: se muestra en este turno y se borra cuando existe un mensaje de usuario posterior.
    - role: system
      clear_at: next_user_message
      content: "The reader is on call: keep this reply under 50 words."
  YAML
  ```

  ```python Python
  client = anthropic.Anthropic()

  response = client.beta.messages.create(
      model="claude-fable-5-1",
      max_tokens=4096,
      messages=[
          {
              "role": "user",
              "content": "Draft a short status update on the database migration for the team channel.",
          },
          # Recordatorio limitado al turno: se muestra en este turno y se borra cuando existe un mensaje de usuario posterior.
          {
              "role": "system",
              "clear_at": "next_user_message",
              "content": "The reader is on call: keep this reply under 50 words.",
          },
      ],
      betas=["mid-conversation-system-clear-at-2026-08-21"],
  )

  for block in response.content:
      if block.type == "text":
          print(block.text)
  ```

  ```typescript TypeScript
  const client = new Anthropic();

  const response = await client.beta.messages.create({
    model: "claude-fable-5-1",
    max_tokens: 4096,
    messages: [
      {
        role: "user",
        content: "Draft a short status update on the database migration for the team channel."
      },
      // Recordatorio limitado al turno: se muestra en este turno y se borra cuando existe un mensaje de usuario posterior.
      {
        role: "system",
        clear_at: "next_user_message",
        content: "The reader is on call: keep this reply under 50 words."
      }
    ],
    betas: ["mid-conversation-system-clear-at-2026-08-21"]
  });

  for (const block of response.content) {
    if (block.type === "text") {
      console.log(block.text);
    }
  }
  ```

  ```csharp C#
  using Anthropic.Models.Beta;
  using Anthropic.Models.Beta.Messages;
  using Messages = Anthropic.Models.Messages;

  AnthropicClient client = new();

  var response = await client.Beta.Messages.Create(new MessageCreateParams
  {
      Model = Messages::Model.ClaudeFable5_1,
      MaxTokens = 4096,
      Messages =
      [
          new() { Role = Role.User, Content = "Draft a short status update on the database migration for the team channel." },
          // Recordatorio limitado al turno: se muestra en este turno y se borra cuando existe un mensaje de usuario posterior.
          new()
          {
              Role = Role.System,
              ClearAt = ClearAt.NextUserMessage,
              Content = "The reader is on call: keep this reply under 50 words.",
          },
      ],
      Betas = [AnthropicBeta.MidConversationSystemClearAt2026_08_21],
  });

  foreach (var block in response.Content)
  {
      if (block.TryPickText(out var textBlock))
      {
          Console.WriteLine(textBlock.Text);
      }
  }
  ```

  ```go Go
  client := anthropic.NewClient()

  response, err := client.Beta.Messages.New(context.Background(), anthropic.BetaMessageNewParams{
  	Model:     anthropic.ModelClaudeFable5_1,
  	MaxTokens: 4096,
  	Messages: []anthropic.BetaMessageParam{
  		anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("Draft a short status update on the database migration for the team channel.")),
  		// Recordatorio limitado al turno: se muestra en este turno y se borra cuando existe un mensaje de usuario posterior.
  		{
  			Role:    anthropic.BetaMessageParamRoleSystem,
  			ClearAt: anthropic.BetaMessageParamClearAtNextUserMessage,
  			Content: []anthropic.BetaContentBlockParamUnion{anthropic.NewBetaTextBlock("The reader is on call: keep this reply under 50 words.")},
  		},
  	},
  	Betas: []anthropic.AnthropicBeta{anthropic.AnthropicBetaMidConversationSystemClearAt2026_08_21},
  })
  if err != nil {
  	log.Fatal(err)
  }

  for _, block := range response.Content {
  	if textBlock, ok := block.AsAny().(anthropic.BetaTextBlock); ok {
  		fmt.Println(textBlock.Text)
  	}
  }
  ```

  ```java Java
  import com.anthropic.models.beta.AnthropicBeta;
  import com.anthropic.models.beta.messages.BetaMessage;
  import com.anthropic.models.beta.messages.BetaMessageParam;
  import com.anthropic.models.beta.messages.MessageCreateParams;
  import com.anthropic.models.messages.Model;

  void main() {
      AnthropicClient client = AnthropicOkHttpClient.fromEnv();

      MessageCreateParams params = MessageCreateParams.builder()
          .model(Model.CLAUDE_FABLE_5_1)
          .maxTokens(4096L)
          .addBeta(AnthropicBeta.MID_CONVERSATION_SYSTEM_CLEAR_AT_2026_08_21)
          .addUserMessage("Draft a short status update on the database migration for the team channel.")
          // Recordatorio limitado al turno: se muestra en este turno y se borra cuando existe un mensaje de usuario posterior.
          .addMessage(BetaMessageParam.builder()
              .role(BetaMessageParam.Role.SYSTEM)
              .clearAt(BetaMessageParam.ClearAt.NEXT_USER_MESSAGE)
              .content("The reader is on call: keep this reply under 50 words.")
              .build())
          .build();

      BetaMessage response = client.beta().messages().create(params);
      response.content().stream()
          .flatMap(block -> block.text().stream())
          .forEach(textBlock -> IO.println(textBlock.text()));
  }
  ```

  ```php PHP
  use Anthropic\Beta\AnthropicBeta;
  use Anthropic\Beta\Messages\BetaMessageParam;
  use Anthropic\Client;

  $client = new Client();

  $response = $client->beta->messages->create(
      model: 'claude-fable-5-1',
      maxTokens: 4096,
      messages: [
          BetaMessageParam::with(role: 'user', content: 'Draft a short status update on the database migration for the team channel.'),
          // Recordatorio limitado al turno: se muestra en este turno y se borra cuando existe un mensaje de usuario posterior.
          BetaMessageParam::with(
              role: 'system',
              clearAt: 'next_user_message',
              content: 'The reader is on call: keep this reply under 50 words.',
          ),
      ],
      betas: [AnthropicBeta::MID_CONVERSATION_SYSTEM_CLEAR_AT_2026_08_21],
  );

  foreach ($response->content as $block) {
      if ($block->type === 'text') {
          echo $block->text, PHP_EOL;
      }
  }
  ```

  ```ruby Ruby
  client = Anthropic::Client.new

  response = client.beta.messages.create(
    model: "claude-fable-5-1",
    max_tokens: 4096,
    messages: [
      {role: "user", content: "Draft a short status update on the database migration for the team channel."},
      # Recordatorio limitado al turno: se muestra en este turno y se borra cuando existe un mensaje de usuario posterior.
      {role: "system", clear_at: :next_user_message, content: "The reader is on call: keep this reply under 50 words."}
    ],
    betas: [Anthropic::AnthropicBeta::MID_CONVERSATION_SYSTEM_CLEAR_AT_2026_08_21]
  )

  response.content.each do |block|
    puts block.text if block.type == :text
  end
  ```
</CodeGroup>

## Combinación con el almacenamiento en caché de prompts

Los mensajes del sistema a mitad de conversación y el [almacenamiento en caché de prompts](https://platform.claude.com/docs/es/build-with-claude/prompt-caching) están diseñados para usarse juntos:

* **Habilita el almacenamiento en caché explícitamente.** El almacenamiento en caché solo ocurre cuando la solicitud incluye `cache_control`, ya sea el campo de [almacenamiento en caché automático](https://platform.claude.com/docs/es/build-with-claude/prompt-caching#automatic-caching) de nivel superior o un [punto de interrupción explícito](https://platform.claude.com/docs/es/build-with-claude/prompt-caching#explicit-cache-breakpoints) en un bloque de contenido. Un mensaje del sistema a mitad de conversación no crea una entrada de caché por sí mismo, y sin el almacenamiento en caché habilitado no hay ahorros que preservar.
* **Almacena en caché el prefijo estable como de costumbre.** Coloca `cache_control` en el último bloque que permanece igual entre solicitudes, ya sea el final del campo `system` de nivel superior, el final de tus definiciones de herramientas o un punto estable en el historial de mensajes.
* **Agrega el mensaje del sistema después del punto de interrupción.** Como viene después del prefijo en caché, no cambia el hash del prefijo y la caché sigue acertando.
* **Un mensaje del sistema a mitad de conversación es en sí mismo almacenable en caché.** Una vez que está en la conversación, se convierte en parte del historial estable. En el siguiente turno puedes mover tu punto de interrupción de caché más allá de él (o confiar en el [almacenamiento en caché automático](https://platform.claude.com/docs/es/build-with-claude/prompt-caching#automatic-caching) para hacerlo) y el mensaje del sistema se lee desde la caché como cualquier otro turno.

Evita editar o eliminar un mensaje del sistema a mitad de la conversación que ya se haya enviado. Como cualquier otro cambio en mensajes anteriores, eso invalida la caché a partir de ese punto. En Claude Fable 5.1, Claude Opus 5.5, Claude Sonnet 5.5 y Claude Haiku 5.5, también invalida los [bloques de pensamiento](https://platform.claude.com/docs/es/build-with-claude/thinking#preserved-in-conversation) de cada turno posterior del asistente. Para indicaciones que deben aplicarse a un solo turno, usa un [mensaje del sistema con alcance de turno](https://platform.claude.com/docs/es/build-with-claude/mid-conversation-system-messages#turn-scoped-system-messages) y déjalo en su lugar. Si la instrucción necesita evolucionar, agrega un nuevo mensaje del sistema en lugar de reescribir el anterior. Los mensajes del sistema consecutivos se aceptan y se tratan como una sola sección del sistema, que sigue la misma regla de ubicación en su conjunto.

## Limitaciones

* **No para el primer mensaje.** Un mensaje `system` que lleva contenido no puede ser la primera entrada de `messages`. Usa el campo `system` de nivel superior para instrucciones que se aplican desde el principio.
* **La ubicación está restringida.** Un mensaje `system` que lleva contenido (bloques `text`, `tool_addition` o `tool_removal`) debe seguir inmediatamente a un turno `user` (incluido un turno `user` que lleva bloques `tool_result`) o a un turno `assistant` que termine en un resultado de herramienta de servidor, y debe preceder a un turno `assistant` o terminar el arreglo. No puede ubicarse entre un bloque `tool_use` y su `tool_result`. Colocarlo en otro lugar devuelve un error 400. Una excepción: los bloques `tool_addition` y `tool_removal` no se aceptan inmediatamente después de un turno `assistant` [pausado](https://platform.claude.com/docs/es/build-with-claude/handling-stop-reasons#pause-turn) (uno que termina en un resultado de herramienta de servidor), aunque los bloques `text` sí; primero reanuda el turno pausado y luego envía el cambio de herramientas en el siguiente mensaje `system`. Un mensaje con `content` vacío que solo establece [`output_config.effort`](https://platform.claude.com/docs/es/build-with-claude/effort#change-effort-mid-conversation-beta) no renderiza nada en su posición y se acepta en cualquier lugar de `messages`, incluso como primera entrada o entre un turno `assistant` y un turno `user`. Los mensajes `system` consecutivos se evalúan en conjunto, así que agregar un mensaje con texto junto a uno que solo establece el esfuerzo hace que todo el grupo siga la regla de contenido.
* **Los mensajes con alcance de turno son solo de texto y se reenvían textualmente.** Un mensaje `clear_at: "next_user_message"` no lleva `tool_addition`, `tool_removal`, `output_config` ni `cache_control`, y una vez borrado debe permanecer en `messages` byte por byte en las solicitudes posteriores. Consulta [Mensajes del sistema con alcance de turno](https://platform.claude.com/docs/es/build-with-claude/mid-conversation-system-messages#turn-scoped-system-messages).
* **No es un lugar para contenido no confiable.** Claude trata el contenido del sistema como instrucciones del operador y las sigue. No coloques directamente en un mensaje del sistema texto proveniente de fuera de la conversación, como resultados sin procesar de herramientas, documentos recuperados o contenido web; hacerlo le otorga a ese texto autoridad de nivel de operador. Mantén esos datos en bloques `tool_result` y sigue aplicando [Mitigar jailbreaks e inyecciones de prompts](https://platform.claude.com/docs/es/test-and-evaluate/strengthen-guardrails/mitigate-jailbreaks).

## Relacionado

<CardGroup cols={2}>
  <Card title="Almacenamiento en caché de prompts" icon="bolt" href="https://platform.claude.com/docs/es/build-with-claude/prompt-caching">
    Cómo funciona el almacenamiento en caché, dónde colocar los puntos de interrupción y cómo leer los campos de uso de caché.
  </Card>

  <Card title="Diagnóstico de caché" icon="magnifying-glass" href="https://platform.claude.com/docs/es/build-with-claude/cache-diagnostics">
    Descubre exactamente dónde divergieron dos solicitudes cuando un acierto de caché que esperabas no ocurre.
  </Card>

  <Card title="Uso de la Messages API" icon="message" href="https://platform.claude.com/docs/es/build-with-claude/working-with-messages">
    Estructura de mensajes, conversaciones de múltiples turnos y el campo `system`.
  </Card>

  <Card title="Mejores prácticas de prompting" icon="text" href="https://platform.claude.com/docs/es/build-with-claude/prompt-engineering/claude-prompting-best-practices">
    Cómo escribir prompts e instrucciones del sistema efectivos.
  </Card>

  <Card title="Uso de herramientas con Claude" icon="wrench" href="https://platform.claude.com/docs/es/agents-and-tools/tool-use/overview">
    Cómo se estructuran los bloques `tool_use` y `tool_result` en el arreglo `messages`.
  </Card>
</CardGroup>
