---
title: Streaming granulare degli strumenti
url: https://platform.claude.com/docs/it/agents-and-tools/tool-use/fine-grained-tool-streaming
description: Trasmetti in streaming gli input degli strumenti senza buffering JSON lato server per applicazioni sensibili alla latenza.
---

<Note>
  Per scoprire come la "zero data retention" (conservazione zero dei dati), o ZDR, si applica a questa funzionalità, consulta [API e conservazione dei dati](https://platform.claude.com/docs/it/manage-claude/api-and-data-retention).
</Note>

Il "fine-grained tool streaming" (streaming granulare degli strumenti) consegna l'input di uno strumento al tuo client man mano che Claude lo genera, senza buffering lato server né validazione JSON. Saltare la fase di buffering riduce il tempo necessario per ricevere il primo frammento di un parametro di grandi dimensioni, come un documento o un blocco di codice, e i frammenti arrivano attraverso gli stessi eventi di [Streaming dei messaggi](https://platform.claude.com/docs/it/build-with-claude/streaming) dell'uso degli strumenti standard.

<Warning>
  Poiché l'API non esegue il buffering né valida l'input di uno strumento prima di trasmetterlo in streaming, potresti ricevere JSON parziale o non valido. Una risposta che termina con lo [stop reason](https://platform.claude.com/docs/it/build-with-claude/handling-stop-reasons) `max_tokens` può inoltre troncare un parametro a metà. Accumula i frammenti, proteggi il parsing e consulta [Gestione del JSON non valido nelle risposte degli strumenti](https://platform.claude.com/docs/it/agents-and-tools/tool-use/fine-grained-tool-streaming#handling-invalid-json-in-tool-responses) per sapere come restituire a Claude un input non analizzabile.
</Warning>

## Come usare lo streaming granulare degli strumenti

Tutti i modelli supportano lo streaming granulare degli strumenti sulla Claude API, su [Amazon Bedrock](https://platform.claude.com/docs/it/build-with-claude/claude-in-amazon-bedrock), [Claude Platform on AWS](https://platform.claude.com/docs/it/build-with-claude/claude-platform-on-aws), [Google Cloud](https://platform.claude.com/docs/it/build-with-claude/claude-on-vertex-ai) e [Microsoft Foundry](https://platform.claude.com/docs/it/build-with-claude/claude-in-microsoft-foundry). Per usarlo, imposta `eager_input_streaming` su `true` su qualsiasi strumento definito dall'utente per il quale desideri abilitare lo streaming granulare, e abilita lo streaming nella tua richiesta.

Il campo `eager_input_streaming` è facoltativo. Impostarlo su `true` attiva lo streaming granulare per quello strumento, mentre ometterlo ti fornisce lo streaming standard con buffering, in cui l'API esegue il buffering e valida ogni valore di parametro prima di ritrasmetterlo in streaming. L'eccezione è una richiesta che invia ancora l'header beta legacy `fine-grained-tool-streaming-2025-05-14`, che attiva lo streaming granulare per gli strumenti che lasciano il campo non impostato. Il campo per singolo strumento sostituisce quell'header, e un `false` esplicito mantiene lo streaming con buffering per uno strumento anche quando una richiesta lo invia ancora. L'header legacy non può essere combinato con una voce di toolset [computer use](https://platform.claude.com/docs/it/agents-and-tools/tool-use/computer-use-tool) o [browser use](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-tool): l'API rifiuta una richiesta che invia entrambi, quindi rimuovi l'header e imposta `eager_input_streaming` sugli strumenti definiti dall'utente che ne hanno bisogno. Consulta il [Riferimento degli strumenti](https://platform.claude.com/docs/it/agents-and-tools/tool-use/tool-reference) per la definizione del campo.

L'esempio seguente attiva lo streaming granulare per uno strumento `make_file` e chiede a Claude una lunga poesia, in modo che l'input dello strumento sia abbastanza grande da poterlo osservare mentre arriva in streaming:

<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": 65536,
      "tools": [
        {
          "name": "make_file",
          "description": "Write text to a file",
          "eager_input_streaming": true,
          "input_schema": {
            "type": "object",
            "properties": {
              "filename": {
                "type": "string",
                "description": "The filename to write text to"
              },
              "lines_of_text": {
                "type": "array",
                "description": "An array of lines of text to write to the file"
              }
            },
            "required": ["filename", "lines_of_text"]
          }
        }
      ],
      "messages": [
        {
          "role": "user",
          "content": "Can you write a long poem and make a file called poem.txt?"
        }
      ],
      "stream": true
    }'
  ```

  ```bash CLI
  ant messages create --stream --format jsonl <<'YAML' |
  model: claude-opus-5-5
  max_tokens: 65536
  tools:
    - name: make_file
      description: Write text to a file
      eager_input_streaming: true
      input_schema:
        type: object
        properties:
          filename:
            type: string
            description: The filename to write text to
          lines_of_text:
            type: array
            description: An array of lines of text to write to the file
        required:
          - filename
          - lines_of_text
  messages:
    - role: user
      content: Can you write a long poem and make a file called poem.txt?
  YAML
    jq -rj 'select(.delta.type == "input_json_delta") | .delta.partial_json'
  ```

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

  with client.messages.stream(
      max_tokens=65536,
      model="claude-opus-5-5",
      tools=[
          {
              "name": "make_file",
              "description": "Write text to a file",
              "eager_input_streaming": True,
              "input_schema": {
                  "type": "object",
                  "properties": {
                      "filename": {
                          "type": "string",
                          "description": "The filename to write text to",
                      },
                      "lines_of_text": {
                          "type": "array",
                          "description": "An array of lines of text to write to the file",
                      },
                  },
                  "required": ["filename", "lines_of_text"],
              },
          }
      ],
      messages=[
          {
              "role": "user",
              "content": "Can you write a long poem and make a file called poem.txt?",
          }
      ],
  ) as stream:
      for event in stream:
          if event.type == "input_json":
              print(event.partial_json, end="", flush=True)
      final_message = stream.get_final_message()

  print()
  for block in final_message.content:
      if block.type == "tool_use":
          print(f"Complete tool input: {block.input}")
  ```

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

  const stream = client.messages.stream({
    model: "claude-opus-5-5",
    max_tokens: 65536,
    tools: [
      {
        name: "make_file",
        description: "Write text to a file",
        eager_input_streaming: true,
        input_schema: {
          type: "object",
          properties: {
            filename: {
              type: "string",
              description: "The filename to write text to"
            },
            lines_of_text: {
              type: "array",
              description: "An array of lines of text to write to the file"
            }
          },
          required: ["filename", "lines_of_text"]
        }
      }
    ],
    messages: [
      {
        role: "user",
        content: "Can you write a long poem and make a file called poem.txt?"
      }
    ]
  });

  stream.on("inputJson", (partialJson) => {
    process.stdout.write(partialJson);
  });

  const message = await stream.finalMessage();
  console.log();
  for (const block of message.content) {
    if (block.type === "tool_use") {
      console.log("Complete tool input:", block.input);
    }
  }
  ```

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

  MessageCreateParams parameters = new()
  {
      Model = Model.ClaudeOpus5_5,
      MaxTokens = 65536,
      Tools =
      [
          new Tool
          {
              Name = "make_file",
              Description = "Write text to a file",
              EagerInputStreaming = true,
              InputSchema = new InputSchema
              {
                  Properties = new Dictionary<string, JsonElement>
                  {
                      ["filename"] = JsonSerializer.SerializeToElement(
                          new { type = "string", description = "The filename to write text to" }
                      ),
                      ["lines_of_text"] = JsonSerializer.SerializeToElement(
                          new { type = "array", description = "An array of lines of text to write to the file" }
                      ),
                  },
                  Required = ["filename", "lines_of_text"],
              },
          },
      ],
      Messages =
      [
          new()
          {
              Role = Role.User,
              Content = "Can you write a long poem and make a file called poem.txt?",
          },
      ],
  };

  // L'esempio C# assembla l'input da sé: indice del blocco di contenuto -> JSON accumulato
  var toolInputs = new Dictionary<long, StringBuilder>();

  await foreach (var streamEvent in client.Messages.CreateStreaming(parameters))
  {
      if (
          streamEvent.TryPickContentBlockStart(out var start)
          && start.ContentBlock.TryPickToolUse(out _)
      )
      {
          toolInputs[start.Index] = new StringBuilder();
      }
      else if (
          streamEvent.TryPickContentBlockDelta(out var delta)
          && delta.Delta.TryPickInputJson(out var inputJson)
      )
      {
          Console.Write(inputJson.PartialJson);
          toolInputs[delta.Index].Append(inputJson.PartialJson);
      }
  }

  Console.WriteLine();
  foreach (var accumulatedInput in toolInputs.Values)
  {
      Console.WriteLine($"Complete tool input: {accumulatedInput}");
  }
  ```

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

  makeFileTool := anthropic.ToolParam{
  	Name:                "make_file",
  	Description:         anthropic.String("Write text to a file"),
  	EagerInputStreaming: anthropic.Bool(true),
  	InputSchema: anthropic.ToolInputSchemaParam{
  		Properties: map[string]any{
  			"filename": map[string]any{
  				"type":        "string",
  				"description": "The filename to write text to",
  			},
  			"lines_of_text": map[string]any{
  				"type":        "array",
  				"description": "An array of lines of text to write to the file",
  			},
  		},
  		Required: []string{"filename", "lines_of_text"},
  	},
  }

  stream := client.Messages.NewStreaming(context.Background(), anthropic.MessageNewParams{
  	Model:     anthropic.ModelClaudeOpus5_5,
  	MaxTokens: 65536,
  	Tools:     []anthropic.ToolUnionParam{{OfTool: &makeFileTool}},
  	Messages: []anthropic.MessageParam{
  		anthropic.NewUserMessage(anthropic.NewTextBlock(
  			"Can you write a long poem and make a file called poem.txt?",
  		)),
  	},
  })

  message := anthropic.Message{}
  for stream.Next() {
  	event := stream.Current()
  	if err := message.Accumulate(event); err != nil {
  		panic(err)
  	}
  	if delta, ok := event.AsAny().(anthropic.ContentBlockDeltaEvent); ok {
  		if inputJSON, ok := delta.Delta.AsAny().(anthropic.InputJSONDelta); ok {
  			fmt.Print(inputJSON.PartialJSON)
  		}
  	}
  }
  if err := stream.Err(); err != nil {
  	panic(err)
  }

  fmt.Println()
  for _, block := range message.Content {
  	if toolUse, ok := block.AsAny().(anthropic.ToolUseBlock); ok {
  		fmt.Printf("Complete tool input: %s\n", toolUse.Input)
  	}
  }
  ```

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

  Tool makeFileTool = Tool.builder()
      .name("make_file")
      .description("Write text to a file")
      .eagerInputStreaming(true)
      .inputSchema(Tool.InputSchema.builder()
          .properties(Tool.InputSchema.Properties.builder()
              .putAdditionalProperty("filename", JsonValue.from(Map.of(
                  "type", "string",
                  "description", "The filename to write text to")))
              .putAdditionalProperty("lines_of_text", JsonValue.from(Map.of(
                  "type", "array",
                  "description", "An array of lines of text to write to the file")))
              .build())
          .addRequired("filename")
          .addRequired("lines_of_text")
          .build())
      .build();

  MessageCreateParams params = MessageCreateParams.builder()
      .model(Model.CLAUDE_OPUS_5_5)
      .maxTokens(65536L)
      .addTool(makeFileTool)
      .addUserMessage("Can you write a long poem and make a file called poem.txt?")
      .build();

  MessageAccumulator accumulator = MessageAccumulator.create();

  try (StreamResponse<RawMessageStreamEvent> streamResponse =
          client.messages().createStreaming(params)) {
      streamResponse.stream().forEach(event -> {
          accumulator.accumulate(event);
          if (event.isContentBlockDelta()) {
              var delta = event.asContentBlockDelta().delta();
              if (delta.isInputJson()) {
                  IO.print(delta.asInputJson().partialJson());
              }
          }
      });
  }

  IO.println("");
  accumulator.message().content().forEach(block ->
      block.toolUse().ifPresent(toolUse ->
          IO.println("Complete tool input: " + toolUse._input())));
  ```

  ```php PHP
  use Anthropic\Client;
  use Anthropic\Messages\InputJSONDelta;
  use Anthropic\Messages\Model;
  use Anthropic\Messages\RawContentBlockDeltaEvent;
  use Anthropic\Messages\RawContentBlockStartEvent;
  use Anthropic\Messages\ToolUseBlock;

  $client = new Client();

  $stream = $client->messages->createStream(
      maxTokens: 65536,
      model: Model::CLAUDE_OPUS_5_5,
      tools: [
          [
              'name' => 'make_file',
              'description' => 'Write text to a file',
              'eager_input_streaming' => true,
              'input_schema' => [
                  'type' => 'object',
                  'properties' => [
                      'filename' => [
                          'type' => 'string',
                          'description' => 'The filename to write text to',
                      ],
                      'lines_of_text' => [
                          'type' => 'array',
                          'description' => 'An array of lines of text to write to the file',
                      ],
                  ],
                  'required' => ['filename', 'lines_of_text'],
              ],
          ],
      ],
      messages: [
          [
              'role' => 'user',
              'content' => 'Can you write a long poem and make a file called poem.txt?',
          ],
      ],
  );

  // L'esempio PHP assembla l'input da sé: indice => stringa JSON accumulata
  $toolInputs = [];

  foreach ($stream as $event) {
      switch (true) {
          case $event instanceof RawContentBlockStartEvent:
              if ($event->contentBlock instanceof ToolUseBlock) {
                  $toolInputs[$event->index] = '';
              }
              break;
          case $event instanceof RawContentBlockDeltaEvent:
              if ($event->delta instanceof InputJSONDelta) {
                  echo $event->delta->partialJSON;
                  $toolInputs[$event->index] .= $event->delta->partialJSON;
              }
              break;
      }
  }

  echo "\n";
  foreach ($toolInputs as $toolInput) {
      echo "Complete tool input: {$toolInput}\n";
  }
  ```

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

  stream = client.messages.stream(
    model: Anthropic::Models::Model::CLAUDE_OPUS_5_5,
    max_tokens: 65_536,
    tools: [
      {
        name: "make_file",
        description: "Write text to a file",
        eager_input_streaming: true,
        input_schema: {
          type: "object",
          properties: {
            filename: {
              type: "string",
              description: "The filename to write text to"
            },
            lines_of_text: {
              type: "array",
              description: "An array of lines of text to write to the file"
            }
          },
          required: ["filename", "lines_of_text"]
        }
      }
    ],
    messages: [
      {
        role: "user",
        content: "Can you write a long poem and make a file called poem.txt?"
      }
    ]
  )

  stream.each do |event|
    print event.partial_json if event.is_a?(Anthropic::Streaming::InputJsonEvent)
  end

  puts
  stream.accumulated_message.content.each do |block|
    puts "Complete tool input: #{block.input}" if block.type == :tool_use
  end
  ```
</CodeGroup>

Ogni scheda attiva lo streaming granulare per lo strumento `make_file`. Le schede degli SDK stampano ogni frammento di input nel momento in cui arriva, quindi stampano l'input completo accumulato una volta terminato lo stream. La scheda cURL mostra lo stream di eventi grezzo, e la scheda CLI usa `jq` per stampare solo i frammenti. Poiché i frammenti stampati si uniscono a formare l'input completo dello strumento, la poesia riempie il tuo terminale man mano che Claude la scrive:

```text wrap
{"filename": "poem.txt", "lines_of_text": ["The Wanderer's Journey", "", "I.", "", "Beneath the vast and star-strewn sky,", "Where silver moonbeams softly lie,", ...
Complete tool input: {"filename": "poem.txt", "lines_of_text": ["The Wanderer's Journey", ...]}
```

Senza `eager_input_streaming`, l'API esegue il buffering e valida ogni valore di parametro prima di ritrasmetterlo in streaming, quindi per un parametro di grandi dimensioni non viene stampato nulla finché Claude non ha finito di generarlo. Con esso, i frammenti iniziano ad arrivare non appena Claude inizia il parametro, e sono tipicamente più lunghi, con meno interruzioni a metà parola.

## Accumulare i delta dell'input degli strumenti

Il contratto di accumulo è lo stesso dello streaming standard dell'uso degli strumenti, quindi questa sezione si applica con e senza `eager_input_streaming`. Consulta [Input JSON delta](https://platform.claude.com/docs/it/build-with-claude/streaming#input-json-delta) in Streaming dei messaggi per il formato degli eventi. Lo streaming granulare degli strumenti cambia ciò che puoi presumere sul risultato: il server trasmette i frammenti in streaming senza validarli, quindi la stringa accumulata potrebbe non essere JSON valido.

Quando un blocco di contenuto `tool_use` viene trasmesso in streaming, l'evento iniziale `content_block_start` contiene `input: {}` (un oggetto vuoto). Si tratta di un segnaposto. L'input effettivo arriva come una serie di eventi `input_json_delta`, ciascuno dei quali trasporta un frammento di stringa `partial_json`. Per assemblare l'input completo, concatena questi frammenti e analizza il risultato quando il blocco si chiude.

Se il tuo SDK fornisce un helper di accumulo (come fanno le schede Python, TypeScript, Go, Java e Ruby nell'esempio precedente), se ne occupa lui per te. Usa il pattern manuale quando il tuo SDK non ha un helper o quando desideri il pieno controllo su come viene assemblato l'input.

Il contratto di accumulo:

1. Su `content_block_start` con `type: "tool_use"`, inizializza una stringa vuota: `input_json = ""`
2. Per ogni `content_block_delta` con `type: "input_json_delta"`, aggiungi: `input_json += event.delta.partial_json`
3. Su `content_block_stop`, analizza la stringa accumulata

Proteggi il parsing, come fanno i seguenti esempi degli SDK. Una risposta può anche fermarsi a `max_tokens` a metà di un parametro. Controlla lo [stop reason](https://platform.claude.com/docs/it/build-with-claude/handling-stop-reasons) e decidi se ritentare la richiesta con un `max_tokens` più alto o riparare l'input parziale.

La discrepanza di tipo tra l'`input: {}` iniziale (oggetto) e `partial_json` (stringa) è intenzionale. L'oggetto vuoto contrassegna la posizione nell'array del contenuto. Le stringhe delta costruiscono il valore reale.

<CodeGroup exclude="shell">
  <CodeGroupItem language="cURL">
    Accumulare i frammenti di input di ogni blocco `tool_use` richiede più logica di un singolo comando shell. Usa il [Claude SDK](https://platform.claude.com/docs/it/cli-sdks-libraries/overview) per farlo. Per vedere i frammenti grezzi mentre arrivano in streaming, esegui la richiesta in [Come usare lo streaming granulare degli strumenti](https://platform.claude.com/docs/it/agents-and-tools/tool-use/fine-grained-tool-streaming#how-to-use-fine-grained-tool-streaming).
  </CodeGroupItem>

  <CodeGroupItem language="CLI">
    Accumulare i frammenti di input di ogni blocco `tool_use` richiede più logica di un singolo comando shell. Usa il [Claude SDK](https://platform.claude.com/docs/it/cli-sdks-libraries/overview) per farlo. Per vedere i frammenti grezzi mentre arrivano in streaming, esegui la richiesta in [Come usare lo streaming granulare degli strumenti](https://platform.claude.com/docs/it/agents-and-tools/tool-use/fine-grained-tool-streaming#how-to-use-fine-grained-tool-streaming).
  </CodeGroupItem>

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

  tool_inputs: dict[int, str] = {}  # index -> accumulated JSON string

  with client.messages.stream(
      model="claude-opus-5-5",
      max_tokens=1024,
      tools=[
          {
              "name": "get_weather",
              "description": "Get current weather for a city",
              "eager_input_streaming": True,
              "input_schema": {
                  "type": "object",
                  "properties": {"city": {"type": "string"}},
                  "required": ["city"],
              },
          }
      ],
      messages=[{"role": "user", "content": "Weather in Paris?"}],
  ) as stream:
      for event in stream:
          match event.type:
              case "content_block_start" if event.content_block.type == "tool_use":
                  tool_inputs[event.index] = ""
              case "content_block_delta" if event.delta.type == "input_json_delta":
                  tool_inputs[event.index] += event.delta.partial_json
              case "content_block_stop" if event.index in tool_inputs:
                  raw_input = tool_inputs[event.index]
                  try:
                      parsed = json.loads(raw_input)
                  except json.JSONDecodeError:
                      # Non è garantito che la stringa accumulata sia JSON valido.
                      # Vedi "Handling invalid JSON in tool responses" in questa pagina.
                      print(f"Invalid tool input: {raw_input}")
                  else:
                      print(f"Tool input: {parsed}")
  ```

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

  const toolInputs = new Map<number, string>();

  const stream = client.messages.stream({
    model: "claude-opus-5-5",
    max_tokens: 1024,
    tools: [
      {
        name: "get_weather",
        description: "Get current weather for a city",
        eager_input_streaming: true,
        input_schema: {
          type: "object",
          properties: { city: { type: "string" } },
          required: ["city"]
        }
      }
    ],
    messages: [{ role: "user", content: "Weather in Paris?" }]
  });

  for await (const event of stream) {
    switch (event.type) {
      case "content_block_start":
        if (event.content_block.type === "tool_use") {
          toolInputs.set(event.index, "");
        }
        break;
      case "content_block_delta":
        if (event.delta.type === "input_json_delta") {
          toolInputs.set(
            event.index,
            (toolInputs.get(event.index) ?? "") + event.delta.partial_json
          );
        }
        break;
      case "content_block_stop":
        if (toolInputs.has(event.index)) {
          const rawInput = toolInputs.get(event.index)!;
          try {
            console.log("Tool input:", JSON.parse(rawInput));
          } catch {
            // Non è garantito che la stringa accumulata sia JSON valido.
            // Vedi "Handling invalid JSON in tool responses" in questa pagina.
            console.log("Invalid tool input:", rawInput);
          }
        }
        break;
    }
  }
  ```

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

  MessageCreateParams parameters = new()
  {
      Model = Model.ClaudeOpus5_5,
      MaxTokens = 1024,
      Tools =
      [
          new Tool
          {
              Name = "get_weather",
              Description = "Get current weather for a city",
              EagerInputStreaming = true,
              InputSchema = new InputSchema
              {
                  Properties = new Dictionary<string, JsonElement>
                  {
                      ["city"] = JsonSerializer.SerializeToElement(new { type = "string" }),
                  },
                  Required = ["city"],
              },
          },
      ],
      Messages = [new() { Role = Role.User, Content = "Weather in Paris?" }],
  };

  // Indice del blocco -> frammenti JSON accumulati
  // Questo esempio accumula i delta manualmente per mostrare lo stream grezzo;
  // il MessageContentAggregator dell'SDK può anche accumulare automaticamente l'input degli strumenti.
  var toolInputs = new Dictionary<long, StringBuilder>();

  await foreach (var streamEvent in client.Messages.CreateStreaming(parameters))
  {
      if (
          streamEvent.TryPickContentBlockStart(out var start)
          && start.ContentBlock.TryPickToolUse(out _)
      )
      {
          toolInputs[start.Index] = new StringBuilder();
      }
      else if (
          streamEvent.TryPickContentBlockDelta(out var delta)
          && delta.Delta.TryPickInputJson(out var inputJson)
      )
      {
          toolInputs[delta.Index].Append(inputJson.PartialJson);
      }
      else if (
          streamEvent.TryPickContentBlockStop(out var stop)
          && toolInputs.TryGetValue(stop.Index, out var accumulated)
      )
      {
          try
          {
              using var parsed = JsonDocument.Parse(accumulated.ToString());
              Console.WriteLine($"Tool input: {parsed.RootElement}");
          }
          catch (JsonException)
          {
              // Non è garantito che la stringa accumulata sia JSON valido.
              // Vedi "Gestione di JSON non valido nelle risposte degli strumenti" in questa pagina.
              Console.WriteLine($"Invalid tool input: {accumulated}");
          }
      }
  }
  ```

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

  toolInputs := map[int64]string{} // content block index -> accumulated JSON

  stream := client.Messages.NewStreaming(context.Background(), anthropic.MessageNewParams{
  	Model:     anthropic.ModelClaudeOpus5_5,
  	MaxTokens: 1024,
  	Tools: []anthropic.ToolUnionParam{{
  		OfTool: &anthropic.ToolParam{
  			Name:                "get_weather",
  			Description:         anthropic.String("Get current weather for a city"),
  			EagerInputStreaming: anthropic.Bool(true),
  			InputSchema: anthropic.ToolInputSchemaParam{
  				Properties: map[string]any{
  					"city": map[string]any{"type": "string"},
  				},
  				Required: []string{"city"},
  			},
  		},
  	}},
  	Messages: []anthropic.MessageParam{
  		anthropic.NewUserMessage(anthropic.NewTextBlock("Weather in Paris?")),
  	},
  })

  for stream.Next() {
  	switch event := stream.Current().AsAny().(type) {
  	case anthropic.ContentBlockStartEvent:
  		if _, ok := event.ContentBlock.AsAny().(anthropic.ToolUseBlock); ok {
  			toolInputs[event.Index] = ""
  		}
  	case anthropic.ContentBlockDeltaEvent:
  		if delta, ok := event.Delta.AsAny().(anthropic.InputJSONDelta); ok {
  			toolInputs[event.Index] += delta.PartialJSON
  		}
  	case anthropic.ContentBlockStopEvent:
  		if accumulated, ok := toolInputs[event.Index]; ok {
  			var parsed map[string]any
  			if err := json.Unmarshal([]byte(accumulated), &parsed); err != nil {
  				// Non è garantito che la stringa accumulata sia JSON valido.
  				// Vedi "Handling invalid JSON in tool responses" in questa pagina.
  				fmt.Println("Invalid tool input:", accumulated)
  			} else {
  				fmt.Println("Tool input:", parsed)
  			}
  		}
  	}
  }
  if err := stream.Err(); err != nil {
  	panic(err)
  }
  ```

  ```java Java
  AnthropicClient client = AnthropicOkHttpClient.fromEnv();
  ObjectMapper objectMapper = new ObjectMapper();

  Tool weatherTool = Tool.builder()
          .name("get_weather")
          .description("Get current weather for a city")
          .eagerInputStreaming(true)
          .inputSchema(Tool.InputSchema.builder()
                  .properties(Tool.InputSchema.Properties.builder()
                          .putAdditionalProperty("city", JsonValue.from(Map.of("type", "string")))
                          .build())
                  .addRequired("city")
                  .build())
          .build();

  MessageCreateParams createParams = MessageCreateParams.builder()
          .model(Model.CLAUDE_OPUS_5_5)
          .maxTokens(1024)
          .addTool(weatherTool)
          .addUserMessage("Weather in Paris?")
          .build();

  // Indice del blocco di contenuto -> JSON di input dello strumento accumulato
  Map<Long, StringBuilder> toolInputs = new HashMap<>();

  try (StreamResponse<RawMessageStreamEvent> streamResponse = client.messages().createStreaming(createParams)) {
      var eventIterator = streamResponse.stream().iterator();
      while (eventIterator.hasNext()) {
          RawMessageStreamEvent event = eventIterator.next();
          switch (event.type().value()) {
              case CONTENT_BLOCK_START -> {
                  var blockStart = event.asContentBlockStart();
                  if (blockStart.contentBlock().isToolUse()) {
                      toolInputs.put(blockStart.index(), new StringBuilder());
                  }
              }
              case CONTENT_BLOCK_DELTA -> {
                  var blockDelta = event.asContentBlockDelta();
                  if (blockDelta.delta().isInputJson() && toolInputs.containsKey(blockDelta.index())) {
                      toolInputs.get(blockDelta.index()).append(blockDelta.delta().asInputJson().partialJson());
                  }
              }
              case CONTENT_BLOCK_STOP -> {
                  var blockStop = event.asContentBlockStop();
                  if (toolInputs.containsKey(blockStop.index())) {
                      String accumulated = toolInputs.get(blockStop.index()).toString();
                      try {
                          IO.println("Tool input: " + objectMapper.readTree(accumulated));
                      } catch (JsonProcessingException e) {
                          // Non è garantito che la stringa accumulata sia JSON valido.
                          // Vedi "Gestione del JSON non valido nelle risposte degli strumenti" in questa pagina.
                          IO.println("Invalid tool input: " + accumulated);
                      }
                  }
              }
          }
      }
  }
  ```

  ```php PHP
  use Anthropic\Client;
  use Anthropic\Messages\InputJSONDelta;
  use Anthropic\Messages\Model;
  use Anthropic\Messages\RawContentBlockDeltaEvent;
  use Anthropic\Messages\RawContentBlockStartEvent;
  use Anthropic\Messages\RawContentBlockStopEvent;
  use Anthropic\Messages\ToolUseBlock;

  $client = new Client();

  // L'SDK PHP non fornisce un accumulatore di stream per l'input degli strumenti;
  // il pattern manuale mostrato qui è l'approccio supportato.
  $toolInputs = []; // index => accumulated JSON string

  $stream = $client->messages->createStream(
      maxTokens: 1024,
      model: Model::CLAUDE_OPUS_5_5,
      tools: [
          [
              'name' => 'get_weather',
              'description' => 'Get current weather for a city',
              'eager_input_streaming' => true,
              'input_schema' => [
                  'type' => 'object',
                  'properties' => ['city' => ['type' => 'string']],
                  'required' => ['city'],
              ],
          ],
      ],
      messages: [['role' => 'user', 'content' => 'Weather in Paris?']],
  );

  foreach ($stream as $event) {
      switch (true) {
          case $event instanceof RawContentBlockStartEvent:
              if ($event->contentBlock instanceof ToolUseBlock) {
                  $toolInputs[$event->index] = '';
              }
              break;
          case $event instanceof RawContentBlockDeltaEvent:
              if ($event->delta instanceof InputJSONDelta) {
                  $toolInputs[$event->index] .= $event->delta->partialJSON;
              }
              break;
          case $event instanceof RawContentBlockStopEvent:
              if (isset($toolInputs[$event->index])) {
                  $accumulated = $toolInputs[$event->index];
                  try {
                      $parsed = json_decode($accumulated, associative: true, flags: JSON_THROW_ON_ERROR);
                      echo "Tool input: " . json_encode($parsed) . "\n";
                  } catch (\JsonException $e) {
                      // Non è garantito che la stringa accumulata sia JSON valido.
                      // Vedi "Gestione del JSON non valido nelle risposte degli strumenti" in questa pagina.
                      echo "Invalid tool input: {$accumulated}\n";
                  }
              }
              break;
      }
  }
  ```

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

  tool_inputs = {} # index -> accumulated JSON string

  stream = client.messages.stream_raw(
    model: Anthropic::Models::Model::CLAUDE_OPUS_5_5,
    max_tokens: 1024,
    tools: [
      {
        name: "get_weather",
        description: "Get current weather for a city",
        eager_input_streaming: true,
        input_schema: {
          type: "object",
          properties: {city: {type: "string"}},
          required: ["city"]
        }
      }
    ],
    messages: [{role: "user", content: "Weather in Paris?"}]
  )

  stream.each do |event|
    case event
    when Anthropic::Models::RawContentBlockStartEvent
      tool_inputs[event.index] = +"" if event.content_block.type == :tool_use
    when Anthropic::Models::RawContentBlockDeltaEvent
      if event.delta.is_a?(Anthropic::Models::InputJSONDelta)
        tool_inputs[event.index] << event.delta.partial_json
      end
    when Anthropic::Models::RawContentBlockStopEvent
      if tool_inputs.key?(event.index)
        accumulated = tool_inputs[event.index]
        begin
          parsed = JSON.parse(accumulated)
          puts "Tool input: #{parsed}"
        rescue JSON::ParserError
          # Non è garantito che la stringa accumulata sia un JSON valido.
          # Vedi "Handling invalid JSON in tool responses" in questa pagina.
          puts "Invalid tool input: #{accumulated}"
        end
      end
    end
  end
  ```
</CodeGroup>

<Tip>
  Reagire ai frammenti e assemblarli sono due aspetti distinti. Il primo esempio reagisce a ogni frammento man mano che arriva e affida comunque l'assemblaggio all'SDK nelle schede che usano un helper di accumulo. Usa il pattern manuale quando non stai usando un helper di accumulo o quando desideri il pieno controllo sull'assemblaggio.
</Tip>

## Gestione del JSON non valido nelle risposte degli strumenti

Con lo streaming granulare degli strumenti, l'input accumulato per una chiamata a uno strumento potrebbe essere JSON non valido o incompleto. Quando lo è, non puoi eseguire lo strumento, quindi segnala invece l'errore a Claude. Il `content` di un risultato di strumento non deve necessariamente essere JSON, ma racchiudere la stringa grezza in un oggetto JSON sotto una singola chiave rende inequivocabile per Claude che hai ricevuto JSON non valido, e preserva l'input originale per il debugging:

```json
{
  "INVALID_JSON": "<the unparseable input you received>"
}
```

Restituisci il wrapper, serializzato in una stringa, come `content` di un blocco di contenuto [tool result](https://platform.claude.com/docs/it/agents-and-tools/tool-use/handle-tool-calls#handling-errors-with-is-error) con `is_error` impostato su `true`:

```json
{
  "type": "tool_result",
  "tool_use_id": "toolu_01A09q90qw90lq917835lq9",
  "is_error": true,
  "content": "{\"INVALID_JSON\": \"<the unparseable input you received>\"}"
}
```

<Note>
  Costruisci il wrapper con la tua libreria JSON anziché concatenando stringhe, in modo che le virgolette e gli altri caratteri speciali nell'input non valido vengano sottoposti a escape correttamente.
</Note>

## Prossimi passi

<CardGroup cols={2}>
  <Card title="Finestre di contesto" icon="stack" href="https://platform.claude.com/docs/it/build-with-claude/context-windows">
    Comprendi come funziona la finestra di contesto, come il ragionamento esteso e l'uso degli strumenti vengono conteggiati al suo interno, e come gestire il contesto man mano che le conversazioni crescono.
  </Card>

  <Card title="Streaming dei messaggi" icon="lightning" href="https://platform.claude.com/docs/it/build-with-claude/streaming">
    Trasmetti in streaming le risposte della Messages API in modo incrementale con server-sent events, inclusi i delta di testo, uso degli strumenti e ragionamento esteso.
  </Card>

  <Card title="Gestire le chiamate agli strumenti" icon="arrows-left-right" href="https://platform.claude.com/docs/it/agents-and-tools/tool-use/handle-tool-calls">
    Analizza i blocchi tool\_use, formatta le risposte tool\_result e gestisci gli errori con is\_error.
  </Card>

  <Card title="Riferimento degli strumenti" icon="book" href="https://platform.claude.com/docs/it/agents-and-tools/tool-use/tool-reference">
    Elenco degli strumenti forniti da Anthropic e riferimento per le proprietà facoltative di definizione degli strumenti.
  </Card>
</CardGroup>
