---
title: Herramientas de servidor
url: https://platform.claude.com/docs/es/agents-and-tools/tool-use/server-tools
description: "Trabaja con herramientas ejecutadas por Anthropic: bloques server_tool_use, continuación con pause_turn, turnos que mezclan herramientas de servidor y de cliente, y filtrado de dominios."
---

Las herramientas ejecutadas en el servidor comparten estas mecánicas: el bloque `server_tool_use`, la continuación con `pause_turn`, los turnos que mezclan herramientas de servidor y de cliente, la elegibilidad para "Zero Data Retention" (retención cero de datos), o ZDR, y el filtrado de dominios. Para herramientas individuales, consulta la [referencia de herramientas](https://platform.claude.com/docs/es/agents-and-tools/tool-use/tool-reference).

## El bloque server\_tool\_use

El bloque `server_tool_use` aparece en la respuesta de Claude cuando se ejecuta una herramienta ejecutada en el servidor. Su campo `id` usa el prefijo `srvtoolu_` para distinguirlo de las llamadas a herramientas de cliente:

```json
{
  "type": "server_tool_use",
  "id": "srvtoolu_01A2B3C4D5E6F7G8H9",
  "name": "web_search",
  "input": { "query": "latest quantum computing breakthroughs" }
}
```

La API ejecuta la herramienta internamente. Ves la llamada y su resultado en la respuesta, pero no te encargas de la ejecución. A diferencia de los bloques `tool_use` de cliente, no necesitas responder con un `tool_result`. El bloque de resultado de la herramienta (por ejemplo, `web_search_tool_result` para la búsqueda web) sigue al bloque `server_tool_use` en el mismo turno del asistente, emparejado por `tool_use_id`. Si Claude llama a una de tus herramientas de cliente al mismo tiempo, el bloque `server_tool_use` aparece sin su resultado, y la respuesta termina con `stop_reason: "tool_use"`. La API ejecuta la herramienta cuando devuelves los bloques `tool_result` de cliente en tu siguiente solicitud.

## El bucle del lado del servidor y pause\_turn

Al usar herramientas de servidor como la búsqueda web, la API ejecuta las llamadas a herramientas en un bucle agéntico del lado del servidor. En un turno de larga duración, la API podría pausar ese bucle y devolver un motivo de detención `pause_turn`.

Así es como se maneja el motivo de detención `pause_turn`:

<CodeGroup>
  ```bash cURL
  # Solicitud inicial. Si "stop_reason" en la respuesta es "pause_turn", continúa
  # el turno reenviando la solicitud con el contenido del asistente añadido a messages.
  curl https://api.anthropic.com/v1/messages \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "claude-opus-5-5",
      "max_tokens": 1024,
      "messages": [
        {
          "role": "user",
          "content": "Search for comprehensive information about quantum computing breakthroughs in 2025"
        }
      ],
      "tools": [{"type": "web_search_20250305", "name": "web_search", "max_uses": 10}]
    }'
  ```

  ```bash CLI
  # Solicitud inicial. Si "stop_reason" en la salida es "pause_turn", vuelve a ejecutar con
  # el contenido del asistente añadido a messages (consulta las pestañas del SDK).
  ant messages create <<'YAML'
  model: claude-opus-5-5
  max_tokens: 1024
  tools:
    - {type: web_search_20250305, name: web_search, max_uses: 10}
  messages:
    - {role: user, content: "Search for comprehensive information about quantum computing breakthroughs in 2025"}
  YAML
  ```

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

  # Solicitud inicial con búsqueda web
  response = client.messages.create(
      model="claude-opus-5-5",
      max_tokens=1024,
      messages=[
          {
              "role": "user",
              "content": "Search for comprehensive information about quantum computing breakthroughs in 2025",
          }
      ],
      tools=[{"type": "web_search_20250305", "name": "web_search", "max_uses": 10}],
  )

  # Verifica si la respuesta tiene el stop reason pause_turn
  if response.stop_reason == "pause_turn":
      # Continúa la conversación con el contenido pausado
      messages = [
          {
              "role": "user",
              "content": "Search for comprehensive information about quantum computing breakthroughs in 2025",
          },
          {"role": "assistant", "content": response.content},
      ]

      # Envía la solicitud de continuación
      continuation = client.messages.create(
          model="claude-opus-5-5",
          max_tokens=1024,
          messages=messages,
          tools=[{"type": "web_search_20250305", "name": "web_search", "max_uses": 10}],
      )

      print(continuation)
  else:
      print(response)
  ```

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

  // Solicitud inicial con búsqueda web
  const response = await client.messages.create({
    model: "claude-opus-5-5",
    max_tokens: 1024,
    messages: [
      {
        role: "user",
        content:
          "Search for comprehensive information about quantum computing breakthroughs in 2025"
      }
    ],
    tools: [
      {
        type: "web_search_20250305",
        name: "web_search",
        max_uses: 10
      }
    ]
  });

  // Comprueba si la respuesta tiene el stop reason pause_turn
  if (response.stop_reason === "pause_turn") {
    // Continúa la conversación con el contenido pausado
    const messages: Anthropic.MessageParam[] = [
      {
        role: "user",
        content:
          "Search for comprehensive information about quantum computing breakthroughs in 2025"
      },
      { role: "assistant", content: response.content }
    ];

    // Envía la solicitud de continuación
    const continuation = await client.messages.create({
      model: "claude-opus-5-5",
      max_tokens: 1024,
      messages,
      tools: [
        {
          type: "web_search_20250305",
          name: "web_search",
          max_uses: 10
        }
      ]
    });

    console.log(continuation);
  } else {
    console.log(response);
  }
  ```

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

  var parameters = new MessageCreateParams
  {
      Model = Model.ClaudeOpus5_5,
      MaxTokens = 1024,
      Messages = [
          new() {
              Role = Role.User,
              Content = "Search for comprehensive information about quantum computing breakthroughs in 2025"
          }
      ],
      Tools = [new ToolUnion(new WebSearchTool20250305 { MaxUses = 10 })]
  };

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

  if (response.StopReason?.Value() == StopReason.PauseTurn)
  {
      // Continúa la conversación con el contenido pausado
      var continuationParams = new MessageCreateParams
      {
          Model = Model.ClaudeOpus5_5,
          MaxTokens = 1024,
          Messages = [
              new() {
                  Role = Role.User,
                  Content = "Search for comprehensive information about quantum computing breakthroughs in 2025"
              },
              new() {
                  Role = Role.Assistant,
                  Content = response.Content.Select(block => new ContentBlockParam(block.Json)).ToList()
              }
          ],
          Tools = [new ToolUnion(new WebSearchTool20250305 { MaxUses = 10 })]
      };

      var continuation = await client.Messages.Create(continuationParams);
      Console.WriteLine(continuation);
  }
  else
  {
      Console.WriteLine(response);
  }
  ```

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

  webSearchTool := []anthropic.ToolUnionParam{
  	{OfWebSearchTool20250305: &anthropic.WebSearchTool20250305Param{
  		MaxUses: anthropic.Int(10),
  	}},
  }

  response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
  	Model:     anthropic.ModelClaudeOpus5_5,
  	MaxTokens: 1024,
  	Messages: []anthropic.MessageParam{
  		anthropic.NewUserMessage(anthropic.NewTextBlock("Search for comprehensive information about quantum computing breakthroughs in 2025")),
  	},
  	Tools: webSearchTool,
  })
  if err != nil {
  	log.Fatal(err)
  }

  if response.StopReason == anthropic.StopReasonPauseTurn {
  	// Devuelve la respuesta pausada tal cual para que Claude pueda continuar el turno
  	continuation, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
  		Model:     anthropic.ModelClaudeOpus5_5,
  		MaxTokens: 1024,
  		Messages: []anthropic.MessageParam{
  			anthropic.NewUserMessage(anthropic.NewTextBlock("Search for comprehensive information about quantum computing breakthroughs in 2025")),
  			response.ToParam(),
  		},
  		Tools: webSearchTool,
  	})
  	if err != nil {
  		log.Fatal(err)
  	}
  	fmt.Println(continuation)
  } else {
  	fmt.Println(response)
  }
  ```

  ```java Java
  import com.anthropic.models.messages.StopReason;
  import com.anthropic.models.messages.WebSearchTool20250305;

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

      MessageCreateParams params = MessageCreateParams.builder()
          .model(Model.CLAUDE_OPUS_5_5)
          .maxTokens(1024L)
          .addUserMessage("Search for comprehensive information about quantum computing breakthroughs in 2025")
          .addTool(WebSearchTool20250305.builder()
              .maxUses(10L)
              .build())
          .build();

      Message response = client.messages().create(params);

      if (response.stopReason().isPresent()
              && response.stopReason().get().equals(StopReason.PAUSE_TURN)) {
          MessageCreateParams continuationParams = MessageCreateParams.builder()
              .model(Model.CLAUDE_OPUS_5_5)
              .maxTokens(1024L)
              .addUserMessage("Search for comprehensive information about quantum computing breakthroughs in 2025")
              .addMessage(response)
              .addTool(WebSearchTool20250305.builder()
                  .maxUses(10L)
                  .build())
              .build();

          Message continuation = client.messages().create(continuationParams);
          IO.println(continuation);
      } else {
          IO.println(response);
      }
  }
  ```

  ```php PHP
  $client = new Client();

  $response = $client->messages->create(
      maxTokens: 1024,
      messages: [
          [
              'role' => 'user',
              'content' => 'Search for comprehensive information about quantum computing breakthroughs in 2025'
          ]
      ],
      model: 'claude-opus-5-5',
      tools: [
          [
              'type' => 'web_search_20250305',
              'name' => 'web_search',
              'max_uses' => 10
          ]
      ],
  );

  if ($response->stopReason === 'pause_turn') {
      $messages = [
          [
              'role' => 'user',
              'content' => 'Search for comprehensive information about quantum computing breakthroughs in 2025'
          ],
          [
              'role' => 'assistant',
              'content' => $response->content
          ]
      ];

      $continuation = $client->messages->create(
          maxTokens: 1024,
          messages: $messages,
          model: 'claude-opus-5-5',
          tools: [
              [
                  'type' => 'web_search_20250305',
                  'name' => 'web_search',
                  'max_uses' => 10
              ]
          ],
      );

      echo $continuation;
  } else {
      echo $response;
  }
  ```

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

  response = client.messages.create(
    model: "claude-opus-5-5",
    max_tokens: 1024,
    messages: [
      {
        role: "user",
        content:
          "Search for comprehensive information about quantum computing breakthroughs in 2025"
      }
    ],
    tools: [
      {
        type: "web_search_20250305",
        name: "web_search",
        max_uses: 10
      }
    ]
  )

  if response.stop_reason == :pause_turn
    messages = [
      {
        role: "user",
        content: "Search for comprehensive information about quantum computing breakthroughs in 2025"
      },
      {
        role: "assistant",
        content: response.content
      }
    ]

    continuation = client.messages.create(
      model: "claude-opus-5-5",
      max_tokens: 1024,
      messages: messages,
      tools: [
        {
          type: "web_search_20250305",
          name: "web_search",
          max_uses: 10
        }
      ]
    )

    puts continuation
  else
    puts response
  end
  ```
</CodeGroup>

Al manejar `pause_turn`:

* **Continúa la conversación:** Devuelve la respuesta pausada tal cual en una solicitud posterior para permitir que Claude continúe su turno.
* **Preserva el estado de las herramientas:** Incluye las mismas herramientas en la solicitud de continuación. Un turno pausado puede terminar con un bloque `server_tool_use` cuya herramienta aún no se ha ejecutado, y la API devuelve un error de validación si esa herramienta falta en la continuación.
* **Repite según sea necesario:** Un turno continuado puede pausarse de nuevo. Verifica `stop_reason` en cada respuesta y continúa hasta que obtengas un motivo de detención diferente, limitando el número de continuaciones como lo harías con cualquier bucle de reintentos.

Para los demás valores de `stop_reason` y los patrones generales de manejo, consulta [Motivos de detención y alternativas](https://platform.claude.com/docs/es/build-with-claude/handling-stop-reasons).

## Mezclar herramientas de servidor y herramientas de cliente en un turno

Claude puede llamar a una herramienta de servidor y a una herramienta de cliente en el mismo grupo de llamadas a herramientas en paralelo, por ejemplo, `web_fetch` junto con una herramienta definida por el usuario. Una herramienta de cliente es cualquier herramienta que tu código ejecuta y que produce un bloque `tool_use`, ya sea definida por el usuario o una herramienta de cliente con esquema de Anthropic como la [herramienta Bash](https://platform.claude.com/docs/es/agents-and-tools/tool-use/bash-tool). Cuando eso sucede, la API no ejecuta la herramienta de servidor. Devuelve la respuesta de inmediato para que puedas ejecutar primero la herramienta de cliente:

* `stop_reason` es `"tool_use"`, no `"pause_turn"`.
* `content` contiene el bloque `server_tool_use` y el bloque `tool_use` de cliente, pero ningún bloque de resultado para la herramienta de servidor: esa llamada no ha terminado.
* No hay ningún otro marcador. Detecta el estado buscando un bloque `server_tool_use` cuyo `id` no tenga un bloque de resultado correspondiente en la respuesta. Un bloque `mcp_tool_use` del [conector MCP](https://platform.claude.com/docs/es/agents-and-tools/mcp-connector) se comporta de la misma manera. Las llamadas a herramientas de servidor que ya tienen su bloque de resultado en la misma respuesta están completas y no necesitan nada de tu parte.

<Note>
  Con la [llamada programática de herramientas](https://platform.claude.com/docs/es/agents-and-tools/tool-use/programmatic-tool-calling), la misma forma de respuesta significa algo diferente. El bloque `tool_use` de cliente proviene de código que se está ejecutando en la herramienta `code_execution` en lugar de provenir directamente de Claude, y su campo `caller` nombra el bloque `code_execution` que lo llamó. Ese código ya ha comenzado: está pausado esperando tus bloques `tool_result`, y enviarlos reanuda la ejecución en lugar de iniciar una herramienta diferida. El propio bloque de resultado del bloque `code_execution` llega una vez que el código termina, lo que puede tomar más de una ronda de resultados de herramientas. El mensaje de usuario de seguimiento en sí es el mismo en ambos casos; con la llamada programática de herramientas, devuelve también el `id` del campo `container` de la respuesta, como muestra esa página.
</Note>

```json
{
  "stop_reason": "tool_use",
  "content": [
    {
      "type": "text",
      "text": "I'll fetch the article and check your system at the same time."
    },
    {
      "type": "server_tool_use",
      "id": "srvtoolu_01HxbWnMRmbWyMfUtJKC45rA",
      "name": "web_fetch",
      "input": { "url": "https://example.com/article" }
    },
    {
      "type": "tool_use",
      "id": "toolu_01PjgRJLbXrXEMZwDNYLnBqk",
      "name": "run_command",
      "input": { "command": "uname -a" }
    }
  ]
}
```

Para continuar el turno, ejecuta las herramientas de cliente y envía un mensaje de usuario cuyo contenido sea únicamente los bloques `tool_result`, uno por cada bloque `tool_use` de esa respuesta. Mantén el mismo arreglo `tools`: una solicitud de reanudación que ya no define la herramienta de servidor en espera falla con un 400 cuyo mensaje termina en ``but no `web_fetch` tool was provided``.

```json
{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_01PjgRJLbXrXEMZwDNYLnBqk",
      "content": "Linux demo-host 6.8.0-52-generic x86_64 GNU/Linux"
    }
  ]
}
```

La API adjunta tus resultados al turno del asistente aún abierto, ejecuta la herramienta de servidor diferida (en el caso de una ejecución de código pausada, la reanuda) y luego permite que Claude continúe. Para una herramienta de servidor que Claude llamó directamente, la siguiente respuesta comienza con el bloque de resultado que responde al `id` del `server_tool_use` de la respuesta anterior, seguido del contenido recién generado y un nuevo `stop_reason`:

```json
{
  "stop_reason": "end_turn",
  "content": [
    {
      "type": "web_fetch_tool_result",
      "tool_use_id": "srvtoolu_01HxbWnMRmbWyMfUtJKC45rA",
      "content": {
        "type": "web_fetch_result",
        "url": "https://example.com/article",
        "content": {
          "type": "document",
          "source": {
            "type": "text",
            "media_type": "text/plain",
            "data": "Full text content of the article..."
          }
        }
      }
    },
    {
      "type": "text",
      "text": "The article argues that... and your machine is running Linux..."
    }
  ]
}
```

Un bloque `server_tool_use` y su bloque de resultado se emparejan por `tool_use_id`, no por posición: en este flujo llegan en dos respuestas diferentes, y el bloque `server_tool_use` no se repite en la segunda. En solicitudes posteriores, mantén todo el intercambio en tu arreglo `messages` en orden: la primera respuesta como un mensaje `assistant`, el mensaje de usuario con los `tool_result`, y luego la siguiente respuesta como otro mensaje `assistant`, de la misma manera en que acumulas cualquier otro intercambio de uso de herramientas.

<Warning>
  El mensaje de usuario de seguimiento no debe contener nada excepto bloques `tool_result`. Un bloque agregado después de los resultados, como texto, le indica a la API que el turno del asistente ha terminado. Para una herramienta de servidor que Claude llamó directamente, eso deja el turno con una llamada a herramienta de servidor sin resolver, y la solicitud falla con un 400 `invalid_request_error`:

  ```text wrap
  `web_fetch` tool use with id `srvtoolu_01HxbWnMRmbWyMfUtJKC45rA` was found without a corresponding `web_fetch_tool_result` block
  ```

  Un seguimiento que coloca contenido antes de los resultados, responde solo a algunos de los IDs de `tool_use` de cliente, o no contiene ningún bloque `tool_result` falla antes, con el error de herramienta de cliente descrito en [Manejar llamadas a herramientas](https://platform.claude.com/docs/es/agents-and-tools/tool-use/handle-tool-calls):

  ```text wrap
  `tool_use` ids were found without `tool_result` blocks immediately after: toolu_01PjgRJLbXrXEMZwDNYLnBqk. Each `tool_use` block must have a corresponding `tool_result` block in the next message.
  ```

  Para darle más información a Claude, envíala como un mensaje de usuario separado después de que el turno se complete.
</Warning>

**En qué se diferencia esto de `pause_turn`:** Una [respuesta `pause_turn`](https://platform.claude.com/docs/es/agents-and-tools/tool-use/server-tools#the-server-side-loop-and-pause-turn) también puede terminar con un bloque `server_tool_use` que no se ha ejecutado, pero nunca deja un bloque `tool_use` de cliente esperando por ti, así que la continúas reenviando el contenido del asistente tal cual. Una respuesta que deja un bloque `tool_use` de cliente esperando por ti nunca tiene un `stop_reason` de `pause_turn`: cuando Claude se detiene para llamar a tus herramientas, `stop_reason` es `tool_use`, y la continúas enviando los bloques `tool_result` de cliente en lugar de reenviar la respuesta. En ambos casos la API ejecuta la herramienta de servidor pendiente al inicio de la siguiente solicitud.

El siguiente ejemplo habilita web fetch junto con una herramienta `run_command` definida por el usuario y maneja la respuesta mixta:

<CodeGroup>
  ```bash cURL
  # Si "stop_reason" es "tool_use" y un bloque server_tool_use no tiene un bloque
  # de resultado correspondiente, esa llamada no ha terminado. Ejecuta las herramientas del cliente y luego haz POST
  # de nuevo con un mensaje de usuario más que contenga solo sus bloques tool_result
  # y el mismo array de tools (consulta las pestañas del SDK).
  curl https://api.anthropic.com/v1/messages \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "claude-opus-5-5",
      "max_tokens": 1024,
      "messages": [
        {
          "role": "user",
          "content": "Summarize https://example.com/article and run uname -a to tell me what system this is on."
        }
      ],
      "tools": [
        {"type": "web_fetch_20250910", "name": "web_fetch", "max_uses": 5},
        {
          "name": "run_command",
          "description": "Run a shell command on this computer and return its output.",
          "input_schema": {
            "type": "object",
            "properties": {"command": {"type": "string", "description": "The command to run"}},
            "required": ["command"]
          }
        }
      ]
    }'
  ```

  ```bash CLI
  # Si "stop_reason" es "tool_use" y un bloque server_tool_use no tiene un bloque
  # de resultado correspondiente, ejecuta las herramientas del cliente y vuelve a ejecutar con un mensaje de usuario
  # que solo contenga sus bloques tool_result añadidos (consulta las pestañas del SDK).
  ant messages create <<'YAML'
  model: claude-opus-5-5
  max_tokens: 1024
  messages:
    - role: user
      content: "Summarize https://example.com/article and run uname -a to tell me what system this is on."
  tools:
    - {type: web_fetch_20250910, name: web_fetch, max_uses: 5}
    - name: run_command
      description: Run a shell command on this computer and return its output.
      input_schema:
        type: object
        properties:
          command: {type: string, description: The command to run}
        required: [command]
  YAML
  ```

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

  tools = [
      {"type": "web_fetch_20250910", "name": "web_fetch", "max_uses": 5},
      {
          "name": "run_command",
          "description": "Run a shell command on this computer and return its output.",
          "input_schema": {
              "type": "object",
              "properties": {
                  "command": {"type": "string", "description": "The command to run"}
              },
              "required": ["command"],
          },
      },
  ]
  messages = [
      {
          "role": "user",
          "content": "Summarize https://example.com/article and run uname -a to tell me what system this is on.",
      }
  ]

  response = client.messages.create(
      model="claude-opus-5-5", max_tokens=1024, tools=tools, messages=messages
  )

  tool_results = [
      {
          "type": "tool_result",
          "tool_use_id": block.id,
          # Ejecuta tu herramienta aquí. Este ejemplo devuelve una cadena fija.
          "content": "Linux demo-host 6.8.0-52-generic x86_64 GNU/Linux",
      }
      for block in response.content
      if block.type == "tool_use"
  ]

  if response.stop_reason == "tool_use" and tool_results:
      # Un bloque server_tool_use sin bloque de resultado en esta respuesta no ha terminado; su resultado llega en una respuesta posterior.
      # Envía de vuelta solo los bloques tool_result del cliente, con las mismas herramientas.
      continuation = client.messages.create(
          model="claude-opus-5-5",
          max_tokens=1024,
          tools=tools,
          messages=[
              *messages,
              {"role": "assistant", "content": response.content},
              {"role": "user", "content": tool_results},
          ],
      )
      # Si un web_fetch fue diferido, se ejecuta en esta solicitud y su
      # web_fetch_tool_result es el primer bloque de continuation.content.
      print(continuation)
  else:
      print(response)
  ```

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

  const webFetchTool = {
    type: "web_fetch_20250910",
    name: "web_fetch",
    max_uses: 5
  } as const;
  const runCommandTool: Anthropic.Tool = {
    name: "run_command",
    description: "Run a shell command on this computer and return its output.",
    input_schema: {
      type: "object" as const,
      properties: {
        command: { type: "string", description: "The command to run" }
      },
      required: ["command"]
    }
  };
  const messages: Anthropic.MessageParam[] = [
    {
      role: "user",
      content:
        "Summarize https://example.com/article and run uname -a to tell me what system this is on."
    }
  ];

  const response = await client.messages.create({
    model: "claude-opus-5-5",
    max_tokens: 1024,
    tools: [webFetchTool, runCommandTool],
    messages
  });

  const toolResults: Anthropic.ToolResultBlockParam[] = [];
  for (const block of response.content) {
    if (block.type === "tool_use") {
      toolResults.push({
        type: "tool_result",
        tool_use_id: block.id,
        // Ejecuta tu herramienta aquí. Este ejemplo devuelve una cadena fija.
        content: "Linux demo-host 6.8.0-52-generic x86_64 GNU/Linux"
      });
    }
  }

  if (response.stop_reason === "tool_use" && toolResults.length > 0) {
    // Un bloque server_tool_use sin bloque de resultado en esta respuesta no ha terminado; su resultado llega en una respuesta posterior.
    // Envía de vuelta solo los bloques tool_result del cliente, con las mismas herramientas.
    const continuation = await client.messages.create({
      model: "claude-opus-5-5",
      max_tokens: 1024,
      tools: [webFetchTool, runCommandTool],
      messages: [
        ...messages,
        { role: "assistant", content: response.content },
        { role: "user", content: toolResults }
      ]
    });
    // Si un web_fetch fue diferido, se ejecuta en esta solicitud y su
    // web_fetch_tool_result es el primer bloque de continuation.content.
    console.log(continuation);
  } else {
    console.log(response);
  }
  ```

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

  List<ToolUnion> tools =
  [
      new ToolUnion(new WebFetchTool20250910() { MaxUses = 5 }),
      new ToolUnion(new Tool()
      {
          Name = "run_command",
          Description = "Run a shell command on this computer and return its output.",
          InputSchema = new InputSchema()
          {
              Properties = new Dictionary<string, JsonElement>
              {
                  ["command"] = JsonSerializer.SerializeToElement(
                      new { type = "string", description = "The command to run" }
                  ),
              },
              Required = ["command"],
          },
      }),
  ];
  MessageParam userMessage = new()
  {
      Role = Role.User,
      Content = "Summarize https://example.com/article and run uname -a to tell me what system this is on."
  };

  var response = await client.Messages.Create(new MessageCreateParams
  {
      Model = Model.ClaudeOpus5_5,
      MaxTokens = 1024,
      Tools = tools,
      Messages = [userMessage]
  });

  var toolResults = new List<ContentBlockParam>();
  foreach (var block in response.Content)
  {
      if (block.TryPickToolUse(out var toolUse))
      {
          toolResults.Add(new ContentBlockParam(new ToolResultBlockParam()
          {
              ToolUseID = toolUse.ID,
              // Ejecuta tu herramienta aquí. Este ejemplo devuelve una cadena fija.
              Content = "Linux demo-host 6.8.0-52-generic x86_64 GNU/Linux",
          }));
      }
  }

  if (response.StopReason?.Value() == StopReason.ToolUse && toolResults.Count > 0)
  {
      // Un bloque server_tool_use sin bloque de resultado en esta respuesta no ha terminado; su resultado llega en una respuesta posterior.
      // Envía de vuelta solo los bloques tool_result del cliente, con las mismas herramientas.
      var continuation = await client.Messages.Create(new MessageCreateParams
      {
          Model = Model.ClaudeOpus5_5,
          MaxTokens = 1024,
          Tools = tools,
          Messages =
          [
              userMessage,
              new()
              {
                  Role = Role.Assistant,
                  Content = response.Content.Select(block => new ContentBlockParam(block.Json)).ToList()
              },
              new() { Role = Role.User, Content = new MessageParamContent(toolResults) }
          ]
      });
      // Si un web_fetch fue diferido, se ejecuta en esta solicitud y su
      // web_fetch_tool_result es el primer bloque de continuation.Content.
      Console.WriteLine(continuation);
  }
  else
  {
      Console.WriteLine(response);
  }
  ```

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

  tools := []anthropic.ToolUnionParam{
  	{OfWebFetchTool20250910: &anthropic.WebFetchTool20250910Param{
  		MaxUses: anthropic.Int(5),
  	}},
  	{OfTool: &anthropic.ToolParam{
  		Name:        "run_command",
  		Description: anthropic.String("Run a shell command on this computer and return its output."),
  		InputSchema: anthropic.ToolInputSchemaParam{
  			Properties: map[string]any{
  				"command": map[string]any{
  					"type":        "string",
  					"description": "The command to run",
  				},
  			},
  			Required: []string{"command"},
  		},
  	}},
  }
  userMessage := anthropic.NewUserMessage(anthropic.NewTextBlock("Summarize https://example.com/article and run uname -a to tell me what system this is on."))

  response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
  	Model:     anthropic.ModelClaudeOpus5_5,
  	MaxTokens: 1024,
  	Tools:     tools,
  	Messages:  []anthropic.MessageParam{userMessage},
  })
  if err != nil {
  	log.Fatal(err)
  }

  var toolResults []anthropic.ContentBlockParamUnion
  for _, block := range response.Content {
  	if toolUse, ok := block.AsAny().(anthropic.ToolUseBlock); ok {
  		// Ejecuta tu herramienta aquí. Este ejemplo devuelve una cadena fija.
  		output := "Linux demo-host 6.8.0-52-generic x86_64 GNU/Linux"
  		toolResults = append(toolResults, anthropic.NewToolResultBlock(toolUse.ID, output, false))
  	}
  }

  if response.StopReason == anthropic.StopReasonToolUse && len(toolResults) > 0 {
  	// Un bloque server_tool_use sin bloque de resultado en esta respuesta no ha terminado; su resultado llega en una respuesta posterior.
  	// Envía de vuelta solo los bloques tool_result del cliente, con las mismas herramientas.
  	continuation, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
  		Model:     anthropic.ModelClaudeOpus5_5,
  		MaxTokens: 1024,
  		Tools:     tools,
  		Messages: []anthropic.MessageParam{
  			userMessage,
  			response.ToParam(),
  			anthropic.NewUserMessage(toolResults...),
  		},
  	})
  	if err != nil {
  		log.Fatal(err)
  	}
  	// Si un web_fetch fue diferido, se ejecuta en esta solicitud y su
  	// web_fetch_tool_result es el primer bloque de continuation.Content.
  	fmt.Println(continuation)
  } else {
  	fmt.Println(response)
  }
  ```

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

      Tool runCommandTool = Tool.builder()
          .name("run_command")
          .description("Run a shell command on this computer and return its output.")
          .inputSchema(Tool.InputSchema.builder()
              .properties(JsonValue.from(Map.of(
                  "command", Map.of("type", "string", "description", "The command to run")
              )))
              .putAdditionalProperty("required", JsonValue.from(List.of("command")))
              .build())
          .build();
      String prompt = "Summarize https://example.com/article and run uname -a to tell me what system this is on.";

      Message response = client.messages().create(MessageCreateParams.builder()
          .model(Model.CLAUDE_OPUS_5_5)
          .maxTokens(1024L)
          .addTool(WebFetchTool20250910.builder().maxUses(5L).build())
          .addTool(runCommandTool)
          .addUserMessage(prompt)
          .build());

      List<ContentBlockParam> toolResults = new ArrayList<>();
      for (ContentBlock block : response.content()) {
          block.toolUse().ifPresent(toolUse -> toolResults.add(ContentBlockParam.ofToolResult(
              ToolResultBlockParam.builder()
                  .toolUseId(toolUse.id())
                  // Ejecuta tu herramienta aquí. Este ejemplo devuelve una cadena fija.
                  .content("Linux demo-host 6.8.0-52-generic x86_64 GNU/Linux")
                  .build()
          )));
      }

      boolean isToolUse = response.stopReason()
          .map(StopReason.TOOL_USE::equals)
          .orElse(false);
      if (isToolUse && !toolResults.isEmpty()) {
          // Un bloque server_tool_use sin bloque de resultado en esta respuesta no ha terminado; su resultado llega en una respuesta posterior.
          // Envía de vuelta solo los bloques tool_result del cliente, con las mismas herramientas.
          Message continuation = client.messages().create(MessageCreateParams.builder()
              .model(Model.CLAUDE_OPUS_5_5)
              .maxTokens(1024L)
              .addTool(WebFetchTool20250910.builder().maxUses(5L).build())
              .addTool(runCommandTool)
              .addUserMessage(prompt)
              .addMessage(response)
              .addUserMessageOfBlockParams(toolResults)
              .build());
          // Si un web_fetch fue diferido, se ejecuta en esta solicitud y su
          // web_fetch_tool_result es el primer bloque de continuation.content().
          IO.println(continuation);
      } else {
          IO.println(response);
      }
  }
  ```

  ```php PHP
  $client = new Client();

  $tools = [
      ['type' => 'web_fetch_20250910', 'name' => 'web_fetch', 'max_uses' => 5],
      [
          'name' => 'run_command',
          'description' => 'Run a shell command on this computer and return its output.',
          'input_schema' => [
              'type' => 'object',
              'properties' => [
                  'command' => ['type' => 'string', 'description' => 'The command to run']
              ],
              'required' => ['command']
          ]
      ]
  ];
  $userMessage = ['role' => 'user', 'content' => 'Summarize https://example.com/article and run uname -a to tell me what system this is on.'];

  $response = $client->messages->create(
      maxTokens: 1024,
      messages: [$userMessage],
      model: 'claude-opus-5-5',
      tools: $tools,
  );

  $toolResults = [];
  foreach ($response->content as $block) {
      if ($block->type === 'tool_use') {
          $toolResults[] = [
              'type' => 'tool_result',
              'tool_use_id' => $block->id,
              // Ejecuta tu herramienta aquí. Este ejemplo devuelve una cadena fija.
              'content' => 'Linux demo-host 6.8.0-52-generic x86_64 GNU/Linux'
          ];
      }
  }

  if ($response->stopReason === 'tool_use' && count($toolResults) > 0) {
      // Un bloque server_tool_use sin bloque de resultado en esta respuesta no ha terminado; su resultado llega en una respuesta posterior.
      // Envía de vuelta solo los bloques tool_result del cliente, con las mismas herramientas.
      $continuation = $client->messages->create(
          maxTokens: 1024,
          messages: [
              $userMessage,
              ['role' => 'assistant', 'content' => $response->content],
              ['role' => 'user', 'content' => $toolResults],
          ],
          model: 'claude-opus-5-5',
          tools: $tools,
      );
      // Si un web_fetch fue diferido, se ejecuta en esta solicitud y su
      // web_fetch_tool_result es el primer bloque de $continuation->content.
      echo $continuation;
  } else {
      echo $response;
  }
  ```

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

  tools = [
    { type: "web_fetch_20250910", name: "web_fetch", max_uses: 5 },
    {
      name: "run_command",
      description: "Run a shell command on this computer and return its output.",
      input_schema: {
        type: "object",
        properties: {
          command: { type: "string", description: "The command to run" }
        },
        required: ["command"]
      }
    }
  ]
  user_message = {
    role: "user",
    content: "Summarize https://example.com/article and run uname -a to tell me what system this is on."
  }

  response = client.messages.create(
    model: "claude-opus-5-5",
    max_tokens: 1024,
    tools: tools,
    messages: [user_message]
  )

  tool_results = []
  response.content.each do |block|
    next unless block.type == :tool_use

    tool_results << {
      type: "tool_result",
      tool_use_id: block.id,
      # Ejecuta tu herramienta aquí. Este ejemplo devuelve una cadena fija.
      content: "Linux demo-host 6.8.0-52-generic x86_64 GNU/Linux"
    }
  end

  if response.stop_reason == :tool_use && !tool_results.empty?
    # Un bloque server_tool_use sin bloque de resultado en esta respuesta no ha terminado; su resultado llega en una respuesta posterior.
    # Envía de vuelta solo los bloques tool_result del cliente, con las mismas herramientas.
    continuation = client.messages.create(
      model: "claude-opus-5-5",
      max_tokens: 1024,
      tools: tools,
      messages: [
        user_message,
        { role: "assistant", content: response.content },
        { role: "user", content: tool_results }
      ]
    )
    # Si un web_fetch fue diferido, se ejecuta en esta solicitud y su
    # web_fetch_tool_result es el primer bloque de continuation.content.
    puts continuation
  else
    puts response
  end
  ```
</CodeGroup>

Este código también es correcto cuando Claude no mezcla los dos tipos de llamada. Un turno con solo bloques `tool_use` de cliente sigue la misma ruta de continuación, y un turno con solo llamadas a herramientas de servidor no necesita bloques `tool_result` de cliente de tu parte: sus bloques de resultado normalmente ya están presentes, y uno que regresa suspendido, como una [respuesta `pause_turn`](https://platform.claude.com/docs/es/agents-and-tools/tool-use/server-tools#the-server-side-loop-and-pause-turn), se reenvía tal cual en su lugar.

## ZDR y allowed\_callers

Las versiones básicas de búsqueda web (`web_search_20250305`) y web fetch (`web_fetch_20250910`) son elegibles para [Zero Data Retention (ZDR)](https://platform.claude.com/docs/es/manage-claude/api-and-data-retention).

Las versiones `_20260209` y posteriores con filtrado dinámico **no** son elegibles para ZDR de forma predeterminada porque el filtrado dinámico depende internamente de la ejecución de código.

Para usar una herramienta de servidor `_20260209` o posterior con ZDR, desactiva el filtrado dinámico estableciendo `"allowed_callers": ["direct"]` en la herramienta:

```json
{
  "type": "web_search_20260209",
  "name": "web_search",
  "allowed_callers": ["direct"]
}
```

Esto restringe la herramienta únicamente a la invocación directa, omitiendo el paso interno de ejecución de código.

`allowed_callers` controla cómo se puede invocar una herramienta: directamente por Claude (`"direct"`), desde dentro de un contenedor de ejecución de código (por ejemplo, `"code_execution_20260120"`), o ambos. Las versiones `_20260209` de las herramientas web usan de forma predeterminada solo el invocador de ejecución de código; las versiones anteriores usan `["direct"]` de forma predeterminada. En modelos que no admiten la llamada programática de herramientas, estas versiones requieren `allowed_callers: ["direct"]`; sin ello, la API devuelve un error de validación que indica que debes establecerlo.

<Note>
  Incluso cuando web fetch se usa en una configuración elegible para ZDR, los editores de sitios web podrían retener cualquier parámetro pasado a la URL si Claude obtiene contenido de su sitio.
</Note>

## Filtrado de dominios

Las herramientas de servidor que acceden a la web aceptan los parámetros `allowed_domains` y `blocked_domains` para controlar a qué dominios puede llegar Claude. Ambos son campos del objeto de la herramienta:

```json
{
  "type": "web_search_20250305",
  "name": "web_search",
  "allowed_domains": ["example.com", "docs.python.org"]
}
```

Al usar filtros de dominio:

* Los dominios no deben incluir el esquema HTTP/HTTPS (usa `example.com` en lugar de `https://example.com`).
* Los subdominios se incluyen automáticamente (`example.com` cubre `docs.example.com`).
* Los subdominios específicos restringen los resultados solo a ese subdominio (`docs.example.com` devuelve solo resultados de ese subdominio, no de `example.com` ni de `api.example.com`).
* Las subrutas son compatibles con la búsqueda web y coinciden con cualquier cosa después de la ruta (`example.com/blog` coincide con `example.com/blog/post-1`).
* Web fetch coincide solo con el dominio: una entrada que incluye una ruta nunca coincide con una URL de web fetch.
* Puedes usar `allowed_domains` o `blocked_domains`, pero no ambos en la misma solicitud.

**Compatibilidad con comodines:**

* Los comodines (`*`) no están permitidos en el dominio en sí, solo en la ruta que le sigue.
* Válido: `example.com/*`, `example.com/*/articles`
* Inválido: `*.example.com`, `ex*.com`

Los formatos de dominio inválidos se rechazan en el momento de la solicitud con un 400 `invalid_request_error`.

<Note>
  Las restricciones de dominio a nivel de solicitud funcionan en conjunto con cualquier restricción de dominio a nivel de organización configurada en Claude Console. Los `allowed_domains` a nivel de solicitud deben ser un subconjunto de la lista permitida a nivel de organización; las entradas fuera de ella hacen que la API devuelva un error de validación. Una lista permitida a nivel de solicitud que incluya un dominio que tu organización bloquea se rechaza con un error `400` que nombra las entradas en conflicto.
</Note>

<Warning>
  Los caracteres Unicode en los nombres de dominio pueden eludir los filtros de dominio mediante ataques de homógrafos: `аmazon.com` (con una `а` cirílica) se ve idéntico a `amazon.com` pero es un dominio diferente. Usa nombres de dominio solo en ASCII en las listas de permitidos y bloqueados, y audita las entradas existentes en busca de caracteres no ASCII.
</Warning>

[Claude Managed Agents](https://platform.claude.com/docs/es/managed-agents/overview) usa los mismos campos `allowed_domains` y `blocked_domains` en las entradas `web_search` y `web_fetch` del conjunto de herramientas del agente. En Managed Agents, cada lista contiene como máximo 64 entradas, los dominios listados para `web_fetch` no pueden incluir una ruta, y los campos específicos de las herramientas de la Messages API, como `max_uses`, `citations` y `cache_control`, no están disponibles. Consulta [Reglas de las listas de dominios](https://platform.claude.com/docs/es/managed-agents/tools-web-restrictions#domain-list-rules) para ver las reglas completas.

La configuración de búsqueda web y web fetch a nivel de organización en Claude Console se aplica solo a las solicitudes de la Messages API. No se aplica a las sesiones de Managed Agents, que en su lugar usan las listas por herramienta del conjunto de herramientas del agente. Para una sesión en un entorno en la nube con redes `limited`, el `allowed_hosts` del entorno también se aplica a `web_search` y `web_fetch`; consulta [Redes](https://platform.claude.com/docs/es/managed-agents/environments#networking). Las listas por herramienta restringen aún más estas herramientas, dentro de los hosts que `allowed_hosts` permite.

## Filtrado dinámico con ejecución de código

Las versiones `_20260209` y posteriores de búsqueda web y web fetch usan internamente la ejecución de código para aplicar filtros dinámicos a los resultados de búsqueda.

<Note>
  No necesitas agregar una herramienta `code_execution` para estas versiones: cuando se ejecuta el filtrado dinámico, la API aprovisiona automáticamente la ejecución de código para la solicitud, y ambas herramientas comparten un único contenedor de ejecución. Si incluyes una, usa `code_execution_20260120` o posterior; la API rechaza las versiones anteriores de ejecución de código junto con estas versiones de las herramientas web.
</Note>

## Streaming de eventos de herramientas de servidor

Los eventos de herramientas de servidor se transmiten por streaming como parte del flujo normal de "server-sent events" (eventos enviados por el servidor), o SSE. Un bloque `server_tool_use` que Claude llama directamente se transmite como un bloque `tool_use` de cliente: un evento `content_block_start` seguido de eventos `input_json_delta`. El bloque de resultado llega completo en un único evento `content_block_start`, sin deltas.

Consulta [Streaming](https://platform.claude.com/docs/es/build-with-claude/streaming) para ver la referencia completa de eventos. Las páginas de herramientas individuales documentan los nombres de eventos específicos de cada herramienta cuando difieren.

## Solicitudes por lotes

Todas las herramientas de servidor admiten el procesamiento por lotes. En un lote, el bucle agéntico se ejecuta igual que para las solicitudes síncronas, con un límite de iteraciones por turno más alto. Si el bucle alcanza ese límite, la respuesta termina con `stop_reason: "pause_turn"`; puedes continuarla enviando una solicitud de seguimiento con el contenido devuelto. Consulta [Herramientas de servidor y el bucle agéntico](https://platform.claude.com/docs/es/build-with-claude/batch-processing#server-tools-and-the-agentic-loop) para más detalles.

Las cargas de trabajo por lotes comunes incluyen enriquecer un conjunto de datos con información de la web, verificar un gran conjunto de documentos contra fuentes actuales y ejecutar código de análisis sobre muchos archivos.

## Próximos pasos

<CardGroup cols={2}>
  <Card title="Solución de problemas del uso de herramientas" icon="wrench" href="https://platform.claude.com/docs/es/agents-and-tools/tool-use/troubleshooting-tool-use">
    Corrige los errores más comunes del uso de herramientas con tablas de diagnóstico de síntoma a solución.
  </Card>

  <Card title="Herramienta de búsqueda web" icon="magnifying-glass" href="https://platform.claude.com/docs/es/agents-and-tools/tool-use/web-search-tool">
    Busca en la web y cita los resultados.
  </Card>

  <Card title="Herramienta web fetch" icon="download" href="https://platform.claude.com/docs/es/agents-and-tools/tool-use/web-fetch-tool">
    Obtén y lee contenido de URLs específicas para ampliar el contexto de Claude con contenido web en vivo.
  </Card>

  <Card title="Herramienta de ejecución de código" icon="terminal" href="https://platform.claude.com/docs/es/agents-and-tools/tool-use/code-execution-tool">
    Ejecuta código Python y bash en un contenedor aislado para analizar datos, generar archivos e iterar sobre soluciones.
  </Card>

  <Card title="Herramienta de búsqueda de herramientas" icon="compass" href="https://platform.claude.com/docs/es/agents-and-tools/tool-use/tool-search-tool">
    Descubre y carga herramientas bajo demanda.
  </Card>
</CardGroup>
