Uso del browser e del computer con i toolset dell'SDK
Esegui lo strumento di uso del browser o lo strumento di uso del computer dall'SDK Python o TypeScript. L'SDK esegue il ciclo e i controlli che configuri, e tu fornisci il browser o il desktop.
Con i "toolset" (set di strumenti) del browser e del computer negli SDK Python e TypeScript, puoi creare un agente che lavora in un browser web o su un desktop. Può usare applicazioni prive di un'interfaccia programmatica. Tramite lo strumento browser use, Claude naviga, legge e interagisce con le pagine web. Tramite lo strumento computer use, Claude vede un desktop attraverso screenshot e usa il mouse e la tastiera.
L'SDK collega Claude al tuo browser o desktop. Esegue i controlli che configuri, come una policy degli URL per navigate o una callback di approvazione. Invia ogni chiamata che li supera al tuo driver e costruisce il tool_result a partire da ciò che il driver restituisce. Il tool runner può eseguire il ciclo agentico al posto tuo.
Sei tu a fornire il driver, che gestisce il browser o il desktop. Parti dal driver di un provider. Con uno di questi, un agente richiede poche righe di codice. Se nessuno è adatto, scrivine uno tuo con una libreria di automazione del browser come Playwright.
La maggior parte di questa pagina riguarda il toolset del browser. Toolset del computer descrive le differenze.
Scrivere un driver personalizzato è un caso avanzato, trattato in Implementa un driver. Il repository claude-quickstarts contiene anche un driver browser di esempio in Python e TypeScript. Controlla Chromium tramite il Chrome DevTools Protocol (CDP) e non è codice di produzione.
Usa un driver di un provider
Con il driver di un provider, come quelli elencati in Integrazioni dei partner, puoi creare un agente in poche righe di codice. Il provider in questo esempio è inventato. Sostituisci example_browser_provider e ExampleBrowser con il pacchetto e la classe driver di un provider reale, e configurali come descritto nella documentazione del provider.
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)Passa l'istanza stessa del driver come voce di tools. Il runner non chiude mai il toolset, quindi una sola istanza può servire più esecuzioni. Chiudila quando hai finito.
Per scrivere un driver personalizzato, consulta Implementa un driver.
Integrazioni dei partner
Browser Use, Browserbase, Daytona ed E2B pubblicano i propri driver per i toolset dell'SDK.
Driver Browser Use per il toolset del browser.
Driver Browserbase Stagehand per il toolset del browser.
Driver Daytona per il toolset del computer.
Driver E2B per i toolset del browser e del computer.
I nostri partner pubblicano anche codice su GitHub: il quickstart di Browser Use, il toolset di Browserbase e l'esempio di E2B.
Personalizza un driver
Gli esempi in questa sezione usano MyBrowser, il driver dell'esempio avanzato più avanti in questa pagina.
Abilita o disabilita i membri
configs accetta le impostazioni per membro descritte in Configurare il toolset. Elenca solo i membri che modifichi:
# Un MyBrowser che implementa anche read_console
browser = MyBrowser(
backend, configs={"read_console": {"enabled": True}, "navigate": {"enabled": False}}
)L'SDK rifiuta una chiamata a un membro disabilitato prima che il tuo codice venga eseguito. Abilitare un membro che la tua classe non implementa è un errore di configurazione, a meno che la classe non sovrascriva execute.
Aggiungi hook prima e dopo
Sovrascrivi execute e chiama l'execute della classe padre. Il codice prima di quella chiamata viene eseguito dopo la policy degli URL, la policy dei file e confirm, e può modificare l'input. L'SDK non ricontrolla l'input modificato. Il codice dopo la chiamata riceve il risultato e può modificarlo. Solleva ToolError (lancialo con throw in TypeScript) per rifiutare la chiamata.
Sovrascrivere execute cambia i membri offerti a Claude. L'SDK considera implementato ogni membro, quindi a Claude viene offerto ogni membro attivo per impostazione predefinita. Il MyBrowser dell'esempio avanzato serve tre membri, quindi il seguente TracedBrowser offre a Claude membri che non può servire. Disattiva quei membri con configs prima di usarlo.
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 resultImplementa un driver
Un driver è una tua sottoclasse di BetaAbstractBrowserToolset20260801. Al suo interno, sovrascrivi i membri supportati dal tuo browser. Un membro che non implementi viene inviato all'API come disabilitato. Se Claude lo chiama comunque, l'SDK restituisce un errore e l'esecuzione continua. Sovrascrivere execute cambia i membri inviati come disabilitati (Aggiungi hook prima e dopo). Ogni membro riceve il contesto della chiamata e l'input del membro come oggetto tipizzato, ad esempio BetaBrowserNavigateInput. I tipi di input provengono da anthropic.types.beta. Strumenti membro elenca i campi di ciascun input.
In TypeScript, scrivi i membri come metodi, non come campi arrow function, perché l'SDK li trova sul prototipo. In TypeScript, scrivi il membro type come type_. In Python, è type.
Restituisci i risultati
Ciò che un membro restituisce determina ciò che Claude legge. Un risultato riuscito termina con un blocco browser_state costruito dal tuo report di stato. Un risultato di errore non contiene alcun blocco.
| Membro | Restituisce | Claude legge |
|---|---|---|
screenshot, zoom | BetaScreenshotResult | Un blocco immagine |
navigate | BetaBrowserNavigateResult | Navigated to {url} — {title} (HTTP {status}) |
new_tab, switch_tab, list_tabs, close_tab | Una voce di scheda (new_tab, switch_tab), un elenco di voci di scheda (list_tabs) o niente (close_tab) | Solo il blocco browser_state |
read_page, get_page_text, find, read_console, read_network, javascript_exec | Una stringa | La stringa, oppure (empty) per una stringa vuota |
| Ogni altro membro | Niente, o una riga di testo | Una breve conferma, come Clicked., seguita dalla riga restituita in un blocco di testo a sé |
Riporta lo stato del browser
L'SDK chiama _browser_state dopo ogni chiamata che restituisce un risultato, incluse le chiamate rifiutate e fallite. Restituisci ogni scheda aperta e ciò che è cambiato dall'ultimo report:
- Schede aperte ed eventi di download.
- Un
BetaNavigationRefusedper ogni navigazione bloccata dal tuo hook delle richieste. - Un
BetaDialogDismissedper ogni finestra di dialogo nativa chiusa dal tuo driver.
Inserisci tutti questi elementi in state_changes. In Python, BetaNavigationRefused() e BetaDialogDismissed(kind=..., message=...) provengono da anthropic.tools.browser. In TypeScript, sono { type: "navigation_refused" } e { type: "dialog_dismissed", kind, message }.
Gli ultimi due non sono cambiamenti di stato dell'API. L'SDK li riporta a Claude come testo al di fuori del blocco browser_state: una riga per tutte le navigazioni rifiutate, che non include un URL, e una riga per ciascuna delle prime tre finestre di dialogo chiuse, seguita dal conteggio delle eventuali altre.
Quando è aperta almeno una scheda, esattamente una deve essere attiva. Ogni membro che accetta un tab_id deve agire sulla scheda che indica. I limiti dell'API sul report sono elencati in Traccia le schede con browser_state.
Claude legge l'URL di ogni scheda, l'URL di ogni download e l'URL restituito dal tuo navigate, così come li ha scritti il tuo driver. L'SDK non analizza questi URL. Trasforma le interruzioni di riga e gli altri caratteri di controllo in spazi, rimuove gli spazi alle estremità e tronca ogni URL a 4.096 caratteri. Quindi il contenuto di un URL data:, il percorso di un URL file: e un nome utente o una password in un URL arrivano tutti a Claude.
Gestisci gli errori
| Sollevato da un membro o dall'SDK | Claude legge | L'esecuzione |
|---|---|---|
ToolError | Il suo messaggio, come risultato di errore | Continua |
ToolsetUsageError | Niente | Si interrompe |
| Qualsiasi altra eccezione | ClassName: message in Python o Error: message in TypeScript, come risultato di errore | Continua |
L'SDK solleva ToolsetUsageError per un errore di configurazione, un uso improprio dell'SDK durante una chiamata o una chiamata dopo close. Lo solleva anche quando _browser_state solleva una qualsiasi eccezione, anche un ToolError. Il tool runner e tool_result non lo intercettano, quindi raggiunge il loro chiamante.
L'SDK non nasconde i percorsi locali nel testo di errore di un membro, nella riga restituita da un'azione come left_click, nell'errore di un download fallito o nel messaggio di una finestra di dialogo chiusa.
Un ToolError proveniente dalla tua policy degli URL, dalla policy dei file o dal callable confirm raggiunge Claude così come è scritto, quindi escludi i percorsi locali dal suo testo. Intercetta le eccezioni nei tuoi membri e solleva ToolError (lancialo con throw in TypeScript) con un testo tuo.
Avvia le chiamate mentre la risposta è in streaming
Per impostazione predefinita, il tool runner esegue le chiamate di un turno dopo la fine della risposta di Claude. Con l'avvio anticipato, può avviare una chiamata al browser o al computer mentre la risposta è ancora in streaming.
Per attivarlo, passa stream=True e run_tools_eagerly=True al tool runner, come fa l'esempio avanzato. Con stream, ogni passaggio del tuo ciclo sul runner ti fornisce uno stream invece di un messaggio.
L'avvio anticipato cambia quando una chiamata può iniziare, non il modo in cui il toolset la esegue:
- I controlli vengono comunque eseguiti per primi: L'SDK chiama la tua policy degli URL, la tua policy dei file e
confirmprima che una chiamata venga eseguita. Con l'avvio anticipato, può chiamareconfirmmentre la risposta è ancora in streaming. - Le chiamate vengono comunque eseguite una alla volta: Un toolset esegue le sue chiamate nell'ordine in cui Claude le ha scritte. Una chiamata fallita interrompe comunque le chiamate successive di quel toolset nel turno.
- Una chiamata avviata non può essere annullata: Se la risposta viene poi troncata, ad esempio a
max_tokens, o il tuo ciclo si interrompe in anticipo, l'azione avviene comunque e Claude non ne legge mai il risultato.
Esegui senza il tool runner
Passa l'istanza in tools (browser.toJSON() in TypeScript) e rispondi a ogni chiamata di membro con tool_result. Lo strumento browser use richiede di fermarsi alla prima chiamata non riuscita (Azioni batch).
Questo ciclo usa MyBrowser e la policy degli URL dell'esempio avanzato. Dopo una chiamata non riuscita, risponde alle chiamate successive del turno senza eseguirle:
from anthropic.types.beta import BetaMessageParam, BetaToolResultBlockParam
NOT_EXECUTED = "Not executed: an earlier action in this turn failed."
MAX_TURNS = 10
with MyBrowser(backend, url_policy=url_policy) as browser:
messages: list[BetaMessageParam] = [{"role": "user", "content": "Open example.com"}]
for _ in range(MAX_TURNS):
response = client.beta.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
tools=[browser],
messages=messages,
)
messages.append({"role": "assistant", "content": response.content})
calls = [
block
for block in response.content
if block.type == "tool_use" and block.toolset_name == browser.toolset_name
]
if not calls:
break
results: list[BetaToolResultBlockParam] = []
failed = False
for call in calls:
if failed:
# Dopo una chiamata fallita, il resto del turno riceve una risposta, ma non viene eseguito.
results.append(
{
"type": "tool_result",
"tool_use_id": call.id,
"toolset_name": call.toolset_name,
"content": NOT_EXECUTED,
"is_error": True,
}
)
continue
result = browser.tool_result(call)
failed = bool(result.get("is_error"))
results.append(result)
messages.append({"role": "user", "content": results})Ogni risposta a una chiamata saltata contiene is_error, il toolset_name della chiamata e il testo esatto richiesto da Azioni batch. Il tool runner invia la stessa risposta.
Esempio avanzato: scrivi un driver personalizzato
Questo esempio riunisce le parti principali di Implementa un driver in un unico driver, MyBrowser, e lo esegue con il tool runner e l'avvio anticipato. MyBrowser implementa navigate, screenshot e left_click, oltre a _browser_state, il report di stato di cui ogni driver ha bisogno.
backend rappresenta il tuo wrapper attorno a una libreria di automazione del browser, come Playwright. La policy degli URL è un esempio. Scrivi una policy degli URL la spiega.
import re
from urllib.parse import urlsplit
from anthropic import Anthropic
from anthropic.tools import ToolError
from anthropic.tools.browser import (
BetaAbstractBrowserToolset20260801,
BetaBrowserNavigateResult,
BetaBrowserState,
BetaScreenshotResult,
BetaToolsetCallContext,
BetaURLContext,
)
from anthropic.types.beta import (
BetaBrowserLeftClickInput,
BetaBrowserNavigateInput,
BetaBrowserScreenshotInput,
BetaBrowserStateTabEntryParam,
)
class MyBrowser(BetaAbstractBrowserToolset20260801):
def __init__(self, backend, **options):
super().__init__(**options)
self.backend = backend
def _browser_state(self, context: BetaToolsetCallContext) -> BetaBrowserState:
return BetaBrowserState(
tabs=[
BetaBrowserStateTabEntryParam(
tab_id=tab.id,
title=tab.title,
url=tab.url,
active=tab.id == self.backend.active,
)
for tab in self.backend.tabs()
],
state_changes=self.backend.drain_changes(),
)
def navigate(
self, context: BetaToolsetCallContext, input: BetaBrowserNavigateInput
) -> BetaBrowserNavigateResult:
# input.url è "back", "forward", "reload" o un URL che la tua policy sugli URL
# ha consentito, così come l'ha scritto Claude. Come is_allowed, backend.goto aggiunge https://
# a un URL che non inizia con uno schema.
page = self.backend.goto(input.url, input.tab_id)
return BetaBrowserNavigateResult(
url=page.url, status=page.status, title=page.title
)
def screenshot(
self, context: BetaToolsetCallContext, input: BetaBrowserScreenshotInput
) -> BetaScreenshotResult:
data = self.backend.png_base64(input.tab_id)
return BetaScreenshotResult(data=data, media_type="image/png")
def left_click(
self, context: BetaToolsetCallContext, input: BetaBrowserLeftClickInput
) -> None:
# Niente da restituire: Claude legge "Clicked."
self.backend.click(input.target, input.tab_id)
def close(self) -> None:
super().close() # first, so no call is still using the browser when it closes
if not self.backend.closed:
self.backend.close()
ALLOWED_HOSTS = ("example.com", "iana.org")
SCHEME_PREFIX = re.compile(r"[a-z][a-z0-9+.-]*:", re.IGNORECASE)
def is_allowed(url: str) -> bool:
# Un esempio, non una policy di produzione. Vedi "Write a URL policy".
if url.lower() == "about:blank":
return True
with_scheme = url if SCHEME_PREFIX.match(url) else f"https://{url}"
# Un browser interpreta "\" come "/" in un URL web.
try:
parts = urlsplit(with_scheme.replace("\\", "/"))
except ValueError:
return False
host = parts.hostname or ""
listed = any(host == name or host.endswith(f".{name}") for name in ALLOWED_HOSTS)
return parts.scheme in ("http", "https") and listed
def url_policy(context: BetaURLContext, url: str) -> None:
if not is_allowed(url):
raise ToolError(f"blocked: {url} is not on an allowed host")
client = Anthropic()
with MyBrowser(backend, url_policy=url_policy) as browser:
runner = client.beta.messages.tool_runner(
model="claude-opus-5-5",
max_tokens=1024,
tools=[browser],
messages=[
{
"role": "user",
"content": "Open example.com and tell me the page heading.",
}
],
stream=True,
run_tools_eagerly=True, # so a call can start before the response ends
)
for stream in runner:
print(stream.get_final_message())Esegui il toolset in sicurezza
La prossima azione di Claude dipende dalle pagine che legge. Una pagina, o un testo iniettato in essa, può tentare di raggiungere servizi interni o di estrarre file dall'host. Può anche tentare di attivare azioni con effetti reali. Prima di eseguire un driver su qualcosa che non sia un browser usa e getta, segui questi sei passaggi:
- Scrivi una policy degli URL che rifiuti ogni URL di
navigatedi cui l'attività non ha bisogno. - Intercetta le richieste nel driver e controlla ogni richiesta con le stesse regole.
- Applica una policy di uscita al container, in modo che la rete blocchi ciò che il driver non può vedere.
- Limita upload e download, oppure lascia gli upload disattivati.
- Sottoponi ad approvazione i membri con conseguenze con
confirm. - Isola l'host del browser in un container o VM dedicato per ogni sessione.
L'SDK applica i passaggi 1, 4 e 5 tramite le policy e il callable che passi. I passaggi 2, 3 e 6 spettano al tuo driver e al tuo deployment. Si applicano anche le precauzioni descritte in Considerazioni sulla sicurezza.
Scrivi una policy degli URL
Una policy degli URL è una funzione che passi come url_policy. Sei tu a scrivere la policy e sei responsabile di renderla pronta per la produzione. Prima che il tuo navigate venga eseguito, l'SDK chiama la policy con il contesto della chiamata e l'URL così come Claude lo ha scritto.
Non restituire nulla per consentire l'URL. Per rifiutarlo, solleva ToolError (lancialo con throw in TypeScript). Claude legge il messaggio dell'errore e navigate non viene eseguito. La policy controlla solo l'URL in ogni chiamata navigate. Non è un controllo di rete.
Questa è la policy dell'esempio avanzato. Consente about:blank, oltre a due siti e ai loro sottodomini su 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:
# Un esempio, non una policy di produzione.
if url.lower() == "about:blank":
return True
with_scheme = url if SCHEME_PREFIX.match(url) else f"https://{url}"
# Un browser interpreta "\" come "/" in un URL web.
try:
parts = urlsplit(with_scheme.replace("\\", "/"))
except ValueError:
return False
host = parts.hostname or ""
listed = any(host == name or host.endswith(f".{name}") for name in ALLOWED_HOSTS)
return parts.scheme in ("http", "https") and listed
def url_policy(context: BetaURLContext, url: str) -> None:
if not is_allowed(url):
raise ToolError(f"blocked: {url} is not on an allowed host")
browser = MyBrowser(backend, url_policy=url_policy)L'SDK chiama la policy solo per navigate, e non per "back", "forward" o "reload". Claude può omettere lo schema, come in example.com, quindi prima di leggere l'host l'esempio aggiunge https:// a un URL che non inizia con uno schema. Il tuo navigate riceve l'URL così come Claude lo ha scritto. Aggiungi lo schema anche lì con lo stesso test, in modo che il browser apra l'URL controllato dalla tua policy.
L'esempio non è pronto per la produzione. Una policy di produzione richiede molto di più, in ambiti come questi:
- Link su cui Claude fa clic, moduli che invia, reindirizzamenti e richieste che una pagina effettua autonomamente. Solo il tuo driver li vede. Consulta Intercetta le richieste nel driver.
- Nomi host e URL provenienti dal contenuto della pagina, che un attaccante può scegliere.
- Analizzare ogni URL come fa il browser, non trovare il nome host con una semplice suddivisione della stringa.
- Schemi URL. L'SDK non li controlla, quindi rifiuta ogni schema che il tuo driver non intende aprire.
- Regole di egress sul container, per ciò che nessun controllo degli URL può vedere. Consulta Applica una policy di uscita al container.
Se passi url_policy=None, l'SDK rifiuta ogni navigate verso un URL, quindi un valore None letto dalla tua configurazione non può lasciare per errore gli URL non controllati. In TypeScript, undefined lascia l'opzione non impostata, come se non la passassi.
Intercetta le richieste nel driver
La policy degli URL valuta solo l'URL in una chiamata navigate. Un link su cui Claude fa clic, un modulo che invia, un reindirizzamento o un'immagine o uno script su una pagina consentita possono comunque raggiungere un host che la policy rifiuterebbe. Usa le regole di egress per bloccare quegli host.
Un driver con intercettazione delle richieste può anche controllare quelle richieste nel suo hook delle richieste, la funzione che la tua libreria di automazione chiama prima di ogni richiesta. Chiama la funzione usata dalla tua policy, in modo da scrivere le regole una sola volta:
class MyBrowser(BetaAbstractBrowserToolset20260801):
...
# Registrato su un contesto browser Playwright con context.route("**/*", self._guard)
def _guard(self, route):
# is_allowed è la funzione di "Scrivi una policy per gli URL"
if not is_allowed(route.request.url):
return route.abort("blockedbyclient")
route.continue_()Un hook delle richieste come questo non vede gli handshake WebSocket, le richieste dei service worker o i passaggi di reindirizzamento. Blocca i service worker. Controlla gli handshake WebSocket e i passaggi di reindirizzamento con un hook che li veda. La policy di esempio rifiuta gli URL ws:// e wss://, quindi cambia ws:// in http:// e wss:// in https:// prima di passare un URL WebSocket a is_allowed.
Applica una policy di uscita al container
L'intercettazione non può vedere ogni richiesta effettuata dal browser, e la policy degli URL non vede dove si risolve un nome. Una "egress policy" (policy di uscita) applicata dalla rete del container copre entrambi i casi:
- Blocca gli intervalli di indirizzi link-local e privati in IPv4 e IPv6. Ciò include l'indirizzo dei metadati cloud
169.254.169.254. - Consenti connessioni in uscita solo verso gli host di cui l'attività ha bisogno. Se le tue regole corrispondono a indirizzi IP, risolvi i nomi host consentiti all'avvio del container.
- Consenti il DNS solo verso il resolver del container.
Le regole applicate all'esterno del container del browser, come una NetworkPolicy di Kubernetes o un firewall cloud, non vedono il loopback al suo interno. Con solo quelle regole, una pagina può raggiungere qualsiasi cosa in ascolto lì, inclusa una porta DevTools, che mostra un elenco delle schede aperte del browser e l'indirizzo che controlla ciascuna di esse.
Limita upload e download
file_upload è disattivato per impostazione predefinita. Senza file_policy, l'SDK rifiuta ogni upload che fornisce un percorso o un ID documento. Per abilitare gli upload, passa un BetaLocalFilePolicy con una directory di upload che contenga solo i file dell'attività:
from anthropic.tools.browser import BetaLocalFilePolicy
# Un MyBrowser che implementa anche file_upload
browser = MyBrowser(
backend,
configs={"file_upload": {"enabled": True}},
confirm=make_confirm(), # required for file_upload; see Gate consequential members
url_policy=url_policy,
file_policy=BetaLocalFilePolicy(
upload_roots=["/task/uploads"],
download_dir="/task/downloads",
expose_download_paths=False,
),
)L'SDK risolve ogni percorso di upload, seguendo i link simbolici, e rifiuta qualsiasi percorso al di fuori delle radici di upload. La policy dei file solleva un errore di configurazione per una directory di download che è una radice di upload, si trova all'interno di una di esse o ne contiene una. Quel controllo può non rilevare un link simbolico, quindi non permettere che un link simbolico renda la directory di download uguale a una radice di upload, o metta l'una dentro l'altra. L'SDK include il path di un download nel report di stato solo quando expose_download_paths è true e il file si trova all'interno della directory di download.
La policy dei file fornita controlla i percorsi sul filesystem del processo che esegue l'SDK. Protegge solo un browser che condivide quel filesystem. Per un browser remoto, segui invece Browser remoti e ospitati.
Configura i download in questo modo:
- Crea tu stesso la directory di download con modalità
0700e montala connoexec,nosuid,nodev. - Tieni la directory fuori dalla portata di altri strumenti che Claude può chiamare, come una shell o uno strumento per i file.
- In un cambiamento di stato
download_failed, scrivierrorcome frase fissa. Il testo di un'eccezione può contenere il percorso o l'URL. - Non leggere un file scaricato nella conversazione, né eseguirlo, finché una persona non decide di farlo.
Sottoponi ad approvazione i membri con conseguenze
javascript_exec e file_upload sono disattivati per impostazione predefinita. Se abiliti uno dei due senza un callable confirm, il costruttore solleva un errore di configurazione. Con un callable confirm, l'SDK lo chiama prima di ogni chiamata che sta per essere eseguita. Senza, non viene chiesto nulla. In TypeScript, confirm: null non solleva l'errore di configurazione e l'SDK rifiuta ogni chiamata a qualsiasi membro.
Restituisci True per eseguire la chiamata, oppure False per rifiutarla. Quando il tuo callable interpella una persona, mostrale il membro, l'URL della pagina e l'input della chiamata. Prima, esegui l'escape di ogni carattere al di fuori dell'ASCII stampabile nell'URL e nell'input, perché entrambi possono contenere testo proveniente dalla pagina.
Questo esempio chiede conferma per i due membri protetti tramite la tua funzione ask_user e approva il resto:
import json
from collections.abc import Callable
from urllib.parse import urlsplit
from anthropic.tools.browser import BetaConfirmContext
GATED = {"javascript_exec", "file_upload"}
def shown(value: object) -> str:
"""A value as JSON, with every character outside printable ASCII escaped."""
return json.dumps(value, ensure_ascii=True, indent=2)
def make_confirm() -> Callable[[BetaConfirmContext], bool]:
granted: set[tuple[str, str, str]] = set()
def confirm(context: BetaConfirmContext) -> bool:
name = context.member
if name not in GATED:
return True
detail = shown(context.input.to_dict())
page = context.tab_url
where = shown(page) if page else "a tab with no URL"
question = f"Allow {name} on {where}?\n{detail}"
if page is None or urlsplit(page).scheme not in ("http", "https"):
return ask_user(question) # no web origin: ask every time
# Un'approvazione copre solo questo input esatto su questa pagina.
key = (name, page, detail)
if key not in granted and ask_user(question):
granted.add(key)
return key in granted
return confirm
# Un MyBrowser che implementa anche javascript_exec e file_upload
# file_upload richiede anche la file_policy da "Confine uploads and downloads"
browser = MyBrowser(
backend,
configs={"javascript_exec": {"enabled": True}, "file_upload": {"enabled": True}},
confirm=make_confirm(),
url_policy=url_policy,
)Ogni chiamata a make_confirm() restituisce un callable senza approvazioni. Chiamala una volta per ogni toolset e assegna a ogni utente il proprio toolset.
Un'approvazione copre la pagina così come l'ha mostrata l'ultimo report di stato, e la pagina può cambiare prima che la chiamata venga eseguita. Acquisti, messaggi inviati e termini accettati avvengono tramite membri ordinari come left_click e type, quindi confirm non può individuarli per nome. Per farli approvare da una persona, chiedi conferma anche per quei membri.
Non abilitare javascript_exec a meno che le regole di egress non limitino le connessioni in uscita del browser agli host di cui l'attività ha bisogno. Uno script può inviare il contenuto della pagina a qualsiasi indirizzo raggiungibile dal browser.
Anche un URL javascript: in una chiamata navigate esegue script nella pagina corrente, anche quando javascript_exec è disattivato. confirm vede quella chiamata come un navigate, quindi il callable confirm dell'esempio la approverebbe. Il tuo driver deve rifiutare gli URL javascript:. Nell'esempio, la policy degli URL li rifiuta prima che confirm venga eseguito.
Isola l'host del browser
Esegui il browser in un container o VM dedicato e con privilegi minimi per ogni sessione:
- Esegui come utente non root, con un filesystem root di sola lettura dove il browser lo consente.
- Non montare nulla dall'host oltre alle directory di upload e download che hai configurato, se presenti.
- Tieni le credenziali fuori dall'ambiente e parti da un profilo del browser nuovo.
- Non condividere alcun filesystem con altri strumenti che Claude può chiamare.
Esegui il codice che chiama l'API all'esterno del container del browser, perché quel codice contiene la tua chiave API e la conversazione. Il tool runner e tool_result eseguono entrambi il toolset nel processo di quel codice, quindi il browser non condivide il filesystem del toolset. Tratta il browser come remoto: si applica Browser remoti e ospitati.
Tratta tutto ciò che una pagina restituisce come non attendibile, inclusi il testo della pagina, gli screenshot, le voci della console e della rete, i titoli delle schede e i nomi dei download.
Browser remoti e ospitati
Alcuni browser non condividono un filesystem con il processo che esegue l'SDK. Ne sono esempi un browser in un altro container, uno che raggiungi tramite un URL DevTools e uno fornito da un servizio di browser ospitato. Con questi, la policy degli URL, l'intercettazione delle richieste e confirm vengono comunque eseguiti nel tuo processo.
Con un browser ospitato, il provider controlla il traffico in uscita e l'isolamento dell'host. La tua policy di uscita non si applica lì, quindi l'hook delle richieste del driver è il tuo unico controllo sulle richieste del browser. Verifica cosa può raggiungere la rete del browser.
I controlli sui percorsi forniti non proteggono un browser remoto. BetaLocalFilePolicy controlla i percorsi sul filesystem del processo che esegue l'SDK, mentre il browser legge e scrive sul proprio filesystem.
L'SDK non può rilevare che un browser è remoto. Quindi, per un browser remoto, lascia file_upload disattivato a meno che il tuo driver non controlli i percorsi di upload dove viene eseguito il browser, con un proprio BetaFilePolicy. Un BetaFilePolicy verifica i percorsi e gli ID documento di ogni upload e decide se Claude vede il percorso di un download.
Fai in modo che un browser remoto rifiuti i download, a meno che il suo host non abbia la configurazione dei download descritta in Limita upload e download.
Tieni la chiave API del provider e l'URL di connessione della sessione, che può contenere una chiave, fuori da log, risultati degli strumenti e testi di errore. Se il provider registra le sessioni, la registrazione è un'ulteriore copia di tutto ciò che Claude ha visto e digitato, e a essa si applicano i termini di conservazione del provider.
Toolset del computer
BetaAbstractComputerToolset20260801 è la classe per lo strumento computer use. Un driver è una sottoclasse con un metodo per ogni strumento, come screenshot o left_click. Parti dal driver di un partner in Integrazioni dei partner. Per scriverne uno tuo, il repository claude-quickstarts contiene un esempio VNC minimale in Python e TypeScript. Controlla un desktop tramite VNC e non è codice di produzione.
La classe accetta configs, confirm e tool_configs, e li passi come fai per la classe del browser. Personalizza un driver, Gestisci gli errori, Avvia le chiamate mentre la risposta è in streaming ed Esegui senza il tool runner si applicano anche a essa, con queste differenze:
- Opzioni solo per il browser: La classe non ha policy degli URL, policy dei file né report di stato.
configs: Ogni strumento che implementi è attivo per impostazione predefinita. Le impostazioni per strumento sono elencate in Parametri dello strumento.confirm: Se la tua classe implementatype,keyohold_key, il costruttore solleva un errore di configurazione a meno che tu non passi un callableconfirmo disattivi quegli strumenti conconfigs. Una classe che sovrascriveexecuteconta come se implementasse tutti e tre.- Chiamate saltate: Dopo una chiamata al computer fallita, il tool runner risponde alle chiamate al computer successive del turno con
Not executed: an earlier computer action in this turn failed.Le chiamate al browser ricevono un testo diverso. In un ciclo scritto da te, rispondi alle chiamate al computer saltate con quel testo esatto (Azioni batch). execute: In TypeScript, un override diexecutetipizzanamecomeBetaComputerMemberNameeinputcomeBetaComputerMemberInput. Entrambi provengono da@anthropic-ai/sdk/resources/beta.- Testo di errore: L'SDK non nasconde i percorsi locali nel testo di errore di uno strumento del computer o nel testo restituito da uno strumento. Claude li legge entrambi, troncati a 4.096 caratteri. Intercetta le eccezioni nei tuoi strumenti e solleva
ToolError(lancialo con throw in TypeScript) con un testo tuo.
Scrivi un driver per il desktop
Questo driver implementa cinque dei 17 strumenti elencati in Azioni disponibili. Uno strumento che non implementi viene inviato all'API come disabilitato, come spiega Implementa un driver. Nell'esempio, display rappresenta il tuo wrapper attorno a ciò che controlla il desktop, come un client VNC o xdotool, e ask_user rappresenta la tua funzione che interpella una persona.
from anthropic import Anthropic
from anthropic.tools.computer import (
BetaAbstractComputerToolset20260801,
BetaComputerCursorPositionResult,
BetaScreenshotResult,
BetaToolsetCallContext,
)
from anthropic.types.beta import (
BetaComputerCursorPositionInput,
BetaComputerKeyInput,
BetaComputerLeftClickInput,
BetaComputerScreenshotInput,
BetaComputerTypeInput,
)
class MyDesktop(BetaAbstractComputerToolset20260801):
def __init__(self, display, **options):
super().__init__(**options)
self.display = display
def screenshot(
self, context: BetaToolsetCallContext, input: BetaComputerScreenshotInput
) -> BetaScreenshotResult:
# Le coordinate di Claude sono posizioni in pixel in questa immagine.
data = self.display.png_base64()
return BetaScreenshotResult(data=data, media_type="image/png")
def cursor_position(
self, context: BetaToolsetCallContext, input: BetaComputerCursorPositionInput
) -> BetaComputerCursorPositionResult:
x, y = self.display.cursor()
return BetaComputerCursorPositionResult(x=x, y=y)
def left_click(
self, context: BetaToolsetCallContext, input: BetaComputerLeftClickInput
) -> None:
# input.coordinate è [x, y], oppure None per cliccare sul cursore. input.text è un
# modificatore da tenere premuto, come "shift". Nulla da restituire: Claude legge "Clicked."
self.display.click(input.coordinate, input.text)
def type(
self, context: BetaToolsetCallContext, input: BetaComputerTypeInput
) -> None:
self.display.type(input.text)
def key(self, context: BetaToolsetCallContext, input: BetaComputerKeyInput) -> None:
self.display.press(input.text, input.repeat or 1)
def close(self) -> None:
super().close() # first, so no call is still using the desktop when it closes
self.display.close()
client = Anthropic()
with MyDesktop(
display,
# obbligatorio per type e key; vedi Run the computer toolset safely
confirm=lambda context: ask_user(f'Allow the "{context.member}" tool?'),
) as desktop:
runner = client.beta.messages.tool_runner(
model="claude-opus-5-5",
max_tokens=1024,
tools=[desktop],
messages=[
{"role": "user", "content": "Open the calculator and compute 17 * 23."}
],
)
for message in runner:
print(message)Quattro strumenti non hanno campi di input: screenshot, cursor_position, left_mouse_down e left_mouse_up. Ogni tipo di input, inclusi i loro, proviene da anthropic.types.beta.
In TypeScript, scrivi gli strumenti come metodi, non come campi con arrow function. Implementa un driver ne spiega il motivo.
Scala le coordinate e dimensiona le immagini
Ogni punto che Claude invia è una posizione in pixel negli screenshot che restituisci: ogni coordinate e start_coordinate, ed entrambi gli angoli di una region di zoom. L'SDK li passa ai tuoi strumenti senza modifiche.
Se i tuoi screenshot hanno una dimensione diversa da quella del display, esegui la conversione in entrambe le direzioni nel tuo driver: i punti di Claude in pixel del display, e la posizione del cursore che riporti in pixel dello screenshot. Mantieni la stessa dimensione dello screenshot per l'intera sessione e assegnale le proporzioni del display:
from anthropic.tools import ToolError
SCREENSHOT_SIZE = (1280, 720) # the size of every full screenshot the driver returns
DISPLAY_SIZE = (2560, 1440)
def to_display(coordinate: list[int]) -> tuple[int, int]:
x, y = coordinate
width, height = SCREENSHOT_SIZE
if not (0 <= x < width and 0 <= y < height):
# Rifiuta il punto. Spostarlo nell'intervallo farebbe clic dove Claude non ha scelto.
raise ToolError(f"[{x}, {y}] is outside the {width}x{height} screenshot")
return x * DISPLAY_SIZE[0] // width, y * DISPLAY_SIZE[1] // height
def to_screenshot(x: int, y: int) -> tuple[int, int]:
width, height = DISPLAY_SIZE
return x * SCREENSHOT_SIZE[0] // width, y * SCREENSHOT_SIZE[1] // height
to_display([640, 360]) # (1280, 720)Chiama to_display su ogni coordinate e start_coordinate prima di agire, e to_screenshot sulla posizione riportata da cursor_position. Un clic o uno scorrimento senza coordinate agisce sul cursore, quindi non c'è nulla da convertire. Scala una region di zoom dello stesso fattore. Un'immagine di zoom non cambia questo comportamento: i punti successivi che Claude invia sono comunque posizioni in pixel nello screenshot completo.
L'SDK non legge né ridimensiona mai un'immagine. L'API rifiuta un'immagine che supera i limiti del modello, il che termina l'esecuzione. Scala ogni screenshot e ogni immagine di zoom nel driver. Consulta Dimensiona gli screenshot per rispettare i limiti delle immagini. In un'esecuzione lunga, una richiesta può contenere più di 20 immagini, e in tal caso l'API applica a ciascuna un limite più rigoroso. Consulta Gestisci la cronologia degli screenshot.
Controlli dell'input e risultati
Non affidarti all'SDK per controllare l'input di una chiamata. Fai in modo che il tuo driver controlli i campi che utilizza e rifiuti una chiamata che non può eseguire: solleva ToolError (lancialo con throw in TypeScript). Claude legge il messaggio e può inviare una chiamata corretta.
Nessuno dei due SDK limita una duration, un conteggio repeat o uno scroll_amount. Il passaggio 4 in Esegui il toolset del computer in sicurezza li tratta.
Ciò che uno strumento restituisce determina ciò che Claude legge:
| Strumento | Restituisce | Claude legge |
|---|---|---|
screenshot, zoom | BetaScreenshotResult | Un blocco immagine |
cursor_position | BetaComputerCursorPositionResult | X={x},Y={y} |
| Ogni altro strumento | Niente, oppure una riga di testo | Una breve conferma, come Clicked., seguita dalla riga restituita in un blocco di testo separato |
Esegui il toolset del computer in sicurezza
Ciò che è sullo schermo orienta la prossima azione di Claude. Una pagina o una notifica può tentare di far digitare Claude nella finestra sbagliata, o di attivare un'azione con effetti reali.
Prima di eseguire un driver su qualcosa che non sia una macchina usa e getta, segui questi cinque passaggi:
- Isola il desktop. Eseguilo in un container o in una VM dedicati e con privilegi minimi per ogni sessione, senza credenziali e senza mount dell'host. Installa solo le applicazioni necessarie per l'attività. Esegui il codice che chiama l'API al di fuori di esso.
- Sottoponi a
confirmle azioni che hanno effetti reali. Un acquisto o un messaggio inviato è un normaleleft_click,typeokey, econfirmriceve lo strumento e il suo input, non lo schermo. Quindi scegli quali strumenti richiedono approvazione in base alle applicazioni che Claude può raggiungere sul desktop. - Tieni terminali, finestre di esecuzione e launcher fuori dal desktop, a meno che l'attività non ne richieda uno. Quando uno di essi ha il focus della tastiera,
type,keyehold_keyeseguono qualsiasi cosa Claude digiti, e un clic può avviare un programma. Se l'attività ne richiede uno, chiedi a una persona prima dell'input da tastiera e dei clic, e isola il desktop in modo che un comando non possa spingersi oltre quanto richiesto dall'attività. - Rifiuta l'input che il tuo desktop non dovrebbe accettare, come un punto al di fuori dello screenshot, un
waitdi diversi minuti, o unrepeato unoscroll_amountmolto grandi. SollevaToolError(lancialo con throw in TypeScript) invece di riportare il valore nell'intervallo consentito. - Considera tutto ciò che è sullo schermo come non attendibile, inclusi i titoli delle finestre e il testo degli appunti. Non eseguirlo né trasmetterlo senza controlli.
L'SDK applica il passaggio 2 tramite il callable che passi. Gli altri passaggi spettano al tuo driver e al tuo deployment. Si applicano anche le precauzioni descritte in Considerazioni sulla sicurezza.
Il seguente confirm chiede a una persona prima di qualsiasi input da tastiera, tramite la tua funzione ask_user, e approva ogni altra chiamata. context.member è il nome dello strumento. Per far approvare a una persona anche i clic, imposta ASK_FIRST su 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
# la funzione mostrata è quella di "Gate consequential members".
detail = shown(context.input.to_dict())
return ask_user(f'Allow the "{context.member}" tool with this input?\n{detail}')
desktop = MyDesktop(display, confirm=confirm)Una chiamata rifiutata riceve un risultato di errore e l'esecuzione continua. Per una chiamata type rifiutata, Claude legge The user did not grant permission to run 'type'. Do not retry it unless the user asks you to.
Questo confirm approva ogni strumento che non è in ASK_FIRST, incluso qualsiasi strumento che aggiungerai in seguito. Per un comportamento fail-closed, approva solo gli strumenti senza effetti sul desktop (screenshot, zoom, cursor_position e wait) e chiedi conferma per ogni altro strumento.
Usa entrambi i toolset in una sola richiesta
Passa entrambe le istanze in tools. Ogni chiamata riporta il proprio toolset_name, browser o computer, e il tool runner invia la chiamata all'istanza corrispondente. Dopo una chiamata non riuscita, il tool runner salta solo le chiamate successive del turno allo stesso toolset, quindi una chiamata al computer non riuscita non fa saltare le chiamate al browser del turno.
# MyBrowser, backend e url_policy provengono dall'esempio avanzato.
# confirm è la funzione di "Esegui il set di strumenti computer in sicurezza".
with (
MyBrowser(backend, url_policy=url_policy) as browser,
MyDesktop(display, confirm=confirm) as desktop,
):
runner = client.beta.messages.tool_runner(
model="claude-opus-5-5",
max_tokens=1024,
tools=[browser, desktop],
messages=[
{
"role": "user",
"content": "Copy the heading on example.com into the open text editor.",
}
],
stream=True,
run_tools_eagerly=True, # so a call can start before the response ends
)
for stream in runner:
print(stream.get_final_message())In un loop scritto da te, rispondi a ogni chiamata con tool_result sull'istanza il cui toolset_name corrisponde al toolset_name della chiamata. In ogni turno, tieni traccia delle chiamate non riuscite separatamente per ciascun toolset, e rispondi a una chiamata saltata con il testo specifico di quel toolset. Esegui senza il tool runner riporta il testo del browser, e il punto elenco sulle chiamate saltate all'inizio di questa sezione riporta quello del computer.
Riferimento
Le opzioni del costruttore hanno lo stesso significato in entrambi gli SDK:
| Python | TypeScript | Imposta |
|---|---|---|
configs | configs | Quali membri sono abilitati |
confirm | confirm | Il callable che approva o rifiuta ogni chiamata |
url_policy | urlPolicy | La tua policy degli URL, che l'SDK chiama prima di ogni navigate verso un URL |
file_policy | filePolicy | Le radici di upload e l'esposizione del percorso di download |
tool_configs | toolConfigs | I campi per la voce tools, come cache_control |
Il metodo _browser_state | browserState | Il report di stato |
La classe del browser accetta tutte le opzioni della tabella. La classe del computer accetta configs, confirm e tool_configs.
Non puoi modificare un'opzione dopo la costruzione. La guida al toolset del browser per Python, la guida al toolset del browser per TypeScript, la guida al toolset del computer per Python e la guida al toolset del computer per TypeScript descrivono ciascuna opzione. Le guide Python trattano anche le classi asincrone, BetaAsyncAbstractBrowserToolset20260801 e BetaAsyncAbstractComputerToolset20260801.
Limitazioni
- L'SDK non include un browser, un desktop, un driver pronto all'uso o una policy degli URL: Li fornisci tu. Consulta Integrazioni dei partner, Implementa un driver e Scrivi una policy degli URL.
- La policy degli URL vede solo le chiamate
navigate: Non vede le richieste effettuate da una pagina, né le pagine che si aprono in qualsiasi altro modo, ad esempio da un link su cui Claude fa clic o da un reindirizzamento. Consulta Intercetta le richieste nel driver. - Un'approvazione si basa sull'ultimo report di stato: La pagina può cambiare dopo quel report. L'SDK non controlla di nuovo la pagina prima che la chiamata venga eseguita.
- Le chiamate su un toolset vengono eseguite una alla volta: Non puoi disattivare questo comportamento.
- L'SDK non controlla che gli ID delle schede siano univoci né quante schede ci siano, e non controlla sempre che una scheda sia attiva: L'API rifiuta un report che viola queste regole.
- Per una chiamata al computer,
confirmriceve lo strumento e il suo input, non lo schermo: Da questi non puoi capire cosa fa un clic in un determinato punto. - L'SDK non ridimensiona le immagini di un toolset del computer: L'API rifiuta un'immagine che supera i limiti del modello. Consulta Scala le coordinate e dimensiona le immagini.
Passaggi successivi
Gli strumenti membri, il blocco browser_state e le considerazioni sulla sicurezza dello strumento.
I 17 strumenti, le coordinate e i limiti delle immagini, e le considerazioni sulla sicurezza dello strumento.
Come l'SDK esegue il loop e come modificare i messaggi che invia.
Protezioni per qualsiasi applicazione che legge contenuti non attendibili.
Was this page helpful?