---
title: Uso del browser e del computer con i toolset dell'SDK
url: https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk
description: Esegui lo strumento di uso del browser o lo strumento di uso del computer dall'SDK Python o TypeScript. L'SDK esegue il ciclo e i controlli che configuri, e tu fornisci il browser o il desktop.
---

Con i "toolset" (set di strumenti) del browser e del computer negli SDK Python e TypeScript, puoi creare un agente che lavora in un browser web o su un desktop. Può usare applicazioni prive di un'interfaccia programmatica. Tramite lo [strumento browser use](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-tool), Claude naviga, legge e interagisce con le pagine web. Tramite lo [strumento computer use](https://platform.claude.com/docs/it/agents-and-tools/tool-use/computer-use-tool), Claude vede un desktop attraverso screenshot e usa il mouse e la tastiera.

L'SDK collega Claude al tuo browser o desktop. Esegue i controlli che configuri, come una policy degli URL per `navigate` o una callback di approvazione. Invia ogni chiamata che li supera al tuo driver e costruisce il `tool_result` a partire da ciò che il driver restituisce. Il [tool runner](https://platform.claude.com/docs/it/agents-and-tools/tool-use/tool-runner) può eseguire il ciclo agentico al posto tuo.

Sei tu a fornire il driver, che gestisce il browser o il desktop. Parti dal [driver di un provider](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#use-a-driver-from-a-provider). Con uno di questi, un agente richiede poche righe di codice. Se nessuno è adatto, scrivine uno tuo con una libreria di automazione del browser come Playwright.

La maggior parte di questa pagina riguarda il toolset del browser. [Toolset del computer](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#computer-toolset) descrive le differenze.

Scrivere un driver personalizzato è un caso avanzato, trattato in [Implementa un driver](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver). Il repository claude-quickstarts contiene anche un [driver browser di esempio](https://github.com/anthropics/claude-quickstarts/tree/main/browser-toolset) in Python e TypeScript. Controlla Chromium tramite il Chrome DevTools Protocol (CDP) e non è codice di produzione.

<Note>
  La classe del toolset del browser è in beta ed è disponibile nell'[SDK Python](https://github.com/anthropics/anthropic-sdk-python/blob/main/browser-toolset.md) e nell'[SDK TypeScript](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/browser-toolset.md). La classe del toolset del computer è in beta negli stessi SDK, che dispongono di una [guida al toolset del computer per Python](https://github.com/anthropics/anthropic-sdk-python/blob/main/computer-toolset.md) e di una [guida al toolset del computer per TypeScript](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/computer-toolset.md).
</Note>

## Usa un driver di un provider

Con il driver di un provider, come quelli elencati in [Integrazioni dei partner](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#partner-integrations), puoi creare un agente in poche righe di codice. Il provider in questo esempio è inventato. Sostituisci `example_browser_provider` (typescript: `example-browser-provider`) e `ExampleBrowser` con il pacchetto e la classe driver di [un provider reale](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#partner-integrations), e configurali come descritto nella documentazione del provider.

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  from anthropic import Anthropic
  from example_browser_provider import ExampleBrowser  # made-up provider

  client = Anthropic()
  with ExampleBrowser() as browser:
      runner = client.beta.messages.tool_runner(
          model="claude-opus-5-5",
          max_tokens=1024,
          tools=[browser],
          messages=[
              {"role": "user", "content": "Open example.com and tell me its heading."}
          ],
      )
      for message in runner:
          print(message)
  ```

  ```typescript TypeScript
  import Anthropic from "@anthropic-ai/sdk";
  import { ExampleBrowser } from "example-browser-provider"; // made-up provider

  const client = new Anthropic();
  const browser = new ExampleBrowser();
  try {
    const runner = client.beta.messages.toolRunner({
      model: "claude-opus-5-5",
      max_tokens: 1024,
      tools: [browser],
      messages: [{ role: "user", content: "Open example.com and tell me its heading." }]
    });
    for await (const message of runner) {
      console.log(message);
    }
  } finally {
    await browser.close();
  }
  ```
</CodeGroup>

Passa l'istanza stessa del driver come voce di `tools`. Il runner non chiude mai il toolset, quindi una sola istanza può servire più esecuzioni. Chiudila quando hai finito.

Per scrivere un driver personalizzato, consulta [Implementa un driver](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver).

## Integrazioni dei partner

Browser Use, Browserbase, Daytona ed E2B pubblicano i propri driver per i toolset dell'SDK.

<CardGroup cols={2}>
  <Card id="browser-use" title="Browser Use" image="https://platform.claude.com/docs/images/partner-logos/browser-use.svg" imageDark="https://platform.claude.com/docs/images/partner-logos/browser-use-dark.svg" href="https://docs.browser-use.com/open-source/customize/integrations/toolsets-for-claude">
    Driver Browser Use per il toolset del browser.
  </Card>

  <Card id="browserbase" title="Browserbase" image="https://platform.claude.com/docs/images/partner-logos/browserbase.svg" imageDark="https://platform.claude.com/docs/images/partner-logos/browserbase-dark.svg" href="https://docs.stagehand.dev/v4/integrations/agent-frameworks/claude-cua-toolset-quickstart">
    Driver Browserbase Stagehand per il toolset del browser.
  </Card>

  <Card id="daytona" title="Daytona" image="https://platform.claude.com/docs/images/partner-logos/daytona.svg" imageDark="https://platform.claude.com/docs/images/partner-logos/daytona-dark.svg" href="https://www.daytona.io/docs/en/guides/claude/claude-draws-daytona-sandbox/">
    Driver Daytona per il toolset del computer.
  </Card>

  <Card id="e2-b" title="E2B" image="https://platform.claude.com/docs/images/partner-logos/e2b.svg" imageDark="https://platform.claude.com/docs/images/partner-logos/e2b-dark.svg" href="https://docs.e2b.dev/agents/claude-toolsets">
    Driver E2B per i toolset del browser e del computer.
  </Card>
</CardGroup>

I nostri partner pubblicano anche codice su GitHub: il [quickstart di Browser Use](https://github.com/browser-use/browser-use/tree/main/examples/integrations/toolsets-for-claude), il [toolset di Browserbase](https://github.com/browserbase/claude-cua-toolset) e l'[esempio di E2B](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/anthropic-computer-use-orangehrm-js).

<Warning>
  Fai queste due cose prima di eseguire un driver su qualcosa che non sia un browser usa e getta:

  * [Scrivi una policy degli URL](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy). Senza di essa, l'SDK non controlla alcun URL. Per un browser ospitato da un provider, leggi anche [Browser remoti e ospitati](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#remote-and-hosted-browsers).
  * Usa un profilo del browser che non abbia effettuato l'accesso ad alcun account i cui dati o le cui azioni non affideresti a Claude. Consulta [Isola l'host del browser](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#isolate-the-browser-host).

  [Esegui il toolset in sicurezza](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#run-the-toolset-safely) tratta il resto.
</Warning>

## Personalizza un driver

Gli esempi in questa sezione usano `MyBrowser`, il driver dell'[esempio avanzato](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#quick-start) più avanti in questa pagina.

### Abilita o disabilita i membri

`configs` accetta le impostazioni per membro descritte in [Configurare il toolset](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-tool#configure-the-toolset). Elenca solo i membri che modifichi:

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  # Un MyBrowser che implementa anche read_console
  browser = MyBrowser(
      backend, configs={"read_console": {"enabled": True}, "navigate": {"enabled": False}}
  )
  ```

  ```typescript TypeScript
  // Un MyBrowser che implementa anche read_console
  const browser = new MyBrowser(backend, {
    configs: { read_console: { enabled: true }, navigate: { enabled: false } }
  });
  ```
</CodeGroup>

L'SDK rifiuta una chiamata a un membro disabilitato prima che il tuo codice venga eseguito. Abilitare un membro che la tua classe non implementa è un errore di configurazione, a meno che la classe non sovrascriva `execute`.

### Aggiungi hook prima e dopo

Sovrascrivi `execute` e chiama l'`execute` della classe padre. Il codice prima di quella chiamata viene eseguito dopo la [policy degli URL](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy), la [policy dei file](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#confine-uploads-and-downloads) e [`confirm`](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#gate-consequential-members), e può modificare l'input. L'SDK non ricontrolla l'input modificato. Il codice dopo la chiamata riceve il risultato e può modificarlo. Solleva `ToolError` (lancialo con throw in TypeScript) per rifiutare la chiamata.

Sovrascrivere `execute` cambia i membri offerti a Claude. L'SDK considera implementato ogni membro, quindi a Claude viene offerto ogni membro attivo per impostazione predefinita. Il `MyBrowser` dell'[esempio avanzato](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#quick-start) serve tre membri, quindi il seguente `TracedBrowser` offre a Claude membri che non può servire. Disattiva quei membri con `configs` prima di usarlo.

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  import time


  class TracedBrowser(MyBrowser):
      def execute(self, context, name, input):
          started = time.monotonic()
          result = super().execute(context, name, input)
          elapsed_ms = (time.monotonic() - started) * 1000
          call_id = context.tool_use.id if context.tool_use else "-"
          log.info("%s %s %.0fms", call_id, name, elapsed_ms)
          return redact(result) if name == "get_page_text" else result
  ```

  ```typescript TypeScript
  import type { BetaToolsetCallContext } from "@anthropic-ai/sdk/helpers/beta/toolsets";
  import type {
    BetaBrowserMemberInput,
    BetaBrowserMemberName
  } from "@anthropic-ai/sdk/resources/beta";

  class TracedBrowser extends MyBrowser {
    protected override async execute(
      ctx: BetaToolsetCallContext,
      name: BetaBrowserMemberName,
      input: BetaBrowserMemberInput
    ) {
      const started = performance.now();
      const result = await super.execute(ctx, name, input);
      log.info(`${ctx.toolUse?.id ?? "-"} ${name} ${Math.round(performance.now() - started)}ms`);
      return name === "get_page_text" ? redact(result as string) : result;
    }
  }
  ```
</CodeGroup>

## Implementa un driver

Un driver è una tua sottoclasse di `BetaAbstractBrowserToolset20260801`. Al suo interno, sovrascrivi i membri supportati dal tuo browser. Un membro che non implementi viene inviato all'API come disabilitato. Se Claude lo chiama comunque, l'SDK restituisce un errore e l'esecuzione continua. Sovrascrivere `execute` cambia i membri inviati come disabilitati ([Aggiungi hook prima e dopo](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#add-before-and-after-hooks)). Ogni membro riceve il contesto della chiamata e l'input del membro come oggetto tipizzato, ad esempio `BetaBrowserNavigateInput`. I tipi di input provengono da `anthropic.types.beta` (typescript: `@anthropic-ai/sdk/resources/beta`). [Strumenti membro](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-tool#member-tools) elenca i campi di ciascun input.

In TypeScript, scrivi i membri come metodi, non come campi arrow function, perché l'SDK li trova sul prototipo. In TypeScript, scrivi il membro `type` come `type_`. In Python, è `type`.

### Restituisci i risultati

Ciò che un membro restituisce determina ciò che Claude legge. Un risultato riuscito termina con un blocco `browser_state` costruito dal tuo report di stato. Un risultato di errore non contiene alcun blocco.

| Membro                                                                                  | Restituisce                                                                                                    | Claude legge                                                                                  |
| --------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| `screenshot`, `zoom`                                                                    | `BetaScreenshotResult`                                                                                         | Un blocco immagine                                                                            |
| `navigate`                                                                              | `BetaBrowserNavigateResult`                                                                                    | `Navigated to {url} — {title} (HTTP {status})`                                                |
| `new_tab`, `switch_tab`, `list_tabs`, `close_tab`                                       | Una voce di scheda (`new_tab`, `switch_tab`), un elenco di voci di scheda (`list_tabs`) o niente (`close_tab`) | Solo il blocco `browser_state`                                                                |
| `read_page`, `get_page_text`, `find`, `read_console`, `read_network`, `javascript_exec` | Una stringa                                                                                                    | La stringa, oppure `(empty)` per una stringa vuota                                            |
| Ogni altro membro                                                                       | Niente, o una riga di testo                                                                                    | Una breve conferma, come `Clicked.`, seguita dalla riga restituita in un blocco di testo a sé |

### Riporta lo stato del browser

L'SDK chiama `_browser_state` (typescript: `browserState`) dopo ogni chiamata che restituisce un risultato, incluse le chiamate rifiutate e fallite. Restituisci ogni scheda aperta e ciò che è cambiato dall'ultimo report:

* Schede aperte ed eventi di download.
* Un `BetaNavigationRefused` per ogni navigazione bloccata dal tuo [hook delle richieste](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#intercept-requests-in-the-driver).
* Un `BetaDialogDismissed` per ogni finestra di dialogo nativa chiusa dal tuo driver.

Inserisci tutti questi elementi in `state_changes`. In Python, `BetaNavigationRefused()` e `BetaDialogDismissed(kind=..., message=...)` provengono da `anthropic.tools.browser`. In TypeScript, sono `{ type: "navigation_refused" }` e `{ type: "dialog_dismissed", kind, message }`.

Gli ultimi due non sono cambiamenti di stato dell'API. L'SDK li riporta a Claude come testo al di fuori del blocco `browser_state`: una riga per tutte le navigazioni rifiutate, che non include un URL, e una riga per ciascuna delle prime tre finestre di dialogo chiuse, seguita dal conteggio delle eventuali altre.

Quando è aperta almeno una scheda, esattamente una deve essere attiva. Ogni membro che accetta un `tab_id` deve agire sulla scheda che indica. I limiti dell'API sul report sono elencati in [Traccia le schede con `browser_state`](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-tool#track-tabs-and-page-state).

Claude legge l'URL di ogni scheda, l'URL di ogni download e l'URL restituito dal tuo `navigate`, così come li ha scritti il tuo driver. L'SDK non analizza questi URL. Trasforma le interruzioni di riga e gli altri caratteri di controllo in spazi, rimuove gli spazi alle estremità e tronca ogni URL a 4.096 caratteri. Quindi il contenuto di un URL `data:`, il percorso di un URL `file:` e un nome utente o una password in un URL arrivano tutti a Claude.

### Gestisci gli errori

| Sollevato da un membro o dall'SDK | Claude legge                                                                              | L'esecuzione  |
| --------------------------------- | ----------------------------------------------------------------------------------------- | ------------- |
| `ToolError`                       | Il suo messaggio, come risultato di errore                                                | Continua      |
| `ToolsetUsageError`               | Niente                                                                                    | Si interrompe |
| Qualsiasi altra eccezione         | `ClassName: message` in Python o `Error: message` in TypeScript, come risultato di errore | Continua      |

L'SDK solleva `ToolsetUsageError` per un errore di configurazione, un uso improprio dell'SDK durante una chiamata o una chiamata dopo `close`. Lo solleva anche quando `_browser_state` (typescript: `browserState`) solleva una qualsiasi eccezione, anche un `ToolError`. Il tool runner e `tool_result` (typescript: `toolResult`) non lo intercettano, quindi raggiunge il loro chiamante.

L'SDK non nasconde i percorsi locali nel testo di errore di un membro, nella riga restituita da un'azione come `left_click`, nell'errore di un download fallito o nel messaggio di una finestra di dialogo chiusa.

Un `ToolError` proveniente dalla tua policy degli URL, dalla policy dei file o dal callable `confirm` raggiunge Claude così come è scritto, quindi escludi i percorsi locali dal suo testo. Intercetta le eccezioni nei tuoi membri e solleva `ToolError` (lancialo con throw in TypeScript) con un testo tuo.

### Avvia le chiamate mentre la risposta è in streaming

Per impostazione predefinita, il tool runner esegue le chiamate di un turno dopo la fine della risposta di Claude. Con l'avvio anticipato, può avviare una chiamata al browser o al computer mentre la risposta è ancora in streaming.

Per attivarlo, passa `stream=True` (typescript: `stream: true`) e `run_tools_eagerly=True` (typescript: `runToolsEagerly: true`) al tool runner, come fa l'[esempio avanzato](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#quick-start). Con `stream`, ogni passaggio del tuo ciclo sul runner ti fornisce uno stream invece di un messaggio.

L'avvio anticipato cambia quando una chiamata può iniziare, non il modo in cui il toolset la esegue:

* **I controlli vengono comunque eseguiti per primi:** L'SDK chiama la tua policy degli URL, la tua policy dei file e `confirm` prima che una chiamata venga eseguita. Con l'avvio anticipato, può chiamare `confirm` mentre la risposta è ancora in streaming.
* **Le chiamate vengono comunque eseguite una alla volta:** Un toolset esegue le sue chiamate nell'ordine in cui Claude le ha scritte. Una chiamata fallita interrompe comunque le chiamate successive di quel toolset nel turno.
* **Una chiamata avviata non può essere annullata:** Se la risposta viene poi troncata, ad esempio a `max_tokens`, o il tuo ciclo si interrompe in anticipo, l'azione avviene comunque e Claude non ne legge mai il risultato.

### Esegui senza il tool runner

Passa l'istanza in `tools` (`browser.toJSON()` in TypeScript) e rispondi a ogni chiamata di membro con `tool_result` (typescript: `toolResult`). Lo strumento browser use richiede di fermarsi alla prima chiamata non riuscita ([Azioni batch](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-tool#batch-actions)).

Questo ciclo usa `MyBrowser` e la policy degli URL dell'[esempio avanzato](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#quick-start). Dopo una chiamata non riuscita, risponde alle chiamate successive del turno senza eseguirle:

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  from anthropic.types.beta import BetaMessageParam, BetaToolResultBlockParam

  NOT_EXECUTED = "Not executed: an earlier action in this turn failed."
  MAX_TURNS = 10

  with MyBrowser(backend, url_policy=url_policy) as browser:
      messages: list[BetaMessageParam] = [{"role": "user", "content": "Open example.com"}]
      for _ in range(MAX_TURNS):
          response = client.beta.messages.create(
              model="claude-opus-5-5",
              max_tokens=1024,
              tools=[browser],
              messages=messages,
          )
          messages.append({"role": "assistant", "content": response.content})
          calls = [
              block
              for block in response.content
              if block.type == "tool_use" and block.toolset_name == browser.toolset_name
          ]
          if not calls:
              break
          results: list[BetaToolResultBlockParam] = []
          failed = False
          for call in calls:
              if failed:
                  # Dopo una chiamata fallita, il resto del turno riceve una risposta, ma non viene eseguito.
                  results.append(
                      {
                          "type": "tool_result",
                          "tool_use_id": call.id,
                          "toolset_name": call.toolset_name,
                          "content": NOT_EXECUTED,
                          "is_error": True,
                      }
                  )
                  continue
              result = browser.tool_result(call)
              failed = bool(result.get("is_error"))
              results.append(result)
          messages.append({"role": "user", "content": results})
  ```

  ```typescript TypeScript
  const NOT_EXECUTED = "Not executed: an earlier action in this turn failed.";
  const MAX_TURNS = 10;

  const browser = new MyBrowser(backend, { urlPolicy });
  try {
    const messages: Anthropic.Beta.BetaMessageParam[] = [
      { role: "user", content: "Open example.com" }
    ];
    for (let turn = 0; turn < MAX_TURNS; turn++) {
      const response = await client.beta.messages.create({
        model: "claude-opus-5-5",
        max_tokens: 1024,
        tools: [browser.toJSON()],
        messages
      });
      messages.push({ role: "assistant", content: response.content });
      const calls = response.content.filter(
        (block): block is Anthropic.Beta.BetaToolUseBlock =>
          block.type === "tool_use" && block.toolset_name === browser.toolsetName
      );
      if (calls.length === 0) {
        break;
      }
      const results: Anthropic.Beta.BetaToolResultBlockParam[] = [];
      let failed = false;
      for (const call of calls) {
        if (failed) {
          // Dopo una chiamata fallita, il resto del turno riceve risposta ma non viene eseguito.
          results.push({
            type: "tool_result",
            tool_use_id: call.id,
            toolset_name: call.toolset_name,
            content: NOT_EXECUTED,
            is_error: true
          });
          continue;
        }
        const result = await browser.toolResult(call);
        failed = result.is_error === true;
        results.push(result);
      }
      messages.push({ role: "user", content: results });
    }
  } finally {
    await browser.close();
  }
  ```
</CodeGroup>

Ogni risposta a una chiamata saltata contiene `is_error`, il `toolset_name` della chiamata e il testo esatto richiesto da [Azioni batch](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-tool#batch-actions). Il tool runner invia la stessa risposta.

## Esempio avanzato: scrivi un driver personalizzato

Questo esempio riunisce le parti principali di [Implementa un driver](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver) in un unico driver, `MyBrowser`, e lo esegue con il tool runner e l'[avvio anticipato](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#start-calls-while-the-response-streams). `MyBrowser` implementa `navigate`, `screenshot` e `left_click`, oltre a `_browser_state` (typescript: `browserState`), il report di stato di cui ogni driver ha bisogno.

`backend` rappresenta il tuo wrapper attorno a una libreria di automazione del browser, come Playwright. La policy degli URL è un esempio. [Scrivi una policy degli URL](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy) la spiega.

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  import re
  from urllib.parse import urlsplit

  from anthropic import Anthropic
  from anthropic.tools import ToolError
  from anthropic.tools.browser import (
      BetaAbstractBrowserToolset20260801,
      BetaBrowserNavigateResult,
      BetaBrowserState,
      BetaScreenshotResult,
      BetaToolsetCallContext,
      BetaURLContext,
  )
  from anthropic.types.beta import (
      BetaBrowserLeftClickInput,
      BetaBrowserNavigateInput,
      BetaBrowserScreenshotInput,
      BetaBrowserStateTabEntryParam,
  )


  class MyBrowser(BetaAbstractBrowserToolset20260801):
      def __init__(self, backend, **options):
          super().__init__(**options)
          self.backend = backend

      def _browser_state(self, context: BetaToolsetCallContext) -> BetaBrowserState:
          return BetaBrowserState(
              tabs=[
                  BetaBrowserStateTabEntryParam(
                      tab_id=tab.id,
                      title=tab.title,
                      url=tab.url,
                      active=tab.id == self.backend.active,
                  )
                  for tab in self.backend.tabs()
              ],
              state_changes=self.backend.drain_changes(),
          )

      def navigate(
          self, context: BetaToolsetCallContext, input: BetaBrowserNavigateInput
      ) -> BetaBrowserNavigateResult:
          # input.url è "back", "forward", "reload" o un URL che la tua policy sugli URL
          # ha consentito, così come l'ha scritto Claude. Come is_allowed, backend.goto aggiunge https://
          # a un URL che non inizia con uno schema.
          page = self.backend.goto(input.url, input.tab_id)
          return BetaBrowserNavigateResult(
              url=page.url, status=page.status, title=page.title
          )

      def screenshot(
          self, context: BetaToolsetCallContext, input: BetaBrowserScreenshotInput
      ) -> BetaScreenshotResult:
          data = self.backend.png_base64(input.tab_id)
          return BetaScreenshotResult(data=data, media_type="image/png")

      def left_click(
          self, context: BetaToolsetCallContext, input: BetaBrowserLeftClickInput
      ) -> None:
          # Niente da restituire: Claude legge "Clicked."
          self.backend.click(input.target, input.tab_id)

      def close(self) -> None:
          super().close()  # first, so no call is still using the browser when it closes
          if not self.backend.closed:
              self.backend.close()


  ALLOWED_HOSTS = ("example.com", "iana.org")
  SCHEME_PREFIX = re.compile(r"[a-z][a-z0-9+.-]*:", re.IGNORECASE)


  def is_allowed(url: str) -> bool:
      # Un esempio, non una policy di produzione. Vedi "Write a URL policy".
      if url.lower() == "about:blank":
          return True
      with_scheme = url if SCHEME_PREFIX.match(url) else f"https://{url}"
      # Un browser interpreta "\" come "/" in un URL web.
      try:
          parts = urlsplit(with_scheme.replace("\\", "/"))
      except ValueError:
          return False
      host = parts.hostname or ""
      listed = any(host == name or host.endswith(f".{name}") for name in ALLOWED_HOSTS)
      return parts.scheme in ("http", "https") and listed


  def url_policy(context: BetaURLContext, url: str) -> None:
      if not is_allowed(url):
          raise ToolError(f"blocked: {url} is not on an allowed host")


  client = Anthropic()
  with MyBrowser(backend, url_policy=url_policy) as browser:
      runner = client.beta.messages.tool_runner(
          model="claude-opus-5-5",
          max_tokens=1024,
          tools=[browser],
          messages=[
              {
                  "role": "user",
                  "content": "Open example.com and tell me the page heading.",
              }
          ],
          stream=True,
          run_tools_eagerly=True,  # so a call can start before the response ends
      )
      for stream in runner:
          print(stream.get_final_message())
  ```

  ```typescript TypeScript
  import Anthropic from "@anthropic-ai/sdk";
  import {
    BetaAbstractBrowserToolset20260801,
    type BetaBrowserNavigateResult,
    type BetaBrowserToolsetOptions,
    type BetaScreenshotResult,
    type BetaToolsetCallContext,
    type BetaURLContext,
    ToolError
  } from "@anthropic-ai/sdk/helpers/beta/toolsets";
  import type {
    BetaBrowserLeftClickInput,
    BetaBrowserNavigateInput,
    BetaBrowserScreenshotInput
  } from "@anthropic-ai/sdk/resources/beta";

  class MyBrowser extends BetaAbstractBrowserToolset20260801 {
    constructor(
      private backend: Backend,
      options: Omit<BetaBrowserToolsetOptions, "browserState"> = {}
    ) {
      super({
        ...options,
        // Obbligatorio: ogni scheda aperta e cosa è cambiato dall'ultimo report.
        browserState: () => ({
          tabs: backend.tabs().map((tab) => ({
            tab_id: tab.id,
            title: tab.title,
            url: tab.url,
            active: tab.id === backend.active
          })),
          state_changes: backend.drainChanges()
        })
      });
    }

    protected override async navigate(
      ctx: BetaToolsetCallContext,
      input: BetaBrowserNavigateInput
    ): Promise<BetaBrowserNavigateResult> {
      // input.url è "back", "forward", "reload" o un URL che la tua policy URL
      // ha consentito, così come Claude l'ha scritto. Come isAllowed, backend.goto aggiunge https://
      // a un URL che non inizia con uno schema.
      const page = await this.backend.goto(input.url, input.tab_id);
      return { url: page.url, status: page.status, title: page.title };
    }

    protected override async screenshot(
      ctx: BetaToolsetCallContext,
      input: BetaBrowserScreenshotInput
    ): Promise<BetaScreenshotResult> {
      return { data: await this.backend.pngBase64(input.tab_id), mediaType: "image/png" };
    }

    protected override async left_click(
      ctx: BetaToolsetCallContext,
      input: BetaBrowserLeftClickInput
    ): Promise<void> {
      // Niente da restituire: Claude legge "Clicked."
      await this.backend.click(input.target, input.tab_id);
    }

    override async close(): Promise<void> {
      await super.close(); // first, so no call is still using the browser when it closes
      if (!this.backend.closed) {
        await this.backend.close();
      }
    }
  }

  const ALLOWED_HOSTS = ["example.com", "iana.org"];
  const SCHEME_PREFIX = /^[a-z][a-z0-9+.-]*:/i;

  // Un esempio, non una policy di produzione. Vedi "Write a URL policy".
  function isAllowed(url: string): boolean {
    if (url.toLowerCase() === "about:blank") {
      return true;
    }
    const withScheme = SCHEME_PREFIX.test(url) ? url : `https://${url}`;
    // Un browser interpreta "\" come "/" in un URL web.
    const parsed = URL.parse(withScheme.replaceAll("\\", "/"));
    const host = parsed?.hostname ?? "";
    const listed = ALLOWED_HOSTS.some((name) => host === name || host.endsWith(`.${name}`));
    return (parsed?.protocol === "http:" || parsed?.protocol === "https:") && listed;
  }

  function urlPolicy(ctx: BetaURLContext, url: string): void {
    if (!isAllowed(url)) {
      throw new ToolError(`blocked: ${url} is not on an allowed host`);
    }
  }

  const client = new Anthropic();
  const browser = new MyBrowser(backend, { urlPolicy });
  try {
    const runner = client.beta.messages.toolRunner({
      model: "claude-opus-5-5",
      max_tokens: 1024,
      tools: [browser],
      messages: [{ role: "user", content: "Open example.com and tell me the page heading." }],
      stream: true,
      runToolsEagerly: true // so a call can start before the response ends
    });
    for await (const stream of runner) {
      console.log(await stream.finalMessage());
    }
  } finally {
    await browser.close();
  }
  ```
</CodeGroup>

## Esegui il toolset in sicurezza

La prossima azione di Claude dipende dalle pagine che legge. Una pagina, o un testo iniettato in essa, può tentare di raggiungere servizi interni o di estrarre file dall'host. Può anche tentare di attivare azioni con effetti reali. Prima di eseguire un driver su qualcosa che non sia un browser usa e getta, segui questi sei passaggi:

1. [Scrivi una policy degli URL](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy) che rifiuti ogni URL di `navigate` di cui l'attività non ha bisogno.
2. [Intercetta le richieste nel driver](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#intercept-requests-in-the-driver) e controlla ogni richiesta con le stesse regole.
3. [Applica una policy di uscita al container](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container), in modo che la rete blocchi ciò che il driver non può vedere.
4. [Limita upload e download](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#confine-uploads-and-downloads), oppure lascia gli upload disattivati.
5. [Sottoponi ad approvazione i membri con conseguenze](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#gate-consequential-members) con `confirm`.
6. [Isola l'host del browser](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#isolate-the-browser-host) in un container o VM dedicato per ogni sessione.

L'SDK applica i passaggi 1, 4 e 5 tramite le policy e il callable che passi. I passaggi 2, 3 e 6 spettano al tuo driver e al tuo deployment. Si applicano anche le precauzioni descritte in [Considerazioni sulla sicurezza](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-tool#security-considerations).

<Note>
  I classificatori di "prompt injection" (iniezione di prompt) di Anthropic, descritti in [Considerazioni sulla sicurezza](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-tool#security-considerations), attualmente non vengono eseguiti sulle richieste del toolset del browser inviate tramite Amazon Bedrock.
</Note>

### Scrivi una policy degli URL

<Warning>
  Senza una policy degli URL, l'SDK non controlla alcun URL e l'API non filtra gli URL che Claude apre. Anche con una policy, un link su cui Claude fa clic o un reindirizzamento può portare il browser al loopback (`localhost`, `127.0.0.1`), a indirizzi link-local come l'indirizzo dei metadati cloud `169.254.169.254` e a intervalli di rete privati. Le [regole di egress sul container](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container) possono bloccare gli indirizzi link-local e privati. Le regole applicate all'esterno del container non possono bloccare il loopback.

  L'SDK non controlla lo schema di un URL, quindi il tuo driver deve rifiutare ogni schema che non intende aprire, come `javascript:`, `view-source:`, `data:` e `file:`. La maggior parte dei driver ha bisogno solo di `http` e `https`. La policy di esempio qui sotto consente solo URL `http` e `https`, più `about:blank`.

  Un URL `file:` consente a Claude di leggere file sull'host del browser. Un URL `javascript:` esegue script nella pagina corrente, anche quando `javascript_exec` è disattivato. Le regole di egress non fermano né l'uno né l'altro.
</Warning>

Una policy degli URL è una funzione che passi come `url_policy` (typescript: `urlPolicy`). Sei tu a scrivere la policy e sei responsabile di renderla pronta per la produzione. Prima che il tuo `navigate` venga eseguito, l'SDK chiama la policy con il contesto della chiamata e l'URL così come Claude lo ha scritto.

Non restituire nulla per consentire l'URL. Per rifiutarlo, solleva `ToolError` (lancialo con throw in TypeScript). Claude legge il messaggio dell'errore e `navigate` non viene eseguito. La policy controlla solo l'URL in ogni chiamata `navigate`. Non è un controllo di rete.

Questa è la policy dell'[esempio avanzato](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#quick-start). Consente `about:blank`, oltre a due siti e ai loro sottodomini su `http` e `https`:

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  import re
  from urllib.parse import urlsplit

  from anthropic.tools import ToolError
  from anthropic.tools.browser import BetaURLContext

  ALLOWED_HOSTS = ("example.com", "iana.org")
  SCHEME_PREFIX = re.compile(r"[a-z][a-z0-9+.-]*:", re.IGNORECASE)


  def is_allowed(url: str) -> bool:
      # Un esempio, non una policy di produzione.
      if url.lower() == "about:blank":
          return True
      with_scheme = url if SCHEME_PREFIX.match(url) else f"https://{url}"
      # Un browser interpreta "\" come "/" in un URL web.
      try:
          parts = urlsplit(with_scheme.replace("\\", "/"))
      except ValueError:
          return False
      host = parts.hostname or ""
      listed = any(host == name or host.endswith(f".{name}") for name in ALLOWED_HOSTS)
      return parts.scheme in ("http", "https") and listed


  def url_policy(context: BetaURLContext, url: str) -> None:
      if not is_allowed(url):
          raise ToolError(f"blocked: {url} is not on an allowed host")


  browser = MyBrowser(backend, url_policy=url_policy)
  ```

  ```typescript TypeScript
  import { type BetaURLContext, ToolError } from "@anthropic-ai/sdk/helpers/beta/toolsets";

  const ALLOWED_HOSTS = ["example.com", "iana.org"];
  const SCHEME_PREFIX = /^[a-z][a-z0-9+.-]*:/i;

  // Un esempio, non una policy di produzione.
  function isAllowed(url: string): boolean {
    if (url.toLowerCase() === "about:blank") {
      return true;
    }
    const withScheme = SCHEME_PREFIX.test(url) ? url : `https://${url}`;
    // Un browser interpreta "\" come "/" in un URL web.
    const parsed = URL.parse(withScheme.replaceAll("\\", "/"));
    const host = parsed?.hostname ?? "";
    const listed = ALLOWED_HOSTS.some((name) => host === name || host.endsWith(`.${name}`));
    return (parsed?.protocol === "http:" || parsed?.protocol === "https:") && listed;
  }

  function urlPolicy(ctx: BetaURLContext, url: string): void {
    if (!isAllowed(url)) {
      throw new ToolError(`blocked: ${url} is not on an allowed host`);
    }
  }

  const browser = new MyBrowser(backend, { urlPolicy });
  ```
</CodeGroup>

L'SDK chiama la policy solo per `navigate`, e non per `"back"`, `"forward"` o `"reload"`. Claude può omettere lo schema, come in `example.com`, quindi prima di leggere l'host l'esempio aggiunge `https://` a un URL che non inizia con uno schema. Il tuo `navigate` riceve l'URL così come Claude lo ha scritto. Aggiungi lo schema anche lì con lo stesso test, in modo che il browser apra l'URL controllato dalla tua policy.

L'esempio non è pronto per la produzione. Una policy di produzione richiede molto di più, in ambiti come questi:

* Link su cui Claude fa clic, moduli che invia, reindirizzamenti e richieste che una pagina effettua autonomamente. Solo il tuo driver li vede. Consulta [Intercetta le richieste nel driver](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#intercept-requests-in-the-driver).
* Nomi host e URL provenienti dal contenuto della pagina, che un attaccante può scegliere.
* Analizzare ogni URL come fa il browser, non trovare il nome host con una semplice suddivisione della stringa.
* Schemi URL. L'SDK non li controlla, quindi rifiuta ogni schema che il tuo driver non intende aprire.
* Regole di egress sul container, per ciò che nessun controllo degli URL può vedere. Consulta [Applica una policy di uscita al container](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container).

Se passi `url_policy=None` (typescript: `urlPolicy: null`), l'SDK rifiuta ogni `navigate` verso un URL, quindi un valore `None` (typescript: `null`) letto dalla tua configurazione non può lasciare per errore gli URL non controllati. In TypeScript, `undefined` lascia l'opzione non impostata, come se non la passassi.

### Intercetta le richieste nel driver

La policy degli URL valuta solo l'URL in una chiamata `navigate`. Un link su cui Claude fa clic, un modulo che invia, un reindirizzamento o un'immagine o uno script su una pagina consentita possono comunque raggiungere un host che la policy rifiuterebbe. Usa le [regole di egress](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container) per bloccare quegli host.

Un driver con intercettazione delle richieste può anche controllare quelle richieste nel suo hook delle richieste, la funzione che la tua libreria di automazione chiama prima di ogni richiesta. Chiama la funzione usata dalla tua policy, in modo da scrivere le regole una sola volta:

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  class MyBrowser(BetaAbstractBrowserToolset20260801):
      ...

      # Registrato su un contesto browser Playwright con context.route("**/*", self._guard)
      def _guard(self, route):
          # is_allowed è la funzione di "Scrivi una policy per gli URL"
          if not is_allowed(route.request.url):
              return route.abort("blockedbyclient")
          route.continue_()
  ```

  ```typescript TypeScript
  // Nella classe driver, su un contesto del browser Playwright.
  // isAllowed è la funzione di "Scrivi una policy per gli URL".
  await context.route("**/*", (route) =>
    isAllowed(route.request().url()) ? route.continue() : route.abort("blockedbyclient")
  );
  ```
</CodeGroup>

Un hook delle richieste come questo non vede gli handshake WebSocket, le richieste dei service worker o i passaggi di reindirizzamento. Blocca i service worker. Controlla gli handshake WebSocket e i passaggi di reindirizzamento con un hook che li veda. La policy di esempio rifiuta gli URL `ws://` e `wss://`, quindi cambia `ws://` in `http://` e `wss://` in `https://` prima di passare un URL WebSocket a `is_allowed` (typescript: `isAllowed`).

### Applica una policy di uscita al container

L'intercettazione non può vedere ogni richiesta effettuata dal browser, e la policy degli URL non vede dove si risolve un nome. Una "egress policy" (policy di uscita) applicata dalla rete del container copre entrambi i casi:

* Blocca gli intervalli di indirizzi link-local e privati in IPv4 e IPv6. Ciò include l'indirizzo dei metadati cloud `169.254.169.254`.
* Consenti connessioni in uscita solo verso gli host di cui l'attività ha bisogno. Se le tue regole corrispondono a indirizzi IP, risolvi i nomi host consentiti all'avvio del container.
* Consenti il DNS solo verso il resolver del container.

Le regole applicate all'esterno del container del browser, come una NetworkPolicy di Kubernetes o un firewall cloud, non vedono il loopback al suo interno. Con solo quelle regole, una pagina può raggiungere qualsiasi cosa in ascolto lì, inclusa una porta DevTools, che mostra un elenco delle schede aperte del browser e l'indirizzo che controlla ciascuna di esse.

### Limita upload e download

`file_upload` è disattivato per impostazione predefinita. Senza `file_policy` (typescript: `filePolicy`), l'SDK rifiuta ogni upload che fornisce un percorso o un [ID documento](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-tool#upload-files). Per abilitare gli upload, passa un `BetaLocalFilePolicy` (typescript: `BetaNodeFilePolicy`) con una directory di upload che contenga solo i file dell'attività:

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  from anthropic.tools.browser import BetaLocalFilePolicy

  # Un MyBrowser che implementa anche file_upload
  browser = MyBrowser(
      backend,
      configs={"file_upload": {"enabled": True}},
      confirm=make_confirm(),  # required for file_upload; see Gate consequential members
      url_policy=url_policy,
      file_policy=BetaLocalFilePolicy(
          upload_roots=["/task/uploads"],
          download_dir="/task/downloads",
          expose_download_paths=False,
      ),
  )
  ```

  ```typescript TypeScript
  import { BetaNodeFilePolicy } from "@anthropic-ai/sdk/helpers/beta/toolsets/node";

  // Un MyBrowser che implementa anche file_upload
  const browser = new MyBrowser(backend, {
    configs: { file_upload: { enabled: true } },
    confirm: makeConfirm(), // required for file_upload; see Gate consequential members
    urlPolicy,
    filePolicy: new BetaNodeFilePolicy({
      uploadRoots: ["/task/uploads"],
      downloadDir: "/task/downloads",
      exposeDownloadPaths: false
    })
  });
  ```
</CodeGroup>

L'SDK risolve ogni percorso di upload, seguendo i link simbolici, e rifiuta qualsiasi percorso al di fuori delle radici di upload. La policy dei file solleva un errore di configurazione per una directory di download che è una radice di upload, si trova all'interno di una di esse o ne contiene una. Quel controllo può non rilevare un link simbolico, quindi non permettere che un link simbolico renda la directory di download uguale a una radice di upload, o metta l'una dentro l'altra. L'SDK include il `path` di un download nel report di stato solo quando `expose_download_paths` (typescript: `exposeDownloadPaths`) è true e il file si trova all'interno della directory di download.

La policy dei file fornita controlla i percorsi sul filesystem del processo che esegue l'SDK. Protegge solo un browser che condivide quel filesystem. Per un browser remoto, segui invece [Browser remoti e ospitati](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#remote-and-hosted-browsers).

Configura i download in questo modo:

* Crea tu stesso la directory di download con modalità `0700` e montala con `noexec,nosuid,nodev`.
* Tieni la directory fuori dalla portata di altri strumenti che Claude può chiamare, come una shell o uno strumento per i file.
* In un cambiamento di stato `download_failed`, scrivi `error` come frase fissa. Il testo di un'eccezione può contenere il percorso o l'URL.
* Non leggere un file scaricato nella conversazione, né eseguirlo, finché una persona non decide di farlo.

### Sottoponi ad approvazione i membri con conseguenze

`javascript_exec` e `file_upload` sono disattivati per impostazione predefinita. Se abiliti uno dei due senza un callable `confirm`, il costruttore solleva un errore di configurazione. Con un callable `confirm`, l'SDK lo chiama prima di ogni chiamata che sta per essere eseguita. Senza, non viene chiesto nulla. In TypeScript, `confirm: null` non solleva l'errore di configurazione e l'SDK rifiuta ogni chiamata a qualsiasi membro.

Restituisci `True` (typescript: `true`) per eseguire la chiamata, oppure `False` (typescript: `false`) per rifiutarla. Quando il tuo callable interpella una persona, mostrale il membro, l'URL della pagina e l'input della chiamata. Prima, esegui l'escape di ogni carattere al di fuori dell'ASCII stampabile nell'URL e nell'input, perché entrambi possono contenere testo proveniente dalla pagina.

Questo esempio chiede conferma per i due membri protetti tramite la tua funzione `ask_user` (typescript: `askUser`) e approva il resto:

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  import json
  from collections.abc import Callable
  from urllib.parse import urlsplit

  from anthropic.tools.browser import BetaConfirmContext

  GATED = {"javascript_exec", "file_upload"}


  def shown(value: object) -> str:
      """A value as JSON, with every character outside printable ASCII escaped."""
      return json.dumps(value, ensure_ascii=True, indent=2)


  def make_confirm() -> Callable[[BetaConfirmContext], bool]:
      granted: set[tuple[str, str, str]] = set()

      def confirm(context: BetaConfirmContext) -> bool:
          name = context.member
          if name not in GATED:
              return True
          detail = shown(context.input.to_dict())
          page = context.tab_url
          where = shown(page) if page else "a tab with no URL"
          question = f"Allow {name} on {where}?\n{detail}"
          if page is None or urlsplit(page).scheme not in ("http", "https"):
              return ask_user(question)  # no web origin: ask every time
          # Un'approvazione copre solo questo input esatto su questa pagina.
          key = (name, page, detail)
          if key not in granted and ask_user(question):
              granted.add(key)
          return key in granted

      return confirm


  # Un MyBrowser che implementa anche javascript_exec e file_upload
  # file_upload richiede anche la file_policy da "Confine uploads and downloads"
  browser = MyBrowser(
      backend,
      configs={"javascript_exec": {"enabled": True}, "file_upload": {"enabled": True}},
      confirm=make_confirm(),
      url_policy=url_policy,
  )
  ```

  ```typescript TypeScript
  import type { BetaConfirmContext } from "@anthropic-ai/sdk/helpers/beta/toolsets";

  const GATED = new Set(["javascript_exec", "file_upload"]);

  /** A value as JSON, with every character outside printable ASCII escaped. */
  const shown = (value: unknown): string =>
    JSON.stringify(value, null, 2).replace(
      /[^\n\x20-\x7e]/g,
      (match) => `\\u${match.charCodeAt(0).toString(16).padStart(4, "0")}`
    );

  const makeConfirm = () => {
    const granted = new Set<string>();
    return async (ctx: BetaConfirmContext): Promise<boolean> => {
      const name = ctx.member;
      if (!GATED.has(name)) {
        return true;
      }
      const detail = shown(ctx.input);
      const page = ctx.tabURL;
      const where = page ? shown(page) : "a tab with no URL";
      const question = `Allow ${name} on ${where}?\n${detail}`;
      const scheme = page === undefined ? undefined : URL.parse(page)?.protocol;
      if (scheme !== "http:" && scheme !== "https:") {
        return askUser(question); // no web origin: ask every time
      }
      // Un'approvazione copre solo questo input esatto su questa pagina.
      const key = `${name} ${page} ${detail}`;
      if (!granted.has(key) && (await askUser(question))) {
        granted.add(key);
      }
      return granted.has(key);
    };
  };

  // Un MyBrowser che implementa anche javascript_exec e file_upload
  // file_upload richiede anche il filePolicy da "Confine uploads and downloads"
  const browser = new MyBrowser(backend, {
    configs: { javascript_exec: { enabled: true }, file_upload: { enabled: true } },
    confirm: makeConfirm(),
    urlPolicy
  });
  ```
</CodeGroup>

Ogni chiamata a `make_confirm()` (typescript: `makeConfirm()`) restituisce un callable senza approvazioni. Chiamala una volta per ogni toolset e assegna a ogni utente il proprio toolset.

Un'approvazione copre la pagina così come l'ha mostrata l'ultimo report di stato, e la pagina può cambiare prima che la chiamata venga eseguita. Acquisti, messaggi inviati e termini accettati avvengono tramite membri ordinari come `left_click` e `type`, quindi `confirm` non può individuarli per nome. Per farli approvare da una persona, chiedi conferma anche per quei membri.

Non abilitare `javascript_exec` a meno che le [regole di egress](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container) non limitino le connessioni in uscita del browser agli host di cui l'attività ha bisogno. Uno script può inviare il contenuto della pagina a qualsiasi indirizzo raggiungibile dal browser.

Anche un URL `javascript:` in una chiamata `navigate` esegue script nella pagina corrente, anche quando `javascript_exec` è disattivato. `confirm` vede quella chiamata come un `navigate`, quindi il callable `confirm` dell'esempio la approverebbe. Il tuo driver deve rifiutare gli URL `javascript:`. Nell'esempio, la [policy degli URL](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy) li rifiuta prima che `confirm` venga eseguito.

### Isola l'host del browser

Esegui il browser in un container o VM dedicato e con privilegi minimi per ogni sessione:

* Esegui come utente non root, con un filesystem root di sola lettura dove il browser lo consente.
* Non montare nulla dall'host oltre alle directory di upload e download che hai configurato, se presenti.
* Tieni le credenziali fuori dall'ambiente e parti da un profilo del browser nuovo.
* Non condividere alcun filesystem con altri strumenti che Claude può chiamare.

<Warning>
  Il profilo del browser non deve aver effettuato l'accesso a nessun account i cui dati o azioni non affideresti a Claude. `javascript_exec` viene eseguito con l'autorità della pagina stessa: i suoi cookie, il suo storage e le sue sessioni autenticate.
</Warning>

Esegui il codice che chiama l'API all'esterno del container del browser, perché quel codice contiene la tua chiave API e la conversazione. Il tool runner e `tool_result` (typescript: `toolResult`) eseguono entrambi il toolset nel processo di quel codice, quindi il browser non condivide il filesystem del toolset. Tratta il browser come remoto: si applica [Browser remoti e ospitati](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#remote-and-hosted-browsers).

Tratta tutto ciò che una pagina restituisce come non attendibile, inclusi il testo della pagina, gli screenshot, le voci della console e della rete, i titoli delle schede e i nomi dei download.

### Browser remoti e ospitati

Alcuni browser non condividono un filesystem con il processo che esegue l'SDK. Ne sono esempi un browser in un altro container, uno che raggiungi tramite un URL DevTools e uno fornito da un servizio di browser ospitato. Con questi, la policy degli URL, l'intercettazione delle richieste e `confirm` vengono comunque eseguiti nel tuo processo.

Con un browser ospitato, il provider controlla il traffico in uscita e l'isolamento dell'host. La tua policy di uscita non si applica lì, quindi l'hook delle richieste del driver è il tuo unico controllo sulle richieste del browser. Verifica cosa può raggiungere la rete del browser.

I controlli sui percorsi forniti non proteggono un browser remoto. `BetaLocalFilePolicy` (typescript: `BetaNodeFilePolicy`) controlla i percorsi sul filesystem del processo che esegue l'SDK, mentre il browser legge e scrive sul proprio filesystem.

L'SDK non può rilevare che un browser è remoto. Quindi, per un browser remoto, lascia `file_upload` disattivato a meno che il tuo driver non controlli i percorsi di upload dove viene eseguito il browser, con un proprio `BetaFilePolicy`. Un `BetaFilePolicy` verifica i percorsi e gli ID documento di ogni upload e decide se Claude vede il percorso di un download.

Fai in modo che un browser remoto rifiuti i download, a meno che il suo host non abbia la configurazione dei download descritta in [Limita upload e download](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#confine-uploads-and-downloads).

Tieni la chiave API del provider e l'URL di connessione della sessione, che può contenere una chiave, fuori da log, risultati degli strumenti e testi di errore. Se il provider registra le sessioni, la registrazione è un'ulteriore copia di tutto ciò che Claude ha visto e digitato, e a essa si applicano i termini di conservazione del provider.

## Toolset del computer

`BetaAbstractComputerToolset20260801` è la classe per lo [strumento computer use](https://platform.claude.com/docs/it/agents-and-tools/tool-use/computer-use-tool). Un driver è una sottoclasse con un metodo per ogni strumento, come `screenshot` o `left_click`. Parti dal driver di un partner in [Integrazioni dei partner](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#partner-integrations). Per scriverne uno tuo, il repository claude-quickstarts contiene un [esempio VNC minimale](https://github.com/anthropics/claude-quickstarts/tree/main/computer-toolset) in Python e TypeScript. Controlla un desktop tramite VNC e non è codice di produzione.

La classe accetta `configs`, `confirm` e `tool_configs` (typescript: `toolConfigs`), e li passi come fai per la classe del browser. [Personalizza un driver](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#customize-a-driver), [Gestisci gli errori](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#handle-errors), [Avvia le chiamate mentre la risposta è in streaming](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#start-calls-while-the-response-streams) ed [Esegui senza il tool runner](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#run-without-the-tool-runner) si applicano anche a essa, con queste differenze:

* **Opzioni solo per il browser:** La classe non ha [policy degli URL](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy), [policy dei file](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#confine-uploads-and-downloads) né [report di stato](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#report-browser-state).
* **`configs`:** Ogni strumento che implementi è attivo per impostazione predefinita. Le impostazioni per strumento sono elencate in [Parametri dello strumento](https://platform.claude.com/docs/it/agents-and-tools/tool-use/computer-use-tool#tool-parameters).
* **`confirm`:** Se la tua classe implementa `type`, `key` o `hold_key`, il costruttore solleva un errore di configurazione a meno che tu non passi un callable `confirm` o disattivi quegli strumenti con `configs`. Una classe che sovrascrive `execute` conta come se implementasse tutti e tre.
* **Chiamate saltate:** Dopo una chiamata al computer fallita, il tool runner risponde alle chiamate al computer successive del turno con `Not executed: an earlier computer action in this turn failed.` Le chiamate al browser ricevono un testo diverso. In un ciclo scritto da te, rispondi alle chiamate al computer saltate con quel testo esatto ([Azioni batch](https://platform.claude.com/docs/it/agents-and-tools/tool-use/computer-use-tool#batch-actions)).
* **`execute`:** In TypeScript, un override di `execute` tipizza `name` come `BetaComputerMemberName` e `input` come `BetaComputerMemberInput`. Entrambi provengono da `@anthropic-ai/sdk/resources/beta`.
* **Testo di errore:** L'SDK non nasconde i percorsi locali nel testo di errore di uno strumento del computer o nel testo restituito da uno strumento. Claude li legge entrambi, troncati a 4.096 caratteri. Intercetta le eccezioni nei tuoi strumenti e solleva `ToolError` (lancialo con throw in TypeScript) con un testo tuo.

### Scrivi un driver per il desktop

Questo driver implementa cinque dei 17 strumenti elencati in [Azioni disponibili](https://platform.claude.com/docs/it/agents-and-tools/tool-use/computer-use-tool#available-actions). Uno strumento che non implementi viene inviato all'API come disabilitato, come spiega [Implementa un driver](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver). Nell'esempio, `display` rappresenta il tuo wrapper attorno a ciò che controlla il desktop, come un client VNC o `xdotool`, e `ask_user` (typescript: `askUser`) rappresenta la tua funzione che interpella una persona.

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  from anthropic import Anthropic
  from anthropic.tools.computer import (
      BetaAbstractComputerToolset20260801,
      BetaComputerCursorPositionResult,
      BetaScreenshotResult,
      BetaToolsetCallContext,
  )
  from anthropic.types.beta import (
      BetaComputerCursorPositionInput,
      BetaComputerKeyInput,
      BetaComputerLeftClickInput,
      BetaComputerScreenshotInput,
      BetaComputerTypeInput,
  )


  class MyDesktop(BetaAbstractComputerToolset20260801):
      def __init__(self, display, **options):
          super().__init__(**options)
          self.display = display

      def screenshot(
          self, context: BetaToolsetCallContext, input: BetaComputerScreenshotInput
      ) -> BetaScreenshotResult:
          # Le coordinate di Claude sono posizioni in pixel in questa immagine.
          data = self.display.png_base64()
          return BetaScreenshotResult(data=data, media_type="image/png")

      def cursor_position(
          self, context: BetaToolsetCallContext, input: BetaComputerCursorPositionInput
      ) -> BetaComputerCursorPositionResult:
          x, y = self.display.cursor()
          return BetaComputerCursorPositionResult(x=x, y=y)

      def left_click(
          self, context: BetaToolsetCallContext, input: BetaComputerLeftClickInput
      ) -> None:
          # input.coordinate è [x, y], oppure None per cliccare sul cursore. input.text è un
          # modificatore da tenere premuto, come "shift". Nulla da restituire: Claude legge "Clicked."
          self.display.click(input.coordinate, input.text)

      def type(
          self, context: BetaToolsetCallContext, input: BetaComputerTypeInput
      ) -> None:
          self.display.type(input.text)

      def key(self, context: BetaToolsetCallContext, input: BetaComputerKeyInput) -> None:
          self.display.press(input.text, input.repeat or 1)

      def close(self) -> None:
          super().close()  # first, so no call is still using the desktop when it closes
          self.display.close()


  client = Anthropic()
  with MyDesktop(
      display,
      # obbligatorio per type e key; vedi Run the computer toolset safely
      confirm=lambda context: ask_user(f'Allow the "{context.member}" tool?'),
  ) as desktop:
      runner = client.beta.messages.tool_runner(
          model="claude-opus-5-5",
          max_tokens=1024,
          tools=[desktop],
          messages=[
              {"role": "user", "content": "Open the calculator and compute 17 * 23."}
          ],
      )
      for message in runner:
          print(message)
  ```

  ```typescript TypeScript
  import Anthropic from "@anthropic-ai/sdk";
  import {
    BetaAbstractComputerToolset20260801,
    type BetaComputerCursorPositionResult,
    type BetaComputerToolsetOptions,
    type BetaScreenshotResult,
    type BetaToolsetCallContext
  } from "@anthropic-ai/sdk/helpers/beta/toolsets";
  import type {
    BetaComputerCursorPositionInput,
    BetaComputerKeyInput,
    BetaComputerLeftClickInput,
    BetaComputerScreenshotInput,
    BetaComputerTypeInput
  } from "@anthropic-ai/sdk/resources/beta";

  class MyDesktop extends BetaAbstractComputerToolset20260801 {
    constructor(
      private display: Display,
      options: BetaComputerToolsetOptions = {}
    ) {
      super(options);
    }

    protected override async screenshot(
      ctx: BetaToolsetCallContext,
      input: BetaComputerScreenshotInput
    ): Promise<BetaScreenshotResult> {
      // Le coordinate di Claude sono posizioni in pixel in questa immagine.
      return { data: await this.display.pngBase64(), mediaType: "image/png" };
    }

    protected override async cursor_position(
      ctx: BetaToolsetCallContext,
      input: BetaComputerCursorPositionInput
    ): Promise<BetaComputerCursorPositionResult> {
      const { x, y } = await this.display.cursor();
      return { x, y };
    }

    protected override async left_click(
      ctx: BetaToolsetCallContext,
      input: BetaComputerLeftClickInput
    ): Promise<void> {
      // input.coordinate è [x, y], oppure null o undefined per cliccare sul cursore. input.text
      // è un modificatore da tenere premuto, come "shift". Nulla da restituire: Claude legge "Clicked."
      await this.display.click(input.coordinate, input.text);
    }

    // Lo strumento type si scrive type_ in TypeScript.
    protected override async type_(
      ctx: BetaToolsetCallContext,
      input: BetaComputerTypeInput
    ): Promise<void> {
      await this.display.type(input.text);
    }

    protected override async key(
      ctx: BetaToolsetCallContext,
      input: BetaComputerKeyInput
    ): Promise<void> {
      await this.display.press(input.text, input.repeat ?? 1);
    }

    override async close(): Promise<void> {
      await super.close(); // first, so no call is still using the desktop when it closes
      await this.display.close();
    }
  }

  const client = new Anthropic();
  const desktop = new MyDesktop(display, {
    // obbligatorio per type e key; vedi Esegui il set di strumenti computer in sicurezza
    confirm: (ctx) => askUser(`Allow the "${ctx.member}" tool?`)
  });
  try {
    const runner = client.beta.messages.toolRunner({
      model: "claude-opus-5-5",
      max_tokens: 1024,
      tools: [desktop],
      messages: [{ role: "user", content: "Open the calculator and compute 17 * 23." }]
    });
    for await (const message of runner) {
      console.log(message);
    }
  } finally {
    await desktop.close();
  }
  ```
</CodeGroup>

<Warning>
  Il `confirm` in questo esempio interpella una persona prima di ogni chiamata eseguita dal driver. Prima di eseguire il driver su qualcosa che non sia una macchina usa e getta, leggi [Esegui il toolset del computer in sicurezza](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#run-the-computer-toolset-safely).
</Warning>

Quattro strumenti non hanno campi di input: `screenshot`, `cursor_position`, `left_mouse_down` e `left_mouse_up`. Ogni tipo di input, inclusi i loro, proviene da `anthropic.types.beta` (typescript: `@anthropic-ai/sdk/resources/beta`).

In TypeScript, scrivi gli strumenti come metodi, non come campi con arrow function. [Implementa un driver](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver) ne spiega il motivo.

### Scala le coordinate e dimensiona le immagini

Ogni punto che Claude invia è una posizione in pixel negli screenshot che restituisci: ogni `coordinate` e `start_coordinate`, ed entrambi gli angoli di una `region` di `zoom`. L'SDK li passa ai tuoi strumenti senza modifiche.

Se i tuoi screenshot hanno una dimensione diversa da quella del display, esegui la conversione in entrambe le direzioni nel tuo driver: i punti di Claude in pixel del display, e la posizione del cursore che riporti in pixel dello screenshot. Mantieni la stessa dimensione dello screenshot per l'intera sessione e assegnale le proporzioni del display:

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  from anthropic.tools import ToolError

  SCREENSHOT_SIZE = (1280, 720)  # the size of every full screenshot the driver returns
  DISPLAY_SIZE = (2560, 1440)


  def to_display(coordinate: list[int]) -> tuple[int, int]:
      x, y = coordinate
      width, height = SCREENSHOT_SIZE
      if not (0 <= x < width and 0 <= y < height):
          # Rifiuta il punto. Spostarlo nell'intervallo farebbe clic dove Claude non ha scelto.
          raise ToolError(f"[{x}, {y}] is outside the {width}x{height} screenshot")
      return x * DISPLAY_SIZE[0] // width, y * DISPLAY_SIZE[1] // height


  def to_screenshot(x: int, y: int) -> tuple[int, int]:
      width, height = DISPLAY_SIZE
      return x * SCREENSHOT_SIZE[0] // width, y * SCREENSHOT_SIZE[1] // height


  to_display([640, 360])  # (1280, 720)
  ```

  ```typescript TypeScript
  import { ToolError } from "@anthropic-ai/sdk/helpers/beta/toolsets";

  const SCREENSHOT_SIZE = { width: 1280, height: 720 }; // the size of every full screenshot
  const DISPLAY_SIZE = { width: 2560, height: 1440 };

  function toDisplay(coordinate: number[]): [number, number] {
    const [x, y] = coordinate;
    const { width, height } = SCREENSHOT_SIZE;
    if (x < 0 || x >= width || y < 0 || y >= height) {
      // Rifiuta il punto. Spostarlo nell'intervallo farebbe cliccare dove Claude non ha scelto.
      throw new ToolError(`[${x}, ${y}] is outside the ${width}x${height} screenshot`);
    }
    return [
      Math.floor((x * DISPLAY_SIZE.width) / width),
      Math.floor((y * DISPLAY_SIZE.height) / height)
    ];
  }

  function toScreenshot(x: number, y: number): [number, number] {
    return [
      Math.floor((x * SCREENSHOT_SIZE.width) / DISPLAY_SIZE.width),
      Math.floor((y * SCREENSHOT_SIZE.height) / DISPLAY_SIZE.height)
    ];
  }

  toDisplay([640, 360]); // [1280, 720]
  ```
</CodeGroup>

Chiama `to_display` (typescript: `toDisplay`) su ogni `coordinate` e `start_coordinate` prima di agire, e `to_screenshot` (typescript: `toScreenshot`) sulla posizione riportata da `cursor_position`. Un clic o uno scorrimento senza `coordinate` agisce sul cursore, quindi non c'è nulla da convertire. Scala una `region` di `zoom` dello stesso fattore. Un'immagine di `zoom` non cambia questo comportamento: i punti successivi che Claude invia sono comunque posizioni in pixel nello screenshot completo.

L'SDK non legge né ridimensiona mai un'immagine. L'API rifiuta un'immagine che supera i limiti del modello, il che termina l'esecuzione. Scala ogni screenshot e ogni immagine di `zoom` nel driver. Consulta [Dimensiona gli screenshot per rispettare i limiti delle immagini](https://platform.claude.com/docs/it/agents-and-tools/tool-use/computer-use-tool#handle-coordinate-scaling-for-higher-resolutions). In un'esecuzione lunga, una richiesta può contenere più di 20 immagini, e in tal caso l'API applica a ciascuna un limite più rigoroso. Consulta [Gestisci la cronologia degli screenshot](https://platform.claude.com/docs/it/agents-and-tools/tool-use/computer-use-tool#manage-screenshot-history).

### Controlli dell'input e risultati

Non affidarti all'SDK per controllare l'input di una chiamata. Fai in modo che il tuo driver controlli i campi che utilizza e rifiuti una chiamata che non può eseguire: solleva `ToolError` (lancialo con throw in TypeScript). Claude legge il messaggio e può inviare una chiamata corretta.

Nessuno dei due SDK limita una `duration`, un conteggio `repeat` o uno `scroll_amount`. Il passaggio 4 in [Esegui il toolset del computer in sicurezza](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#run-the-computer-toolset-safely) li tratta.

Ciò che uno strumento restituisce determina ciò che Claude legge:

| Strumento            | Restituisce                        | Claude legge                                                                                      |
| -------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------- |
| `screenshot`, `zoom` | `BetaScreenshotResult`             | Un blocco immagine                                                                                |
| `cursor_position`    | `BetaComputerCursorPositionResult` | `X={x},Y={y}`                                                                                     |
| Ogni altro strumento | Niente, oppure una riga di testo   | Una breve conferma, come `Clicked.`, seguita dalla riga restituita in un blocco di testo separato |

### Esegui il toolset del computer in sicurezza

Ciò che è sullo schermo orienta la prossima azione di Claude. Una pagina o una notifica può tentare di far digitare Claude nella finestra sbagliata, o di attivare un'azione con effetti reali.

<Warning>
  Un driver che implementa `type`, `key` o `hold_key` necessita di un callable `confirm`. Senza di esso, il costruttore solleva un errore di configurazione, a meno che `configs` non disattivi quegli strumenti. L'SDK stesso non chiede a nessuno: esegue ogni chiamata approvata dal tuo `confirm` e, senza `confirm`, esegue ogni chiamata, incluso ogni clic. Ogni strumento del computer che implementi è attivo per impostazione predefinita.
</Warning>

Prima di eseguire un driver su qualcosa che non sia una macchina usa e getta, segui questi cinque passaggi:

1. Isola il desktop. Eseguilo in un container o in una VM dedicati e con privilegi minimi per ogni sessione, senza credenziali e senza mount dell'host. Installa solo le applicazioni necessarie per l'attività. Esegui il codice che chiama l'API al di fuori di esso.
2. Sottoponi a `confirm` le azioni che hanno effetti reali. Un acquisto o un messaggio inviato è un normale `left_click`, `type` o `key`, e `confirm` riceve lo strumento e il suo input, non lo schermo. Quindi scegli quali strumenti richiedono approvazione in base alle applicazioni che Claude può raggiungere sul desktop.
3. Tieni terminali, finestre di esecuzione e launcher fuori dal desktop, a meno che l'attività non ne richieda uno. Quando uno di essi ha il focus della tastiera, `type`, `key` e `hold_key` eseguono qualsiasi cosa Claude digiti, e un clic può avviare un programma. Se l'attività ne richiede uno, chiedi a una persona prima dell'input da tastiera e dei clic, e isola il desktop in modo che un comando non possa spingersi oltre quanto richiesto dall'attività.
4. Rifiuta l'input che il tuo desktop non dovrebbe accettare, come un punto al di fuori dello screenshot, un `wait` di diversi minuti, o un `repeat` o uno `scroll_amount` molto grandi. Solleva `ToolError` (lancialo con throw in TypeScript) invece di riportare il valore nell'intervallo consentito.
5. Considera tutto ciò che è sullo schermo come non attendibile, inclusi i titoli delle finestre e il testo degli appunti. Non eseguirlo né trasmetterlo senza controlli.

L'SDK applica il passaggio 2 tramite il callable che passi. Gli altri passaggi spettano al tuo driver e al tuo deployment. Si applicano anche le precauzioni descritte in [Considerazioni sulla sicurezza](https://platform.claude.com/docs/it/agents-and-tools/tool-use/computer-use-tool#security-considerations).

Il seguente `confirm` chiede a una persona prima di qualsiasi input da tastiera, tramite la tua funzione `ask_user` (typescript: `askUser`), e approva ogni altra chiamata. `context.member` (typescript: `ctx.member`) è il nome dello strumento. Per far approvare a una persona anche i clic, imposta `ASK_FIRST` su `KEYBOARD | CLICKS` (typescript: `new Set([...KEYBOARD, ...CLICKS])`).

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  from anthropic.tools.computer import BetaComputerConfirmContext

  KEYBOARD = {"type", "key", "hold_key"}
  CLICKS = {
      "left_click",
      "right_click",
      "middle_click",
      "double_click",
      "triple_click",
      "left_click_drag",
      "left_mouse_down",
      "left_mouse_up",
  }
  ASK_FIRST = KEYBOARD


  def confirm(context: BetaComputerConfirmContext) -> bool:
      if context.member not in ASK_FIRST:
          return True
      # la funzione mostrata è quella di "Gate consequential members".
      detail = shown(context.input.to_dict())
      return ask_user(f'Allow the "{context.member}" tool with this input?\n{detail}')


  desktop = MyDesktop(display, confirm=confirm)
  ```

  ```typescript TypeScript
  import type { BetaComputerConfirmContext } from "@anthropic-ai/sdk/helpers/beta/toolsets";

  const KEYBOARD = ["type", "key", "hold_key"];
  const CLICKS = [
    "left_click",
    "right_click",
    "middle_click",
    "double_click",
    "triple_click",
    "left_click_drag",
    "left_mouse_down",
    "left_mouse_up"
  ];
  const ASK_FIRST = new Set(KEYBOARD);

  async function confirm(ctx: BetaComputerConfirmContext): Promise<boolean> {
    if (!ASK_FIRST.has(ctx.member)) {
      return true;
    }
    // la funzione mostrata è quella di "Gate consequential members".
    return askUser(`Allow the "${ctx.member}" tool with this input?\n${shown(ctx.input)}`);
  }

  const desktop = new MyDesktop(display, { confirm });
  ```
</CodeGroup>

Una chiamata rifiutata riceve un risultato di errore e l'esecuzione continua. Per una chiamata `type` rifiutata, Claude legge `The user did not grant permission to run 'type'. Do not retry it unless the user asks you to.`

Questo `confirm` approva ogni strumento che non è in `ASK_FIRST`, incluso qualsiasi strumento che aggiungerai in seguito. Per un comportamento fail-closed, approva solo gli strumenti senza effetti sul desktop (`screenshot`, `zoom`, `cursor_position` e `wait`) e chiedi conferma per ogni altro strumento.

### Usa entrambi i toolset in una sola richiesta

Passa entrambe le istanze in `tools`. Ogni chiamata riporta il proprio `toolset_name`, `browser` o `computer`, e il tool runner invia la chiamata all'istanza corrispondente. Dopo una chiamata non riuscita, il tool runner salta solo le chiamate successive del turno allo stesso toolset, quindi una chiamata al computer non riuscita non fa saltare le chiamate al browser del turno.

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  # MyBrowser, backend e url_policy provengono dall'esempio avanzato.
  # confirm è la funzione di "Esegui il set di strumenti computer in sicurezza".
  with (
      MyBrowser(backend, url_policy=url_policy) as browser,
      MyDesktop(display, confirm=confirm) as desktop,
  ):
      runner = client.beta.messages.tool_runner(
          model="claude-opus-5-5",
          max_tokens=1024,
          tools=[browser, desktop],
          messages=[
              {
                  "role": "user",
                  "content": "Copy the heading on example.com into the open text editor.",
              }
          ],
          stream=True,
          run_tools_eagerly=True,  # so a call can start before the response ends
      )
      for stream in runner:
          print(stream.get_final_message())
  ```

  ```typescript TypeScript
  // MyBrowser, backend e urlPolicy provengono dall'esempio avanzato.
  // confirm è la funzione di "Esegui il set di strumenti computer in sicurezza".
  const browser = new MyBrowser(backend, { urlPolicy });
  const desktop = new MyDesktop(display, { confirm });
  try {
    const runner = client.beta.messages.toolRunner({
      model: "claude-opus-5-5",
      max_tokens: 1024,
      tools: [browser, desktop],
      messages: [
        { role: "user", content: "Copy the heading on example.com into the open text editor." }
      ],
      stream: true,
      runToolsEagerly: true // so a call can start before the response ends
    });
    for await (const stream of runner) {
      console.log(await stream.finalMessage());
    }
  } finally {
    await Promise.all([browser.close(), desktop.close()]);
  }
  ```
</CodeGroup>

In un loop scritto da te, rispondi a ogni chiamata con `tool_result` (typescript: `toolResult`) sull'istanza il cui `toolset_name` (typescript: `toolsetName`) corrisponde al `toolset_name` della chiamata. In ogni turno, tieni traccia delle chiamate non riuscite separatamente per ciascun toolset, e rispondi a una chiamata saltata con il testo specifico di quel toolset. [Esegui senza il tool runner](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#run-without-the-tool-runner) riporta il testo del browser, e il punto elenco sulle chiamate saltate all'inizio di questa sezione riporta quello del computer.

## Riferimento

Le opzioni del costruttore hanno lo stesso significato in entrambi gli SDK:

| Python                     | TypeScript     | Imposta                                                                         |
| -------------------------- | -------------- | ------------------------------------------------------------------------------- |
| `configs`                  | `configs`      | Quali membri sono abilitati                                                     |
| `confirm`                  | `confirm`      | Il callable che approva o rifiuta ogni chiamata                                 |
| `url_policy`               | `urlPolicy`    | La tua policy degli URL, che l'SDK chiama prima di ogni `navigate` verso un URL |
| `file_policy`              | `filePolicy`   | Le radici di upload e l'esposizione del percorso di download                    |
| `tool_configs`             | `toolConfigs`  | I campi per la voce `tools`, come `cache_control`                               |
| Il metodo `_browser_state` | `browserState` | Il report di stato                                                              |

La classe del browser accetta tutte le opzioni della tabella. La classe del computer accetta `configs`, `confirm` e `tool_configs` (typescript: `toolConfigs`).

Non puoi modificare un'opzione dopo la costruzione. La [guida al toolset del browser per Python](https://github.com/anthropics/anthropic-sdk-python/blob/main/browser-toolset.md), la [guida al toolset del browser per TypeScript](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/browser-toolset.md), la [guida al toolset del computer per Python](https://github.com/anthropics/anthropic-sdk-python/blob/main/computer-toolset.md) e la [guida al toolset del computer per TypeScript](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/computer-toolset.md) descrivono ciascuna opzione. Le guide Python trattano anche le classi asincrone, `BetaAsyncAbstractBrowserToolset20260801` e `BetaAsyncAbstractComputerToolset20260801`.

## Limitazioni

* **L'SDK non include un browser, un desktop, un driver pronto all'uso o una policy degli URL:** Li fornisci tu. Consulta [Integrazioni dei partner](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#partner-integrations), [Implementa un driver](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver) e [Scrivi una policy degli URL](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy).
* **La policy degli URL vede solo le chiamate `navigate`:** Non vede le richieste effettuate da una pagina, né le pagine che si aprono in qualsiasi altro modo, ad esempio da un link su cui Claude fa clic o da un reindirizzamento. Consulta [Intercetta le richieste nel driver](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#intercept-requests-in-the-driver).
* **Un'approvazione si basa sull'ultimo report di stato:** La pagina può cambiare dopo quel report. L'SDK non controlla di nuovo la pagina prima che la chiamata venga eseguita.
* **Le chiamate su un toolset vengono eseguite una alla volta:** Non puoi disattivare questo comportamento.
* **L'SDK non controlla che gli ID delle schede siano univoci né quante schede ci siano, e non controlla sempre che una scheda sia attiva:** L'API rifiuta un report che viola queste regole.
* **Per una chiamata al computer, `confirm` riceve lo strumento e il suo input, non lo schermo:** Da questi non puoi capire cosa fa un clic in un determinato punto.
* **L'SDK non ridimensiona le immagini di un toolset del computer:** L'API rifiuta un'immagine che supera i limiti del modello. Consulta [Scala le coordinate e dimensiona le immagini](https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-sdk#scale-coordinates-and-size-images).

## Passaggi successivi

<CardGroup cols={2}>
  <Card title="Strumento browser use" icon="browser" href="https://platform.claude.com/docs/it/agents-and-tools/tool-use/browser-use-tool">
    Gli strumenti membri, il blocco `browser_state` e le considerazioni sulla sicurezza dello strumento.
  </Card>

  <Card title="Strumento computer use" icon="computer" href="https://platform.claude.com/docs/it/agents-and-tools/tool-use/computer-use-tool">
    I 17 strumenti, le coordinate e i limiti delle immagini, e le considerazioni sulla sicurezza dello strumento.
  </Card>

  <Card title="Tool runner (SDK)" icon="arrows-clockwise" href="https://platform.claude.com/docs/it/agents-and-tools/tool-use/tool-runner">
    Come l'SDK esegue il loop e come modificare i messaggi che invia.
  </Card>

  <Card title="Mitigare jailbreak e prompt injection" icon="shield" href="https://platform.claude.com/docs/it/test-and-evaluate/strengthen-guardrails/mitigate-jailbreaks">
    Protezioni per qualsiasi applicazione che legge contenuti non attendibili.
  </Card>
</CardGroup>
