Uso de navegador e de computador com os toolsets do SDK
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, Claude navega, lê e interage com páginas web. Por meio da ferramenta de uso de computador, 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 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. 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 aborda o que é diferente.
Escrever seu próprio driver é um caso avançado, abordado em Implementar um driver. O repositório claude-quickstarts também tem um exemplo de driver de navegador em Python e TypeScript. Ele controla o Chromium por meio do Chrome DevTools Protocol (CDP) e não é código de produção.
Usar um driver de um provedor
Com um driver de um provedor, como um dos listados em Integrações de parceiros, você pode criar um agente em poucas linhas de código. O provedor deste exemplo é fictício. Substitua example_browser_provider e ExampleBrowser pelo pacote e pela classe de driver de um provedor real, e configure-os conforme a documentação do provedor descreve.
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)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.
Integrações de parceiros
Browser Use, Browserbase, Daytona e E2B publicam seus próprios drivers para os toolsets do SDK.
Driver do Browser Use para o toolset de navegador.
Driver Stagehand do Browserbase para o toolset de navegador.
Driver do Daytona para o toolset de computador.
Drivers do E2B para os toolsets de navegador e de computador.
Nossos parceiros também publicam código no GitHub: o início rápido do Browser Use, o toolset do Browserbase e o exemplo do E2B.
Personalizar um driver
Os exemplos desta seção usam MyBrowser, o driver do exemplo avançado mais adiante nesta página.
Habilitar ou desabilitar membros
configs recebe as configurações por membro descritas em Configure o conjunto de ferramentas. Liste apenas os membros que você altera:
# Um MyBrowser que também implementa read_console
browser = MyBrowser(
backend, configs={"read_console": {"enabled": True}, "navigate": {"enabled": False}}
)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, a política de arquivos e o confirm, 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 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.
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 resultImplementar 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). 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. Ferramentas membro 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 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
BetaNavigationRefusedpara cada navegação que seu hook de requisição bloqueou. - Um
BetaDialogDismissedpara 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.
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 lança qualquer exceção, mesmo um ToolError. O tool runner e tool_result 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 e run_tools_eagerly=True ao tool runner, como faz o exemplo avançado. 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
confirmantes de uma chamada ser executada. Com o início antecipado, ele pode chamarconfirmenquanto 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. A ferramenta de uso do navegador exige que você pare na primeira chamada com falha (Ações em lote).
Este loop usa MyBrowser e a política de URL do exemplo avançado. Após uma chamada com falha, ele responde às chamadas posteriores do turno sem executá-las:
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})Cada resposta a uma chamada ignorada carrega is_error, o toolset_name da chamada e o texto exato que Ações em lote 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 em um único driver, MyBrowser, e o executa com o tool runner e o início antecipado. MyBrowser implementa navigate, screenshot e left_click, além de _browser_state, 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 a explica.
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())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:
- Escreva uma política de URL que recuse toda URL de
navigatede que a tarefa não precisa. - Intercepte requisições no driver e verifique cada requisição com as mesmas regras.
- Aplique uma política de "egress" (tráfego de saída) no contêiner, para que a rede bloqueie o que o driver não consegue ver.
- Restrinja uploads e downloads, ou deixe os uploads desativados.
- Controle membros com consequências com
confirm. - Isole o host do navegador 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 também se aplicam.
Escrever uma política de URL
Uma política de URL é uma função que você passa como url_policy. 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. Ela permite about:blank, além de dois sites e seus subdomínios via http e https:
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)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.
- 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.
Se você passar url_policy=None, o SDK recusa todo navigate para uma URL, de modo que um None 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 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:
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_()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.
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, o SDK recusa todo upload que informe um caminho ou um ID de documento. Para habilitar uploads, passe um BetaLocalFilePolicy com um diretório de upload que contenha apenas os arquivos da tarefa:
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,
),
)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 é 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.
Configure os downloads desta forma:
- Crie você mesmo o diretório de download com o modo
0700e monte-o comnoexec,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, escrevaerrorcomo 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 para executar a chamada, ou 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 e aprova o restante:
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,
)Cada chamada a make_confirm() 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 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 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.
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 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 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 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.
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. 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. Para escrever o seu próprio, o repositório claude-quickstarts tem um exemplo mínimo de VNC 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, e você os passa como faz para a classe de navegador. Personalizar um driver, Tratar erros, Iniciar chamadas enquanto a resposta faz streaming e Executar sem o 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, política de arquivos nem relatório de estado.
configs: Toda ferramenta que você implementa está ativada por padrão. As configurações por ferramenta estão listadas em Parâmetros da ferramenta.confirm: Se sua classe implementatype,keyouhold_key, o construtor lança um erro de configuração, a menos que você passe um callableconfirmou desative essas ferramentas comconfigs. Uma classe que sobrescreveexecuteconta 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). execute: Em TypeScript, uma sobrescrita deexecutetipanamecomoBetaComputerMemberNameeinputcomoBetaComputerMemberInput. 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(comthrowem TypeScript) com seu próprio texto.
Escrever um driver de desktop
Este driver implementa cinco das 17 ferramentas listadas em Ações disponíveis. Uma ferramenta que você não implementa é enviada à API como desabilitada, como Implementar um 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 representa sua própria função que pergunta a uma pessoa.
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)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.
Em TypeScript, escreva as ferramentas como métodos, não como campos de arrow function. Implementar um 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:
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)Chame to_display em cada coordinate e start_coordinate antes de agir sobre eles, e to_screenshot 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. 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.
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 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.
Antes de executar um driver em qualquer coisa que não seja uma máquina descartável, siga estes cinco passos:
- 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.
- Controle ações que têm efeitos reais com
confirm. Uma compra ou uma mensagem enviada é umleft_click,typeoukeycomum, econfirmrecebe 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. - 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,keyehold_keyexecutam 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. - Recuse entradas que seu desktop não deve aceitar, como um ponto fora da captura de tela, um
waitde vários minutos ou umrepeatouscroll_amountmuito grande. LanceToolError(comthrowem TypeScript) em vez de ajustar o valor para dentro do intervalo. - 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 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, e aprova todas as outras chamadas. context.member é o nome da ferramenta. Para que uma pessoa também aprove cliques, defina ASK_FIRST como KEYBOARD | CLICKS.
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)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.
# 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())Em um loop que você escreve, responda a cada chamada com tool_result na instância cujo toolset_name 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 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.
Você não pode alterar uma opção após a construção. O guia do toolset de navegador em Python, o guia do toolset de navegador em TypeScript, o guia do toolset de computador em Python e o guia do toolset de computador em TypeScript 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, Implementar um driver e Escrever uma política de URL.
- 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. - 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,
confirmrecebe 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.
Próximos passos
As ferramentas membro, o bloco browser_state e as considerações de segurança da ferramenta.
As 17 ferramentas, coordenadas e limites de imagem, e as considerações de segurança da ferramenta.
Como o SDK executa o loop e como alterar as mensagens que ele envia.
Proteções para qualquer aplicação que lê conteúdo não confiável.
Was this page helpful?