---
title: Uso de navegador e de computador com os toolsets do SDK
url: https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk
description: Execute a ferramenta de uso do navegador ou a ferramenta de uso de computador a partir do SDK Python ou TypeScript. O SDK executa o loop e as verificações que você configura, e você fornece o navegador ou o desktop.
---

Com os toolsets de navegador e de computador nos SDKs Python e TypeScript, você pode criar um agente que trabalha em um navegador web ou em um desktop. Ele pode usar aplicativos que não têm interface programática. Por meio da [ferramenta de uso do navegador](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-tool), Claude navega, lê e interage com páginas web. Por meio da [ferramenta de uso de computador](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/computer-use-tool), Claude vê um desktop em capturas de tela e usa o mouse e o teclado.

O SDK conecta Claude ao seu navegador ou desktop. Ele executa as verificações que você configura, como uma política de URL para `navigate` ou um callback de aprovação. Ele envia cada chamada aprovada ao seu driver e monta o `tool_result` a partir do que o driver retorna. O [tool runner](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/tool-runner) pode executar o loop agêntico para você.

Você fornece o driver, que opera o navegador ou o desktop. Comece pelo [driver de um provedor](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#use-a-driver-from-a-provider). Com um deles, um agente exige poucas linhas de código. Se nenhum servir, escreva o seu próprio com uma biblioteca de automação de navegador, como o Playwright.

A maior parte desta página trata do toolset de navegador. [Toolset de computador](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#computer-toolset) aborda o que é diferente.

Escrever seu próprio driver é um caso avançado, abordado em [Implementar um driver](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver). O repositório claude-quickstarts também tem um [exemplo de driver de navegador](https://github.com/anthropics/claude-quickstarts/tree/main/browser-toolset) em Python e TypeScript. Ele controla o Chromium por meio do Chrome DevTools Protocol (CDP) e não é código de produção.

<Note>
  A classe de toolset de navegador está em beta e disponível no [SDK Python](https://github.com/anthropics/anthropic-sdk-python/blob/main/browser-toolset.md) e no [SDK TypeScript](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/browser-toolset.md). A classe de toolset de computador está em beta nos mesmos SDKs, que têm um [guia do toolset de computador em Python](https://github.com/anthropics/anthropic-sdk-python/blob/main/computer-toolset.md) e um [guia do toolset de computador em TypeScript](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/computer-toolset.md).
</Note>

## Usar um driver de um provedor

Com um driver de um provedor, como um dos listados em [Integrações de parceiros](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#partner-integrations), você pode criar um agente em poucas linhas de código. O provedor deste exemplo é fictício. Substitua `example_browser_provider` (typescript: `example-browser-provider`) e `ExampleBrowser` pelo pacote e pela classe de driver de [um provedor real](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#partner-integrations), e configure-os conforme a documentação do provedor descreve.

<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>

Passe a própria instância do driver como a entrada de `tools`. O runner nunca fecha o toolset, então uma instância pode atender várias execuções. Feche-a quando terminar.

Para escrever seu próprio driver, consulte [Implementar um driver](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver).

## Integrações de parceiros

Browser Use, Browserbase, Daytona e E2B publicam seus próprios drivers para os toolsets do 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 do Browser Use para o toolset de navegador.
  </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 Stagehand do Browserbase para o toolset de navegador.
  </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 do Daytona para o toolset de computador.
  </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">
    Drivers do E2B para os toolsets de navegador e de computador.
  </Card>
</CardGroup>

Nossos parceiros também publicam código no GitHub: o [início rápido do Browser Use](https://github.com/browser-use/browser-use/tree/main/examples/integrations/toolsets-for-claude), o [toolset do Browserbase](https://github.com/browserbase/claude-cua-toolset) e o [exemplo do E2B](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/anthropic-computer-use-orangehrm-js).

<Warning>
  Faça estas duas coisas antes de executar um driver contra qualquer coisa que não seja um navegador descartável:

  * [Escreva uma política de URL](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy). Sem ela, o SDK não verifica nenhuma URL. Para um navegador hospedado por um provedor, leia também [Navegadores remotos e hospedados](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#remote-and-hosted-browsers).
  * Use um perfil de navegador que não esteja conectado a nenhuma conta cujos dados ou ações você não entregaria a Claude. Consulte [Isolar o host do navegador](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#isolate-the-browser-host).

  [Executar o toolset com segurança](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#run-the-toolset-safely) aborda o restante.
</Warning>

## Personalizar um driver

Os exemplos desta seção usam `MyBrowser`, o driver do [exemplo avançado](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#quick-start) mais adiante nesta página.

### Habilitar ou desabilitar membros

`configs` recebe as configurações por membro descritas em [Configure o conjunto de ferramentas](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-tool#configure-the-toolset). Liste apenas os membros que você altera:

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

  ```typescript TypeScript
  // Um MyBrowser que também implementa read_console
  const browser = new MyBrowser(backend, {
    configs: { read_console: { enabled: true }, navigate: { enabled: false } }
  });
  ```
</CodeGroup>

O SDK recusa uma chamada a um membro desabilitado antes que seu código seja executado. Habilitar um membro que sua classe não implementa é um erro de configuração, a menos que a classe sobrescreva `execute`.

### Adicionar hooks antes e depois

Sobrescreva `execute` e chame o `execute` da classe pai. O código antes dessa chamada é executado após a [política de URL](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy), a [política de arquivos](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#confine-uploads-and-downloads) e o [`confirm`](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#gate-consequential-members), e pode alterar a entrada. O SDK não verifica a entrada alterada novamente. O código após a chamada recebe o resultado e pode alterá-lo. Lance `ToolError` (com `throw` em TypeScript) para recusar a chamada.

Sobrescrever `execute` altera quais membros são oferecidos a Claude. O SDK considera todos os membros como implementados, então Claude recebe todos os membros que estão ativados por padrão. O `MyBrowser` do [exemplo avançado](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#quick-start) atende três membros, então o `TracedBrowser` a seguir oferece a Claude membros que ele não consegue atender. Desative esses membros com `configs` antes de usá-lo.

<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>

## Implementar um driver

Um driver é a sua subclasse de `BetaAbstractBrowserToolset20260801`. Nela, sobrescreva os membros que seu navegador suporta. Um membro que você não implementa é enviado à API como desabilitado. Se Claude o chamar mesmo assim, o SDK retorna um erro e a execução continua. Sobrescrever `execute` altera quais membros são enviados como desabilitados ([Adicionar hooks antes e depois](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#add-before-and-after-hooks)). Cada membro recebe o contexto da chamada e a entrada do membro como um objeto tipado, como `BetaBrowserNavigateInput`. Os tipos de entrada vêm de `anthropic.types.beta` (typescript: `@anthropic-ai/sdk/resources/beta`). [Ferramentas membro](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-tool#member-tools) lista os campos de cada entrada.

Em TypeScript, escreva os membros como métodos, não como campos de arrow function, porque o SDK os encontra no protótipo. Escreva o membro `type` como `type_` em TypeScript. Em Python, é `type`.

### Retornar resultados

O que um membro retorna determina o que Claude lê. Um resultado bem-sucedido termina com um bloco `browser_state` construído a partir do seu relatório de estado. Um resultado de erro não carrega nenhum bloco.

| Membro                                                                                  | Retorna                                                                                                        | Claude lê                                                                                       |
| --------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `screenshot`, `zoom`                                                                    | `BetaScreenshotResult`                                                                                         | Um bloco de imagem                                                                              |
| `navigate`                                                                              | `BetaBrowserNavigateResult`                                                                                    | `Navigated to {url} — {title} (HTTP {status})`                                                  |
| `new_tab`, `switch_tab`, `list_tabs`, `close_tab`                                       | Uma entrada de aba (`new_tab`, `switch_tab`), uma lista de entradas de aba (`list_tabs`) ou nada (`close_tab`) | Apenas o bloco `browser_state`                                                                  |
| `read_page`, `get_page_text`, `find`, `read_console`, `read_network`, `javascript_exec` | Uma string                                                                                                     | A string, ou `(empty)` para uma string vazia                                                    |
| Todos os outros membros                                                                 | Nada, ou uma linha de texto                                                                                    | Uma confirmação curta, como `Clicked.`, seguida da linha retornada em um bloco de texto próprio |

### Relatar o estado do navegador

O SDK chama `_browser_state` (typescript: `browserState`) após cada chamada que retorna um resultado, incluindo chamadas recusadas e com falha. Retorne todas as abas abertas e o que mudou desde o último relatório:

* Abas abertas e eventos de download.
* Um `BetaNavigationRefused` para cada navegação que seu [hook de requisição](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#intercept-requests-in-the-driver) bloqueou.
* Um `BetaDialogDismissed` para cada diálogo nativo que seu driver dispensou.

Coloque todos eles em `state_changes`. Em Python, `BetaNavigationRefused()` e `BetaDialogDismissed(kind=..., message=...)` vêm de `anthropic.tools.browser`. Em TypeScript, eles são `{ type: "navigation_refused" }` e `{ type: "dialog_dismissed", kind, message }`.

Os dois últimos não são mudanças de estado da API. O SDK os relata a Claude como texto fora do bloco `browser_state`: uma linha para todas as navegações recusadas, que não inclui uma URL, e uma linha para cada um dos três primeiros diálogos dispensados, seguida de uma contagem dos demais.

Quando qualquer aba estiver aberta, exatamente uma deve estar ativa. Todo membro que recebe um `tab_id` deve agir na aba que ele nomeia. Os limites da API para o relatório estão listados em [Rastreie abas com `browser_state`](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-tool#track-tabs-and-page-state).

Claude lê a URL de cada aba, a URL de cada download e a URL que seu `navigate` retorna, da forma como seu driver as escreveu. O SDK não analisa essas URLs. Ele transforma quebras de linha e outros caracteres de controle em espaços, remove espaços das extremidades e corta cada URL em 4.096 caracteres. Assim, o conteúdo de uma URL `data:`, o caminho de uma URL `file:` e um nome de usuário ou senha em uma URL chegam todos a Claude.

### Tratar erros

| Lançado por um membro ou pelo SDK | Claude lê                                                                                   | A execução |
| --------------------------------- | ------------------------------------------------------------------------------------------- | ---------- |
| `ToolError`                       | Sua mensagem, como um resultado de erro                                                     | Continua   |
| `ToolsetUsageError`               | Nada                                                                                        | Para       |
| Qualquer outra exceção            | `ClassName: message` em Python ou `Error: message` em TypeScript, como um resultado de erro | Continua   |

O SDK lança `ToolsetUsageError` para um erro de configuração, um uso indevido do SDK durante uma chamada ou uma chamada após `close`. Ele também lança um quando `_browser_state` (typescript: `browserState`) lança qualquer exceção, mesmo um `ToolError`. O tool runner e `tool_result` (typescript: `toolResult`) não o capturam, então ele chega a quem os chamou.

O SDK não oculta caminhos locais no texto de erro de um membro, na linha que uma ação como `left_click` retorna, no erro de um download com falha ou na mensagem de um diálogo dispensado.

Um `ToolError` vindo da sua política de URL, da sua política de arquivos ou do callable `confirm` chega a Claude como foi escrito, então deixe caminhos locais fora do texto dele. Capture exceções nos seus membros e lance `ToolError` (com `throw` em TypeScript) com seu próprio texto.

### Iniciar chamadas enquanto a resposta faz streaming

Por padrão, o tool runner executa as chamadas de um turno após o fim da resposta de Claude. Com o início antecipado, ele pode iniciar uma chamada de navegador ou de computador enquanto a resposta ainda está em streaming.

Para ativá-lo, passe `stream=True` (typescript: `stream: true`) e `run_tools_eagerly=True` (typescript: `runToolsEagerly: true`) ao tool runner, como faz o [exemplo avançado](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#quick-start). Com `stream`, cada passagem do seu loop sobre o runner fornece um stream em vez de uma mensagem.

O início antecipado muda quando uma chamada pode começar, não como o toolset a executa:

* **As verificações ainda são executadas primeiro:** O SDK chama sua política de URL, sua política de arquivos e `confirm` antes de uma chamada ser executada. Com o início antecipado, ele pode chamar `confirm` enquanto a resposta ainda está em streaming.
* **As chamadas ainda são executadas uma de cada vez:** Um toolset executa suas chamadas na ordem em que Claude as escreveu. Uma chamada com falha ainda interrompe as chamadas posteriores desse toolset no turno.
* **Uma chamada que já começou não pode ser desfeita:** Se a resposta for então cortada, por exemplo em `max_tokens`, ou se seu loop parar antes, a ação ainda acontece, e Claude nunca lê seu resultado.

### Executar sem o tool runner

Passe a instância em `tools` (`browser.toJSON()` em TypeScript) e responda a cada chamada de membro com `tool_result` (typescript: `toolResult`). A ferramenta de uso do navegador exige que você pare na primeira chamada com falha ([Ações em lote](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-tool#batch-actions)).

Este loop usa `MyBrowser` e a política de URL do [exemplo avançado](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#quick-start). Após uma chamada com falha, ele responde às chamadas posteriores do turno sem executá-las:

<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:
                  # Após uma chamada com falha, o restante do turno é respondido, não executado.
                  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) {
          // Após uma chamada com falha, o restante do turno é respondido, não executado.
          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>

Cada resposta a uma chamada ignorada carrega `is_error`, o `toolset_name` da chamada e o texto exato que [Ações em lote](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-tool#batch-actions) exige. O tool runner envia a mesma resposta.

## Exemplo avançado: escrever seu próprio driver

Este exemplo reúne as principais partes de [Implementar um driver](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver) em um único driver, `MyBrowser`, e o executa com o tool runner e o [início antecipado](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#start-calls-while-the-response-streams). `MyBrowser` implementa `navigate`, `screenshot` e `left_click`, além de `_browser_state` (typescript: `browserState`), o relatório de estado de que todo driver precisa.

`backend` representa seu próprio wrapper em torno de uma biblioteca de automação de navegador, como o Playwright. A política de URL é um exemplo. [Escrever uma política de URL](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy) a explica.

<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" ou uma URL que sua política de URL
          # permitiu, como o Claude a escreveu. Assim como is_allowed, backend.goto adiciona https://
          # a uma URL que não começa com um esquema.
          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:
          # Nada a retornar: o Claude lê "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:
      # Um exemplo, não uma política de produção. Veja "Escreva uma política de URL".
      if url.lower() == "about:blank":
          return True
      with_scheme = url if SCHEME_PREFIX.match(url) else f"https://{url}"
      # Um navegador lê "\" como "/" em uma URL da 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,
        // Obrigatório: todas as abas abertas e o que mudou desde o último relatório.
        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" ou uma URL que sua política de URL
      // permitiu, como o Claude a escreveu. Assim como isAllowed, backend.goto adiciona https://
      // a uma URL que não começa com um esquema.
      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> {
      // Nada a retornar: o Claude lê "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;

  // Um exemplo, não uma política de produção. Veja "Escrever uma política de URL".
  function isAllowed(url: string): boolean {
    if (url.toLowerCase() === "about:blank") {
      return true;
    }
    const withScheme = SCHEME_PREFIX.test(url) ? url : `https://${url}`;
    // Um navegador lê "\" como "/" em uma URL da 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>

## Executar o toolset com segurança

A próxima ação de Claude depende das páginas que ele lê. Uma página, ou um texto injetado nela, pode tentar alcançar serviços internos ou extrair arquivos do host. Também pode tentar acionar ações com efeitos reais. Antes de executar um driver contra qualquer coisa que não seja um navegador descartável, siga estas seis etapas:

1. [Escreva uma política de URL](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy) que recuse toda URL de `navigate` de que a tarefa não precisa.
2. [Intercepte requisições no driver](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#intercept-requests-in-the-driver) e verifique cada requisição com as mesmas regras.
3. [Aplique uma política de "egress" (tráfego de saída) no contêiner](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container), para que a rede bloqueie o que o driver não consegue ver.
4. [Restrinja uploads e downloads](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#confine-uploads-and-downloads), ou deixe os uploads desativados.
5. [Controle membros com consequências](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#gate-consequential-members) com `confirm`.
6. [Isole o host do navegador](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#isolate-the-browser-host) em um contêiner ou VM dedicado para cada sessão.

O SDK aplica os passos 1, 4 e 5 por meio das políticas e do callable que você passa. Os passos 2, 3 e 6 ficam a cargo do seu driver e da sua implantação. As precauções em [Considerações de segurança](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-tool#security-considerations) também se aplicam.

<Note>
  Os classificadores de "prompt injection" (injeção de prompt) da Anthropic, descritos em [Considerações de segurança](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-tool#security-considerations), atualmente não são executados em requisições do toolset de navegador enviadas pelo Amazon Bedrock.
</Note>

### Escrever uma política de URL

<Warning>
  Sem uma política de URL, o SDK não verifica nenhuma URL, e a API não filtra as URLs que Claude abre. Mesmo com uma política, um link em que Claude clica ou um redirecionamento pode levar o navegador ao loopback (`localhost`, `127.0.0.1`), a endereços link-local como o endereço de metadados da nuvem `169.254.169.254` e a faixas de rede privada. [Regras de egress no contêiner](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container) podem bloquear os endereços link-local e privados. Regras aplicadas fora do contêiner não conseguem bloquear o loopback.

  O SDK não verifica o esquema de uma URL, então seu driver deve recusar todo esquema que não pretende abrir, como `javascript:`, `view-source:`, `data:` e `file:`. A maioria dos drivers precisa apenas de `http` e `https`. A política de exemplo abaixo permite apenas URLs `http` e `https`, além de `about:blank`.

  Uma URL `file:` permite que Claude leia arquivos no host do navegador. Uma URL `javascript:` executa script na página atual, mesmo quando `javascript_exec` está desativado. Regras de egress não impedem nenhuma das duas.
</Warning>

Uma política de URL é uma função que você passa como `url_policy` (typescript: `urlPolicy`). Você escreve a política e é responsável por deixá-la pronta para produção. Antes que seu `navigate` seja executado, o SDK chama a política com o contexto da chamada e a URL como Claude a escreveu.

Não retorne nada para permitir a URL. Para recusá-la, lance `ToolError` (com `throw` em TypeScript). Claude lê a mensagem do erro, e `navigate` não é executado. A política verifica apenas a URL em cada chamada de `navigate`. Ela não é um controle de rede.

Esta é a política do [exemplo avançado](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#quick-start). Ela permite `about:blank`, além de dois sites e seus subdomínios via `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:
      # Um exemplo, não uma política de produção.
      if url.lower() == "about:blank":
          return True
      with_scheme = url if SCHEME_PREFIX.match(url) else f"https://{url}"
      # Um navegador lê "\" como "/" em uma URL da 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;

  // Um exemplo, não uma política de produção.
  function isAllowed(url: string): boolean {
    if (url.toLowerCase() === "about:blank") {
      return true;
    }
    const withScheme = SCHEME_PREFIX.test(url) ? url : `https://${url}`;
    // Um navegador lê "\" como "/" em uma URL da 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>

O SDK chama a política apenas para `navigate`, e não para `"back"`, `"forward"` ou `"reload"`. Claude pode omitir o esquema, como em `example.com`, então, antes de ler o host, o exemplo adiciona `https://` a uma URL que não começa com um esquema. Seu `navigate` recebe a URL como Claude a escreveu. Adicione o esquema ali com o mesmo teste, para que o navegador abra a URL que sua política verificou.

O exemplo não é de nível de produção. Uma política de produção precisa de muito mais, em áreas como estas:

* Links em que Claude clica, formulários que ele envia, redirecionamentos e as requisições que uma página faz por conta própria. Apenas seu driver os vê. Consulte [Interceptar requisições no driver](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#intercept-requests-in-the-driver).
* Hostnames e URLs que vêm do conteúdo da página, que um atacante pode escolher.
* Analisar cada URL da mesma forma que o navegador faz, e não encontrar o hostname com uma simples divisão de string.
* Esquemas de URL. O SDK não os verifica, então recuse todo esquema que seu driver não pretende abrir.
* Regras de egress no contêiner, para o que nenhuma verificação de URL consegue ver. Consulte [Aplicar política de egress no contêiner](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container).

Se você passar `url_policy=None` (typescript: `urlPolicy: null`), o SDK recusa todo `navigate` para uma URL, de modo que um `None` (typescript: `null`) lido da sua configuração não pode deixar URLs sem verificação por acidente. Em TypeScript, `undefined` deixa a opção não definida, o mesmo que não passá-la.

### Interceptar requisições no driver

A política de URL avalia apenas a URL em uma chamada de `navigate`. Um link em que Claude clica, um formulário que ele envia, um redirecionamento ou uma imagem ou script em uma página permitida ainda podem alcançar um host que a política recusaria. Use [regras de egress](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container) para bloquear esses hosts.

Um driver com interceptação de requisições também pode verificar essas requisições em seu hook de requisição, a função que sua biblioteca de automação chama antes de cada requisição. Chame a função que sua política usa, para que você escreva as regras uma única vez:

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

      # Registrado em um contexto de navegador do Playwright com context.route("**/*", self._guard)
      def _guard(self, route):
          # is_allowed é a função de "Escreva uma política de URL"
          if not is_allowed(route.request.url):
              return route.abort("blockedbyclient")
          route.continue_()
  ```

  ```typescript TypeScript
  // Na classe do driver, em um contexto de navegador do Playwright.
  // isAllowed é a função de "Escreva uma política de URL".
  await context.route("**/*", (route) =>
    isAllowed(route.request().url()) ? route.continue() : route.abort("blockedbyclient")
  );
  ```
</CodeGroup>

Um hook de requisição como este não vê handshakes de WebSocket, requisições de service worker nem saltos de redirecionamento. Bloqueie service workers. Verifique handshakes de WebSocket e saltos de redirecionamento com um hook que os veja. A política de exemplo recusa URLs `ws://` e `wss://`, então troque `ws://` por `http://` e `wss://` por `https://` antes de passar uma URL de WebSocket para `is_allowed` (typescript: `isAllowed`).

### Aplicar política de egress no contêiner

A interceptação não consegue ver todas as requisições que o navegador faz, e a política de URL não vê para onde um nome resolve. Uma política de egress que a rede do contêiner aplica cobre ambos:

* Bloqueie faixas de endereços link-local e privados em IPv4 e IPv6. Isso inclui o endereço de metadados da nuvem `169.254.169.254`.
* Permita conexões de saída apenas para os hosts de que a tarefa precisa. Se suas regras correspondem a endereços IP, resolva os hostnames que você permite quando o contêiner iniciar.
* Permita DNS apenas para o resolvedor do contêiner.

Regras aplicadas fora do contêiner do navegador, como uma NetworkPolicy do Kubernetes ou um firewall de nuvem, não veem o loopback dentro dele. Com apenas essas regras, uma página pode alcançar qualquer coisa escutando ali, incluindo uma porta do DevTools, que mostra uma lista das abas abertas do navegador e o endereço que controla cada uma.

### Restringir uploads e downloads

`file_upload` está desativado por padrão. Sem `file_policy` (typescript: `filePolicy`), o SDK recusa todo upload que informe um caminho ou um [ID de documento](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-tool#upload-files). Para habilitar uploads, passe um `BetaLocalFilePolicy` (typescript: `BetaNodeFilePolicy`) com um diretório de upload que contenha apenas os arquivos da tarefa:

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

  # Um MyBrowser que também implementa 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";

  // Um MyBrowser que também implementa 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>

O SDK resolve cada caminho de upload, seguindo symlinks, e recusa qualquer caminho fora das raízes de upload. A política de arquivos lança um erro de configuração para um diretório de download que seja uma raiz de upload, esteja dentro de uma ou contenha uma. Essa verificação pode deixar passar um symlink, então não deixe que um symlink torne o diretório de download igual a uma raiz de upload, nem coloque um dentro do outro. O SDK inclui o `path` de um download no relatório de estado apenas quando `expose_download_paths` (typescript: `exposeDownloadPaths`) é verdadeiro e o arquivo está dentro do diretório de download.

A política de arquivos fornecida verifica caminhos no sistema de arquivos do processo que executa o SDK. Ela protege apenas um navegador que compartilha esse sistema de arquivos. Para um navegador remoto, siga [Navegadores remotos e hospedados](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#remote-and-hosted-browsers).

Configure os downloads desta forma:

* Crie você mesmo o diretório de download com o modo `0700` e monte-o com `noexec,nosuid,nodev`.
* Mantenha o diretório fora do alcance de outras ferramentas que Claude pode chamar, como um shell ou uma ferramenta de arquivos.
* Em uma mudança de estado `download_failed`, escreva `error` como uma frase fixa. O texto de uma exceção pode conter o caminho ou a URL.
* Não carregue um arquivo baixado na conversa, nem o execute, até que uma pessoa decida fazê-lo.

### Controlar membros com consequências

`javascript_exec` e `file_upload` estão desativados por padrão. Se você habilitar qualquer um deles sem um callable `confirm`, o construtor lança um erro de configuração. Com um callable `confirm`, o SDK o chama antes de toda chamada que está prestes a ser executada. Sem um, nada é perguntado. Em TypeScript, `confirm: null` não lança o erro de configuração, e o SDK recusa toda chamada a qualquer membro.

Retorne `True` (typescript: `true`) para executar a chamada, ou `False` (typescript: `false`) para recusá-la. Quando seu callable perguntar a uma pessoa, mostre a ela o membro, a URL da página e a entrada da chamada. Primeiro, faça o escape de todo caractere fora do ASCII imprimível na URL e na entrada, porque ambas podem carregar texto da página.

Este exemplo pergunta sobre os dois membros controlados por meio da sua própria função `ask_user` (typescript: `askUser`) e aprova o restante:

<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
          # Uma aprovação cobre apenas esta entrada exata nesta página.
          key = (name, page, detail)
          if key not in granted and ask_user(question):
              granted.add(key)
          return key in granted

      return confirm


  # Um MyBrowser que também implementa javascript_exec e file_upload
  # file_upload também precisa da file_policy de "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
      }
      // Uma aprovação cobre apenas esta entrada exata nesta página.
      const key = `${name} ${page} ${detail}`;
      if (!granted.has(key) && (await askUser(question))) {
        granted.add(key);
      }
      return granted.has(key);
    };
  };

  // Um MyBrowser que também implementa javascript_exec e file_upload
  // file_upload também precisa da filePolicy de "Confine uploads and downloads"
  const browser = new MyBrowser(backend, {
    configs: { javascript_exec: { enabled: true }, file_upload: { enabled: true } },
    confirm: makeConfirm(),
    urlPolicy
  });
  ```
</CodeGroup>

Cada chamada a `make_confirm()` (typescript: `makeConfirm()`) retorna um callable sem aprovações. Chame-a uma vez para cada toolset e dê a cada usuário seu próprio toolset.

Uma aprovação abrange a página como o último relatório de estado a mostrou, e a página pode mudar antes que a chamada seja executada. Compras, mensagens enviadas e termos aceitos acontecem por meio de membros comuns, como `left_click` e `type`, então `confirm` não consegue identificá-los pelo nome. Para que uma pessoa os aprove, pergunte também sobre esses membros.

Não habilite `javascript_exec` a menos que [regras de egress](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container) limitem as conexões de saída do navegador aos hosts de que a tarefa precisa. Um script pode enviar o conteúdo da página para qualquer endereço que o navegador consiga alcançar.

Uma URL `javascript:` em uma chamada de `navigate` também executa script na página atual, mesmo quando `javascript_exec` está desativado. `confirm` vê essa chamada como um `navigate`, então o callable `confirm` do exemplo a aprovaria. Seu driver deve recusar URLs `javascript:`. No exemplo, a [política de URL](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy) as recusa antes que `confirm` seja executado.

### Isolar o host do navegador

Execute o navegador em um contêiner ou VM dedicado e com privilégios mínimos para cada sessão:

* Execute como um usuário não root, com um sistema de arquivos raiz somente leitura onde o navegador permitir.
* Não monte nada do host além dos diretórios de upload e download que você configurou, se houver.
* Mantenha credenciais fora do ambiente e comece a partir de um perfil de navegador novo.
* Não compartilhe nenhum sistema de arquivos com outras ferramentas que Claude pode chamar.

<Warning>
  O perfil do navegador não deve estar conectado a nenhuma conta cujos dados ou ações você não entregaria a Claude. `javascript_exec` é executado com a própria autoridade da página: seus cookies, seu armazenamento e suas sessões conectadas.
</Warning>

Execute o código que chama a API fora do contêiner do navegador, porque esse código guarda sua chave de API e a conversa. O tool runner e `tool_result` (typescript: `toolResult`) executam o toolset no processo desse código, então o navegador não compartilha o sistema de arquivos do toolset. Trate o navegador como remoto: [Navegadores remotos e hospedados](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#remote-and-hosted-browsers) se aplica.

Trate tudo o que uma página retorna como não confiável, incluindo texto da página, capturas de tela, entradas de console e de rede, títulos de abas e nomes de downloads.

### Navegadores remotos e hospedados

Alguns navegadores não compartilham um sistema de arquivos com o processo que executa o SDK. Exemplos são um navegador em outro contêiner, um que você acessa em uma URL do DevTools e um de um serviço de navegador hospedado. Com eles, a política de URL, a interceptação de requisições e `confirm` ainda são executados no seu processo.

Com um navegador hospedado, o provedor controla o egress e o isolamento do host. Sua própria política de egress não se aplica ali, então o hook de requisição do driver é sua única verificação sobre as requisições do navegador. Descubra o que a rede do navegador consegue alcançar.

As verificações de caminho fornecidas não protegem um navegador remoto. `BetaLocalFilePolicy` (typescript: `BetaNodeFilePolicy`) verifica caminhos no sistema de arquivos do processo que executa o SDK, e o navegador lê e grava em seu próprio sistema de arquivos.

O SDK não consegue detectar que um navegador é remoto. Portanto, para um navegador remoto, mantenha `file_upload` desativado, a menos que seu driver verifique os caminhos de upload onde o navegador é executado, com um `BetaFilePolicy` próprio. Um `BetaFilePolicy` avalia os caminhos e IDs de documento de cada upload e decide se Claude vê o caminho de um download.

Faça um navegador remoto recusar downloads, a menos que seu próprio host tenha a configuração de download de [Restringir uploads e downloads](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#confine-uploads-and-downloads).

Mantenha a chave de API do provedor e a URL de conexão da sessão, que pode conter uma chave, fora de logs, resultados de ferramentas e textos de erro. Se o provedor gravar sessões, a gravação é outra cópia de tudo o que Claude viu e digitou, e os termos de retenção do provedor se aplicam a ela.

## Toolset de computador

`BetaAbstractComputerToolset20260801` é a classe para a [ferramenta de uso de computador](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/computer-use-tool). Um driver é uma subclasse com um método por ferramenta, como `screenshot` ou `left_click`. Comece a partir do driver de um parceiro em [Integrações de parceiros](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#partner-integrations). Para escrever o seu próprio, o repositório claude-quickstarts tem um [exemplo mínimo de VNC](https://github.com/anthropics/claude-quickstarts/tree/main/computer-toolset) em Python e TypeScript. Ele controla um desktop via VNC e não é código de produção.

A classe recebe `configs`, `confirm` e `tool_configs` (typescript: `toolConfigs`), e você os passa como faz para a classe de navegador. [Personalizar um driver](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#customize-a-driver), [Tratar erros](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#handle-errors), [Iniciar chamadas enquanto a resposta faz streaming](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#start-calls-while-the-response-streams) e [Executar sem o tool runner](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#run-without-the-tool-runner) também se aplicam a ela, com estas diferenças:

* **Opções exclusivas do navegador:** A classe não tem [política de URL](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy), [política de arquivos](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#confine-uploads-and-downloads) nem [relatório de estado](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#report-browser-state).
* **`configs`:** Toda ferramenta que você implementa está ativada por padrão. As configurações por ferramenta estão listadas em [Parâmetros da ferramenta](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/computer-use-tool#tool-parameters).
* **`confirm`:** Se sua classe implementa `type`, `key` ou `hold_key`, o construtor lança um erro de configuração, a menos que você passe um callable `confirm` ou desative essas ferramentas com `configs`. Uma classe que sobrescreve `execute` conta como implementando as três.
* **Chamadas ignoradas:** Após uma chamada de computador com falha, o tool runner responde às chamadas de computador posteriores do turno com `Not executed: an earlier computer action in this turn failed.` Chamadas de navegador recebem um texto diferente. Em um loop que você escreve, responda às chamadas de computador ignoradas com esse texto exato ([Ações em lote](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/computer-use-tool#batch-actions)).
* **`execute`:** Em TypeScript, uma sobrescrita de `execute` tipa `name` como `BetaComputerMemberName` e `input` como `BetaComputerMemberInput`. Ambos vêm de `@anthropic-ai/sdk/resources/beta`.
* **Texto de erro:** O SDK não oculta caminhos locais no texto de erro de uma ferramenta de computador nem no texto que uma ferramenta retorna. Claude lê ambos, cortados em 4.096 caracteres. Capture exceções nas suas ferramentas e lance `ToolError` (com `throw` em TypeScript) com seu próprio texto.

### Escrever um driver de desktop

Este driver implementa cinco das 17 ferramentas listadas em [Ações disponíveis](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/computer-use-tool#available-actions). Uma ferramenta que você não implementa é enviada à API como desabilitada, como [Implementar um driver](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver) explica. No exemplo, `display` representa seu próprio wrapper em torno do que quer que controle o desktop, como um cliente VNC ou `xdotool`, e `ask_user` (typescript: `askUser`) representa sua própria função que pergunta a uma pessoa.

<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:
          # As coordenadas do Claude são posições em pixels nesta imagem.
          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], ou None para clicar na posição do cursor. input.text é um
          # modificador a manter pressionado, como "shift". Nada a retornar: o Claude lê "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,
      # obrigatório para type e key; veja 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> {
      // As coordenadas do Claude são posições em pixels nesta imagem.
      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], ou null ou undefined para clicar no cursor. input.text
      // é um modificador a manter pressionado, como "shift". Nada a retornar: o Claude lê "Clicked."
      await this.display.click(input.coordinate, input.text);
    }

    // A ferramenta type é escrita como type_ em 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, {
    // obrigatório para type e key; veja Run the computer toolset safely
    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>
  O `confirm` neste exemplo pergunta a uma pessoa antes de toda chamada que o driver executa. Antes de executar o driver contra qualquer coisa que não seja uma máquina descartável, leia [Executar o toolset de computador com segurança](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#run-the-computer-toolset-safely).
</Warning>

Quatro ferramentas não têm campos de entrada: `screenshot`, `cursor_position`, `left_mouse_down` e `left_mouse_up`. Todo tipo de entrada, incluindo os delas, vem de `anthropic.types.beta` (typescript: `@anthropic-ai/sdk/resources/beta`).

Em TypeScript, escreva as ferramentas como métodos, não como campos de arrow function. [Implementar um driver](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver) explica o motivo.

### Dimensionar coordenadas e tamanho das imagens

Cada ponto que Claude envia é uma posição em pixels nas capturas de tela que você retorna: cada `coordinate` e `start_coordinate`, e ambos os cantos de uma `region` de `zoom`. O SDK os repassa às suas ferramentas sem alterações.

Se suas capturas de tela tiverem um tamanho diferente do da tela, converta nas duas direções no seu driver: os pontos de Claude para pixels da tela, e a posição do cursor que você informa para pixels da captura de tela. Mantenha o tamanho da captura de tela o mesmo durante toda a sessão e dê a ela a proporção da tela:

<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):
          # Recuse o ponto. Movê-lo para dentro do intervalo clicaria onde o Claude não escolheu.
          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) {
      // Recuse o ponto. Movê-lo para dentro do intervalo clicaria onde o Claude não escolheu.
      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>

Chame `to_display` (typescript: `toDisplay`) em cada `coordinate` e `start_coordinate` antes de agir sobre eles, e `to_screenshot` (typescript: `toScreenshot`) na posição que `cursor_position` informa. Um clique ou rolagem sem `coordinate` age na posição do cursor, então não há nada a converter. Dimensione uma `region` de `zoom` pelo mesmo fator. Uma imagem de `zoom` não muda isso: os próximos pontos que Claude envia ainda são posições em pixels na captura de tela completa.

O SDK nunca lê nem redimensiona uma imagem. A API rejeita uma imagem acima dos limites do modelo, o que encerra a execução. Dimensione cada captura de tela e imagem de `zoom` no driver. Consulte [Dimensione as capturas de tela para caber nos limites de imagem](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/computer-use-tool#handle-coordinate-scaling-for-higher-resolutions). Em uma execução longa, uma requisição pode conter mais de 20 imagens, e a API então aplica a cada uma um limite mais rigoroso. Consulte [Gerenciar o histórico de capturas de tela](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/computer-use-tool#manage-screenshot-history).

### Verificações de entrada e resultados

Não dependa do SDK para verificar a entrada de uma chamada. Faça seu driver verificar os campos que usa e recusar uma chamada que não consegue executar: lance `ToolError` (com `throw` em TypeScript). Claude lê a mensagem e pode enviar uma chamada corrigida.

Nenhum dos SDKs limita uma `duration`, uma contagem de `repeat` ou um `scroll_amount`. O passo 4 em [Executar o toolset de computador com segurança](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#run-the-computer-toolset-safely) trata deles.

O que uma ferramenta retorna determina o que Claude lê:

| Ferramenta                  | Retorna                            | Claude lê                                                                                       |
| --------------------------- | ---------------------------------- | ----------------------------------------------------------------------------------------------- |
| `screenshot`, `zoom`        | `BetaScreenshotResult`             | Um bloco de imagem                                                                              |
| `cursor_position`           | `BetaComputerCursorPositionResult` | `X={x},Y={y}`                                                                                   |
| Todas as outras ferramentas | Nada, ou uma linha de texto        | Uma confirmação curta, como `Clicked.`, seguida da linha retornada em um bloco de texto próprio |

### Executar o toolset de computador com segurança

O que está na tela orienta a próxima ação de Claude. Uma página ou uma notificação pode tentar fazer Claude digitar na janela errada ou disparar uma ação com efeitos reais.

<Warning>
  Um driver que implementa `type`, `key` ou `hold_key` precisa de um callable `confirm`. Sem ele, o construtor lança um erro de configuração, a menos que `configs` desative essas ferramentas. O próprio SDK não pergunta a ninguém: ele executa toda chamada que seu `confirm` aprova e, sem `confirm`, executa todas as chamadas, incluindo todos os cliques. Toda ferramenta de computador que você implementa fica ativada por padrão.
</Warning>

Antes de executar um driver em qualquer coisa que não seja uma máquina descartável, siga estes cinco passos:

1. Isole o desktop. Execute-o em um contêiner ou VM dedicado e com privilégios mínimos para cada sessão, sem credenciais e sem montagens do host. Instale apenas os aplicativos de que a tarefa precisa. Execute o código que chama a API fora dele.
2. Controle ações que têm efeitos reais com `confirm`. Uma compra ou uma mensagem enviada é um `left_click`, `type` ou `key` comum, e `confirm` recebe a ferramenta e sua entrada, não a tela. Portanto, escolha quais ferramentas precisam de aprovação com base nos aplicativos que Claude pode alcançar no desktop.
3. Mantenha terminais, caixas de diálogo de execução e inicializadores fora do desktop, a menos que a tarefa precise de um. Quando um deles tem o foco do teclado, `type`, `key` e `hold_key` executam o que quer que Claude digite, e um clique pode iniciar um programa. Se a tarefa precisar de um, pergunte a uma pessoa antes de entradas de teclado e cliques, e isole o desktop para que um comando não possa ir além do que a tarefa precisa.
4. Recuse entradas que seu desktop não deve aceitar, como um ponto fora da captura de tela, um `wait` de vários minutos ou um `repeat` ou `scroll_amount` muito grande. Lance `ToolError` (com `throw` em TypeScript) em vez de ajustar o valor para dentro do intervalo.
5. Trate tudo na tela como não confiável, incluindo títulos de janelas e texto da área de transferência. Não execute nem repasse isso sem verificação.

O SDK aplica o passo 2 por meio do callable que você passa. Os outros passos ficam a cargo do seu driver e da sua implantação. As precauções em [Considerações de segurança](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/computer-use-tool#security-considerations) também se aplicam.

O `confirm` a seguir pergunta a uma pessoa antes de qualquer entrada de teclado, por meio da sua própria função `ask_user` (typescript: `askUser`), e aprova todas as outras chamadas. `context.member` (typescript: `ctx.member`) é o nome da ferramenta. Para que uma pessoa também aprove cliques, defina `ASK_FIRST` como `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
      # a função mostrada é a de "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;
    }
    // a função mostrada é a de "Gate consequential members".
    return askUser(`Allow the "${ctx.member}" tool with this input?\n${shown(ctx.input)}`);
  }

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

Uma chamada recusada recebe um resultado de erro, e a execução continua. Para uma chamada `type` recusada, Claude lê `The user did not grant permission to run 'type'. Do not retry it unless the user asks you to.`

Este `confirm` aprova toda ferramenta que não está em `ASK_FIRST`, incluindo qualquer ferramenta que você adicionar depois. Para falhar de forma fechada, aprove apenas as ferramentas sem efeito no desktop (`screenshot`, `zoom`, `cursor_position` e `wait`) e pergunte sobre todas as outras.

### Usar ambos os toolsets em uma requisição

Passe ambas as instâncias em `tools`. Cada chamada carrega seu `toolset_name`, `browser` ou `computer`, e o tool runner envia a chamada para a instância correspondente. Após uma chamada com falha, o tool runner ignora apenas as chamadas posteriores do turno para o mesmo toolset, de modo que uma chamada de computador com falha não faz ignorar as chamadas de navegador do turno.

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  # MyBrowser, backend e url_policy vêm do exemplo avançado.
  # confirm é a função de "Executar o conjunto de ferramentas de computador com segurança".
  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 vêm do exemplo avançado.
  // confirm é a função de "Executar o conjunto de ferramentas de computador com segurança".
  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>

Em um loop que você escreve, responda a cada chamada com `tool_result` (typescript: `toolResult`) na instância cujo `toolset_name` (typescript: `toolsetName`) corresponde ao `toolset_name` da chamada. Em cada turno, acompanhe as chamadas com falha de cada toolset separadamente e responda a uma chamada ignorada com o texto próprio daquele toolset. [Executar sem o tool runner](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#run-without-the-tool-runner) tem o texto do navegador, e o item Chamadas ignoradas no início desta seção tem o do computador.

## Referência

As opções do construtor têm o mesmo significado em ambos os SDKs:

| Python                    | TypeScript     | Define                                                                     |
| ------------------------- | -------------- | -------------------------------------------------------------------------- |
| `configs`                 | `configs`      | Quais membros estão ativados                                               |
| `confirm`                 | `confirm`      | O callable que aprova ou recusa cada chamada                               |
| `url_policy`              | `urlPolicy`    | Sua política de URL, que o SDK chama antes de cada `navigate` para uma URL |
| `file_policy`             | `filePolicy`   | Raízes de upload e exposição do caminho de download                        |
| `tool_configs`            | `toolConfigs`  | Campos para a entrada de `tools`, como `cache_control`                     |
| O método `_browser_state` | `browserState` | O relatório de estado                                                      |

A classe de navegador aceita todas as opções da tabela. A classe de computador aceita `configs`, `confirm` e `tool_configs` (typescript: `toolConfigs`).

Você não pode alterar uma opção após a construção. O [guia do toolset de navegador em Python](https://github.com/anthropics/anthropic-sdk-python/blob/main/browser-toolset.md), o [guia do toolset de navegador em TypeScript](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/browser-toolset.md), o [guia do toolset de computador em Python](https://github.com/anthropics/anthropic-sdk-python/blob/main/computer-toolset.md) e o [guia do toolset de computador em TypeScript](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/computer-toolset.md) descrevem cada opção. Os guias de Python também abordam as classes assíncronas, `BetaAsyncAbstractBrowserToolset20260801` e `BetaAsyncAbstractComputerToolset20260801`.

## Limitações

* **O SDK não inclui um navegador, um desktop, um driver pronto nem uma política de URL:** Você os fornece. Consulte [Integrações de parceiros](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#partner-integrations), [Implementar um driver](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver) e [Escrever uma política de URL](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy).
* **A política de URL vê apenas chamadas `navigate`:** Ela não vê as requisições que uma página faz, nem páginas que se abrem de qualquer outra forma, como a partir de um link em que Claude clica ou de um redirecionamento. Consulte [Interceptar requisições no driver](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#intercept-requests-in-the-driver).
* **Uma aprovação se baseia no último relatório de estado:** A página pode mudar após esse relatório. O SDK não verifica a página novamente antes que a chamada seja executada.
* **Chamadas em um toolset são executadas uma de cada vez:** Você não pode desativar isso.
* **O SDK não verifica se os IDs de abas são únicos nem quantas abas existem, e nem sempre verifica se há uma aba ativa:** A API rejeita um relatório que viole essas regras.
* **Para uma chamada de computador, `confirm` recebe a ferramenta e sua entrada, não a tela:** Não é possível saber a partir delas o que um clique em determinado ponto faz.
* **O SDK não redimensiona as imagens de um toolset de computador:** A API rejeita uma imagem acima dos limites do modelo. Consulte [Dimensionar coordenadas e tamanho das imagens](https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-sdk#scale-coordinates-and-size-images).

## Próximos passos

<CardGroup cols={2}>
  <Card title="Ferramenta de uso do navegador" icon="browser" href="https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/browser-use-tool">
    As ferramentas membro, o bloco `browser_state` e as considerações de segurança da ferramenta.
  </Card>

  <Card title="Ferramenta de uso de computador" icon="computer" href="https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/computer-use-tool">
    As 17 ferramentas, coordenadas e limites de imagem, e as considerações de segurança da ferramenta.
  </Card>

  <Card title="Tool runner (SDK)" icon="arrows-clockwise" href="https://platform.claude.com/docs/pt-BR/agents-and-tools/tool-use/tool-runner">
    Como o SDK executa o loop e como alterar as mensagens que ele envia.
  </Card>

  <Card title="Mitigar jailbreaks e injeções de prompt" icon="shield" href="https://platform.claude.com/docs/pt-BR/test-and-evaluate/strengthen-guardrails/mitigate-jailbreaks">
    Proteções para qualquer aplicação que lê conteúdo não confiável.
  </Card>
</CardGroup>
