Claude Platform Docs
MessagesTools

Browser- und Computernutzung mit den SDK-Toolsets

Führe das Browser-Use-Tool oder das Computer-Use-Tool aus dem Python- oder TypeScript-SDK aus. Das SDK führt die Schleife und die von dir konfigurierten Prüfungen aus, und du stellst den Browser oder den Desktop bereit.

Mit den Browser- und Computer-Toolsets in den Python- und TypeScript-SDKs kannst du einen Agenten bauen, der in einem Webbrowser oder auf einem Desktop arbeitet. Er kann Anwendungen ohne programmatische Schnittstelle nutzen. Über das Browser-Use-Tool navigiert Claude auf Webseiten, liest sie und interagiert mit ihnen. Über das Computer-Use-Tool sieht Claude einen Desktop in Screenshots und verwendet Maus und Tastatur.

Das SDK verbindet Claude mit deinem Browser oder Desktop. Es führt die von dir konfigurierten Prüfungen aus, etwa eine „URL policy“ (URL-Richtlinie) für navigate oder einen Genehmigungs-Callback. Es sendet jeden Aufruf, der diese Prüfungen besteht, an deinen „driver“ (Treiber) und erstellt das tool_result aus dem, was der Treiber zurückgibt. Der Tool-Runner kann die agentische Schleife für dich ausführen.

Du stellst den Treiber bereit, der den Browser oder Desktop bedient. Beginne mit dem Treiber eines Anbieters. Damit braucht ein Agent nur wenige Codezeilen. Wenn keiner passt, schreibe deinen eigenen mit einer Browser-Automatisierungsbibliothek wie Playwright.

Der Großteil dieser Seite behandelt das Browser-Toolset. Computer-Toolset beschreibt, was sich unterscheidet.

Einen eigenen Treiber zu schreiben ist ein fortgeschrittener Fall, der unter Einen Treiber implementieren behandelt wird. Das Repository claude-quickstarts enthält außerdem einen Beispiel-Browser-Treiber in Python und TypeScript. Er steuert Chromium über das Chrome DevTools Protocol (CDP) und ist kein Produktionscode.

Einen Treiber eines Anbieters verwenden

Mit einem Treiber eines Anbieters, etwa einem unter Partner-Integrationen, kannst du einen Agenten in wenigen Codezeilen bauen. Der Anbieter in diesem Beispiel ist erfunden. Ersetze example_browser_provider und ExampleBrowser durch das Paket und die Treiberklasse eines echten Anbieters und richte sie so ein, wie es die Dokumentation des Anbieters beschreibt.

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)

Übergib die Treiberinstanz selbst als Eintrag in tools. Der Runner schließt das Toolset nie, sodass eine Instanz mehrere Läufe bedienen kann. Schließe sie, wenn du fertig bist.

Wie du einen eigenen Treiber schreibst, erfährst du unter Einen Treiber implementieren.

Partner-Integrationen

Browser Use, Browserbase, Daytona und E2B veröffentlichen eigene Treiber für die SDK-Toolsets.

Browser-Use-Treiber für das Browser-Toolset.

Browserbase-Stagehand-Treiber für das Browser-Toolset.

Daytona-Treiber für das Computer-Toolset.

E2B

E2B-Treiber für das Browser- und das Computer-Toolset.

Unsere Partner veröffentlichen außerdem Code auf GitHub: den Browser-Use-Schnellstart, das Browserbase-Toolset und das E2B-Beispiel.

Einen Treiber anpassen

Die Beispiele in diesem Abschnitt verwenden MyBrowser, den Treiber aus dem fortgeschrittenen Beispiel weiter unten auf dieser Seite.

Member aktivieren oder deaktivieren

configs nimmt die Einstellungen pro Member entgegen, die unter Das Toolset konfigurieren beschrieben sind. Liste nur die Member auf, die du änderst:

# Ein MyBrowser, der zusätzlich read_console implementiert
browser = MyBrowser(
    backend, configs={"read_console": {"enabled": True}, "navigate": {"enabled": False}}
)

Das SDK lehnt einen Aufruf eines deaktivierten Members ab, bevor dein Code ausgeführt wird. Ein Member zu aktivieren, das deine Klasse nicht implementiert, ist ein Konfigurationsfehler, es sei denn, die Klasse überschreibt execute.

Vorher- und Nachher-Hooks hinzufügen

Überschreibe execute und rufe das execute der Elternklasse auf. Code vor diesem Aufruf läuft nach der URL-Richtlinie, der Datei-Richtlinie und confirm, und er kann die Eingabe ändern. Das SDK prüft die geänderte Eingabe nicht erneut. Code nach dem Aufruf erhält das Ergebnis und kann es ändern. Löse ToolError aus (in TypeScript mit throw), um den Aufruf abzulehnen.

Das Überschreiben von execute ändert, welche Member Claude angeboten werden. Das SDK zählt jeden Member als implementiert, sodass Claude jeder standardmäßig aktivierte Member angeboten wird. Das MyBrowser im fortgeschrittenen Beispiel bedient drei Member, daher bietet das folgende TracedBrowser Claude Member an, die es nicht bedienen kann. Deaktiviere diese Member mit configs, bevor du es verwendest.

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

Einen Treiber implementieren

Ein Treiber ist deine Unterklasse von BetaAbstractBrowserToolset20260801. Überschreibe darin die Member, die dein Browser unterstützt. Ein Member, den du nicht implementierst, wird als deaktiviert an die API gesendet. Wenn Claude ihn trotzdem aufruft, gibt das SDK einen Fehler zurück, und der Lauf wird fortgesetzt. Das Überschreiben von execute ändert, welche Member als deaktiviert gesendet werden (Vorher- und Nachher-Hooks hinzufügen). Jeder Member erhält den Aufrufkontext und die Eingabe des Members als typisiertes Objekt, etwa BetaBrowserNavigateInput. Die Eingabetypen stammen aus anthropic.types.beta. Member-Tools listet die Felder jeder Eingabe auf.

Schreibe Member in TypeScript als Methoden, nicht als Arrow-Function-Felder, da das SDK sie auf dem Prototyp findet. Schreibe das Member type in TypeScript als type_. In Python heißt es type.

Ergebnisse zurückgeben

Was ein Member zurückgibt, bestimmt, was Claude liest. Ein erfolgreiches Ergebnis endet mit einem browser_state-Block, der aus deinem Zustandsbericht erstellt wird. Ein Fehlerergebnis enthält keinen Block.

MemberGibt zurückClaude liest
screenshot, zoomBetaScreenshotResultEinen Bildblock
navigateBetaBrowserNavigateResultNavigated to {url} — {title} (HTTP {status})
new_tab, switch_tab, list_tabs, close_tabEinen Tab-Eintrag (new_tab, switch_tab), eine Liste von Tab-Einträgen (list_tabs) oder nichts (close_tab)Nur den browser_state-Block
read_page, get_page_text, find, read_console, read_network, javascript_execEinen StringDen String oder (empty) bei einem leeren String
Jedes andere MemberNichts oder eine TextzeileEine kurze Bestätigung wie Clicked., dann die zurückgegebene Zeile in einem eigenen Textblock

Browserzustand melden

Das SDK ruft _browser_state nach jedem Aufruf auf, der ein Ergebnis zurückgibt, einschließlich abgelehnter und fehlgeschlagener Aufrufe. Gib jeden offenen Tab zurück und was sich seit dem letzten Bericht geändert hat:

  • Geöffnete Tabs und Download-Ereignisse.
  • Ein BetaNavigationRefused für jede Navigation, die dein Request-Hook blockiert hat.
  • Ein BetaDialogDismissed für jeden nativen Dialog, den dein Treiber geschlossen hat.

Lege all diese in state_changes ab. In Python stammen BetaNavigationRefused() und BetaDialogDismissed(kind=..., message=...) aus anthropic.tools.browser. In TypeScript sind es { type: "navigation_refused" } und { type: "dialog_dismissed", kind, message }.

Die letzten beiden sind keine API-Zustandsänderungen. Das SDK meldet sie Claude als Text außerhalb des browser_state-Blocks: eine Zeile für alle abgelehnten Navigationen, die keine URL enthält, und eine Zeile für jeden der ersten drei geschlossenen Dialoge, gefolgt von der Anzahl aller weiteren.

Wenn ein Tab geöffnet ist, muss genau einer aktiv sein. Jeder Member, der eine tab_id entgegennimmt, muss auf den Tab wirken, den sie benennt. Die Grenzen der API für den Bericht sind unter Tabs mit browser_state verfolgen aufgeführt.

Claude liest die URL jedes Tabs, die URL jedes Downloads und die URL, die dein navigate zurückgibt, so wie dein Treiber sie geschrieben hat. Das SDK parst diese URLs nicht. Es wandelt Zeilenumbrüche und andere Steuerzeichen in Leerzeichen um, kürzt die Enden und schneidet jede URL bei 4.096 Zeichen ab. Daher erreichen der Inhalt einer data:-URL, der Pfad einer file:-URL sowie ein Benutzername oder Passwort in einer URL alle Claude.

Fehler behandeln

Ausgelöst von einem Member oder dem SDKClaude liestDer Lauf
ToolErrorSeine Nachricht, als FehlerergebnisWird fortgesetzt
ToolsetUsageErrorNichtsStoppt
Jede andere ExceptionClassName: message in Python oder Error: message in TypeScript, als FehlerergebnisWird fortgesetzt

Das SDK löst ToolsetUsageError bei einem Konfigurationsfehler, einer Fehlverwendung des SDK während eines Aufrufs oder einem Aufruf nach close aus. Es löst ihn auch aus, wenn _browser_state irgendeine Exception auslöst, selbst einen ToolError. Der Tool-Runner und tool_result fangen ihn nicht ab, sodass er ihren Aufrufer erreicht.

Das SDK verbirgt keine lokalen Pfade im Fehlertext eines Members, in der Zeile, die eine Aktion wie left_click zurückgibt, im Fehler eines fehlgeschlagenen Downloads oder in der Nachricht eines geschlossenen Dialogs.

Ein ToolError aus deiner URL-Richtlinie, deiner Datei-Richtlinie oder deinem confirm-Callable erreicht Claude so, wie er geschrieben ist, also lass lokale Pfade aus seinem Text heraus. Fange Exceptions in deinen Membern ab und löse ToolError (in TypeScript mit throw) mit deinem eigenen Text aus.

Aufrufe starten, während die Antwort gestreamt wird

Standardmäßig führt der Tool-Runner die Aufrufe eines Turns aus, nachdem Claudes Antwort endet. Mit Early Start kann er einen Browser- oder Computer-Aufruf starten, während die Antwort noch gestreamt wird.

Um ihn zu aktivieren, übergib stream=True und run_tools_eagerly=True an den Tool-Runner, wie es das fortgeschrittene Beispiel tut. Mit stream liefert dir jeder Durchlauf deiner Schleife über den Runner einen Stream statt einer Nachricht.

Early Start ändert, wann ein Aufruf starten kann, nicht wie das Toolset ihn ausführt:

  • Die Prüfungen laufen weiterhin zuerst: Das SDK ruft deine URL-Richtlinie, deine Datei-Richtlinie und confirm auf, bevor ein Aufruf läuft. Mit Early Start kann es confirm aufrufen, während die Antwort noch gestreamt wird.
  • Aufrufe laufen weiterhin einzeln nacheinander: Ein Toolset führt seine Aufrufe in der Reihenfolge aus, in der Claude sie geschrieben hat. Ein fehlgeschlagener Aufruf stoppt weiterhin die späteren Aufrufe dieses Toolsets im Turn.
  • Ein gestarteter Aufruf kann nicht zurückgenommen werden: Wenn die Antwort danach abgeschnitten wird, zum Beispiel bei max_tokens, oder deine Schleife vorzeitig stoppt, findet die Aktion trotzdem statt, und Claude liest ihr Ergebnis nie.

Ohne den Tool-Runner ausführen

Übergib die Instanz in tools (browser.toJSON() in TypeScript) und beantworte jeden Member-Aufruf mit tool_result. Das Browser-Use-Tool verlangt, dass du beim ersten fehlgeschlagenen Aufruf stoppst (Batch-Aktionen).

Diese Schleife verwendet MyBrowser und die URL-Richtlinie aus dem fortgeschrittenen Beispiel. Nach einem fehlgeschlagenen Aufruf beantwortet sie die späteren Aufrufe des Turns, ohne sie auszuführen:

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:
                # Nach einem fehlgeschlagenen Aufruf wird der Rest des Turns beantwortet, nicht ausgeführt.
                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})

Jede Antwort auf einen übersprungenen Aufruf enthält is_error, den toolset_name des Aufrufs und genau den Text, den der Abschnitt Batch-Aktionen verlangt. Der Tool-Runner sendet dieselbe Antwort.

Fortgeschrittenes Beispiel: einen eigenen Treiber schreiben

Dieses Beispiel fasst die Hauptteile von Einen Treiber implementieren in einem Treiber, MyBrowser, zusammen und führt ihn mit dem Tool-Runner und frühem Start aus. MyBrowser implementiert navigate, screenshot und left_click sowie _browser_state, den Zustandsbericht, den jeder Treiber braucht.

backend steht für deinen eigenen Wrapper um eine Browser-Automatisierungsbibliothek wie Playwright. Die URL-Richtlinie ist ein Beispiel. Eine URL-Richtlinie schreiben erklärt sie.

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 ist "back", "forward", "reload" oder eine URL, die deine URL-Policy
        # zugelassen hat, so wie Claude sie geschrieben hat. Wie is_allowed ergänzt backend.goto https://
        # bei einer URL, die nicht mit einem Schema beginnt.
        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:
        # Nichts zurückzugeben: Claude liest "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:
    # Ein Beispiel, keine Policy für die Produktion. Siehe "Write a URL policy".
    if url.lower() == "about:blank":
        return True
    with_scheme = url if SCHEME_PREFIX.match(url) else f"https://{url}"
    # Ein Browser liest "\" in einer Web-URL als "/".
    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())

Das Toolset sicher ausführen

Claudes nächste Aktion hängt von den Seiten ab, die es liest. Eine Seite oder in sie eingeschleuster Text kann versuchen, interne Dienste zu erreichen oder Dateien vom Host abzuziehen. Sie kann auch versuchen, Aktionen mit realen Auswirkungen auszulösen. Bevor du einen Treiber gegen etwas anderes als einen Wegwerf-Browser ausführst, unternimm diese sechs Schritte:

  1. Schreibe eine URL-Richtlinie, die jede navigate-URL ablehnt, die die Aufgabe nicht benötigt.
  2. Fange Anfragen im Treiber ab und prüfe jede Anfrage mit denselben Regeln.
  3. Lege eine „egress policy" (Richtlinie für ausgehenden Datenverkehr) auf den Container, damit das Netzwerk blockiert, was der Treiber nicht sehen kann.
  4. Beschränke Uploads und Downloads oder lass Uploads deaktiviert.
  5. Sichere folgenreiche Member mit confirm ab.
  6. Isoliere den Browser-Host für jede Sitzung in einem dedizierten Container oder einer VM.

Das SDK wendet die Schritte 1, 4 und 5 über die Richtlinien und das Callable an, die du übergibst. Die Schritte 2, 3 und 6 liegen bei deinem Treiber und deinem Deployment. Die Vorsichtsmaßnahmen unter Sicherheitsüberlegungen gelten ebenfalls.

Eine URL-Richtlinie schreiben

Eine URL-Richtlinie ist eine Funktion, die du als url_policy übergibst. Du schreibst die Richtlinie, und du bist dafür verantwortlich, sie produktionsreif zu machen. Bevor dein navigate läuft, ruft das SDK die Richtlinie mit dem Kontext des Aufrufs und der URL auf, so wie Claude sie geschrieben hat.

Gib nichts zurück, um die URL zu erlauben. Um sie abzulehnen, löse ToolError aus (in TypeScript mit throw). Claude liest die Nachricht des Fehlers, und navigate läuft nicht. Die Richtlinie prüft nur die URL in jedem navigate-Aufruf. Sie ist keine Netzwerkkontrolle.

Dies ist die Richtlinie aus dem fortgeschrittenen Beispiel. Sie erlaubt about:blank sowie zwei Sites und ihre Subdomains über http und 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:
    # Ein Beispiel, keine Richtlinie für den Produktiveinsatz.
    if url.lower() == "about:blank":
        return True
    with_scheme = url if SCHEME_PREFIX.match(url) else f"https://{url}"
    # Ein Browser liest "\" in einer Web-URL als "/".
    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)

Das SDK ruft die Richtlinie nur für navigate auf, nicht für "back", "forward" oder "reload". Claude kann das Schema weglassen, wie in example.com, daher fügt das Beispiel einer URL, die nicht mit einem Schema beginnt, https:// hinzu, bevor es den Host liest. Dein navigate erhält die URL so, wie Claude sie geschrieben hat. Füge das Schema dort mit demselben Test hinzu, damit der Browser die URL öffnet, die deine Richtlinie geprüft hat.

Das Beispiel ist nicht produktionstauglich. Eine Produktionsrichtlinie braucht viel mehr, in Bereichen wie diesen:

  • Links, die Claude anklickt, Formulare, die es absendet, Weiterleitungen und die Anfragen, die eine Seite von sich aus stellt. Nur dein Treiber sieht sie. Siehe Anfragen im Treiber abfangen.
  • Hostnamen und URLs, die aus Seiteninhalten stammen, die ein Angreifer wählen kann.
  • Jede URL so zu parsen, wie der Browser es tut, statt den Hostnamen mit einem einfachen String-Split zu finden.
  • URL-Schemata. Das SDK prüft sie nicht, also lehne jedes Schema ab, das dein Treiber nicht öffnen soll.
  • Egress-Regeln auf dem Container für das, was keine URL-Prüfung sehen kann. Siehe Egress-Richtlinie auf den Container legen.

Wenn du url_policy=None übergibst, lehnt das SDK jedes navigate zu einer URL ab, sodass ein aus deiner Konfiguration gelesenes None URLs nicht versehentlich ungeprüft lassen kann. In TypeScript lässt undefined die Option ungesetzt, genauso als würdest du sie nicht übergeben.

Anfragen im Treiber abfangen

Die URL-Richtlinie beurteilt nur die URL in einem navigate-Aufruf. Ein Link, den Claude anklickt, ein Formular, das es absendet, eine Weiterleitung oder ein Bild oder Skript auf einer erlaubten Seite kann trotzdem einen Host erreichen, den die Richtlinie ablehnen würde. Verwende Egress-Regeln, um diese Hosts zu blockieren.

Ein Treiber mit Request-Interception kann diese Anfragen auch in seinem Request-Hook prüfen, der Funktion, die deine Automatisierungsbibliothek vor jeder Anfrage aufruft. Rufe die Funktion auf, die deine Richtlinie verwendet, damit du die Regeln nur einmal schreibst:

class MyBrowser(BetaAbstractBrowserToolset20260801):
    ...

    # Wird auf einem Playwright-Browserkontext mit context.route("**/*", self._guard) registriert
    def _guard(self, route):
        # is_allowed ist die Funktion aus „Eine URL-Richtlinie schreiben“
        if not is_allowed(route.request.url):
            return route.abort("blockedbyclient")
        route.continue_()

Ein Request-Hook wie dieser sieht keine WebSocket-Handshakes, keine Service-Worker-Anfragen und keine Weiterleitungsschritte. Blockiere Service Worker. Prüfe WebSocket-Handshakes und Weiterleitungsschritte mit einem Hook, der sie sieht. Die Beispielrichtlinie lehnt ws://- und wss://-URLs ab, also ändere ws:// in http:// und wss:// in https://, bevor du eine WebSocket-URL an is_allowed übergibst.

Egress-Richtlinie auf den Container legen

Das Abfangen kann nicht jede Anfrage sehen, die der Browser stellt, und die URL-Richtlinie sieht nicht, wohin ein Name aufgelöst wird. Eine Egress-Richtlinie, die das Netzwerk des Containers durchsetzt, deckt beides ab:

  • Blockiere Link-Local- und private Adressbereiche in IPv4 und IPv6. Dazu gehört die Cloud-Metadatenadresse 169.254.169.254.
  • Erlaube ausgehende Verbindungen nur zu den Hosts, die die Aufgabe benötigt. Wenn deine Regeln IP-Adressen abgleichen, löse die erlaubten Hostnamen beim Start des Containers auf.
  • Erlaube DNS nur zum Resolver des Containers.

Regeln, die außerhalb des Browser-Containers durchgesetzt werden, etwa eine Kubernetes NetworkPolicy oder eine Cloud-Firewall, sehen Loopback darin nicht. Mit nur diesen Regeln kann eine Seite alles erreichen, was dort lauscht, einschließlich eines DevTools-Ports, der eine Liste der offenen Tabs des Browsers und die Adresse anzeigt, die jeden davon steuert.

Uploads und Downloads beschränken

file_upload ist standardmäßig deaktiviert. Ohne file_policy lehnt das SDK jeden Upload ab, der einen Pfad oder eine Dokument-ID angibt. Um Uploads zu aktivieren, übergib eine BetaLocalFilePolicy mit einem Upload-Verzeichnis, das nur die Dateien der Aufgabe enthält:

from anthropic.tools.browser import BetaLocalFilePolicy

# Ein MyBrowser, der zusätzlich file_upload implementiert
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,
    ),
)

Das SDK löst jeden Upload-Pfad auf, folgt dabei Symlinks und lehnt jeden Pfad außerhalb der Upload-Roots ab. Die Datei-Richtlinie löst einen Konfigurationsfehler für ein Download-Verzeichnis aus, das ein Upload-Root ist, in einem liegt oder eines enthält. Diese Prüfung kann einen Symlink übersehen, also lass nicht zu, dass ein Symlink das Download-Verzeichnis mit einem Upload-Root gleichsetzt oder eines in das andere legt. Das SDK nimmt den path eines Downloads nur dann in den Zustandsbericht auf, wenn expose_download_paths true ist und die Datei im Download-Verzeichnis liegt.

Die mitgelieferte Datei-Richtlinie prüft Pfade im Dateisystem des Prozesses, der das SDK ausführt. Sie schützt nur einen Browser, der dieses Dateisystem teilt. Folge für einen Remote-Browser stattdessen Entfernte und gehostete Browser.

Richte Downloads so ein:

  • Erstelle das Download-Verzeichnis selbst mit dem Modus 0700 und mounte es mit noexec,nosuid,nodev.
  • Halte das Verzeichnis außer Reichweite anderer Tools, die Claude aufrufen kann, etwa einer Shell oder eines Datei-Tools.
  • Schreibe in einer download_failed-Zustandsänderung error als feste Formulierung. Der Text einer Exception kann den Pfad oder die URL enthalten.
  • Lies eine heruntergeladene Datei nicht in die Konversation ein und führe sie nicht aus, bis eine Person dies entscheidet.

Folgenreiche Member absichern

javascript_exec und file_upload sind standardmäßig deaktiviert. Wenn du einen davon ohne confirm-Callable aktivierst, löst der Konstruktor einen Konfigurationsfehler aus. Mit einem confirm-Callable ruft das SDK es vor jedem Aufruf auf, der gleich ausgeführt wird. Ohne eines wird nichts gefragt. In TypeScript löst confirm: null den Konfigurationsfehler nicht aus, und das SDK lehnt jeden Aufruf jedes Members ab.

Gib True zurück, um den Aufruf auszuführen, oder False, um ihn abzulehnen. Wenn dein Callable eine Person fragt, zeige ihr den Member, die URL der Seite und die Eingabe des Aufrufs. Escape zuerst jedes Zeichen außerhalb von druckbarem ASCII in der URL und der Eingabe, da beide Text von der Seite enthalten können.

Dieses Beispiel fragt über deine eigene Funktion ask_user nach den zwei abgesicherten Membern und genehmigt den Rest:

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
        # Eine Freigabe gilt nur für genau diese Eingabe auf dieser Seite.
        key = (name, page, detail)
        if key not in granted and ask_user(question):
            granted.add(key)
        return key in granted

    return confirm


# Ein MyBrowser, der zusätzlich javascript_exec und file_upload implementiert
# file_upload benötigt außerdem die file_policy aus "Uploads und Downloads einschränken"
browser = MyBrowser(
    backend,
    configs={"javascript_exec": {"enabled": True}, "file_upload": {"enabled": True}},
    confirm=make_confirm(),
    url_policy=url_policy,
)

Jeder Aufruf von make_confirm() gibt ein Callable ohne Genehmigungen zurück. Rufe es einmal für jedes Toolset auf und gib jedem Benutzer sein eigenes Toolset.

Eine Genehmigung gilt für die Seite, wie der letzte Zustandsbericht sie gezeigt hat, und die Seite kann sich ändern, bevor der Aufruf ausgeführt wird. Käufe, gesendete Nachrichten und akzeptierte Bedingungen erfolgen über gewöhnliche Member wie left_click und type, daher kann confirm sie nicht anhand des Namens herausfiltern. Damit eine Person sie genehmigt, frage auch nach diesen Membern.

Aktiviere javascript_exec nicht, es sei denn, Egress-Regeln beschränken die ausgehenden Verbindungen des Browsers auf die Hosts, die die Aufgabe benötigt. Ein Skript kann den Inhalt der Seite an jede Adresse senden, die der Browser erreichen kann.

Eine javascript:-URL in einem navigate-Aufruf führt ebenfalls Skript in der aktuellen Seite aus, selbst wenn javascript_exec deaktiviert ist. confirm sieht diesen Aufruf als navigate, daher würde das confirm-Callable des Beispiels ihn genehmigen. Dein Treiber muss javascript:-URLs ablehnen. Im Beispiel lehnt die URL-Richtlinie sie ab, bevor confirm läuft.

Browser-Host isolieren

Führe den Browser für jede Sitzung in einem dedizierten Container oder einer VM mit minimalen Rechten aus:

  • Führe ihn als Nicht-Root-Benutzer aus, mit einem schreibgeschützten Root-Dateisystem, wo der Browser es zulässt.
  • Mounte nichts vom Host außer den Upload- und Download-Verzeichnissen, die du konfiguriert hast, falls vorhanden.
  • Halte Anmeldedaten aus der Umgebung heraus und starte mit einem frischen Browserprofil.
  • Teile kein Dateisystem mit anderen Tools, die Claude aufrufen kann.

Führe den Code, der die API aufruft, außerhalb des Browser-Containers aus, da dieser Code deinen API-Key und die Konversation enthält. Der Tool-Runner und tool_result führen das Toolset beide im Prozess dieses Codes aus, sodass der Browser das Dateisystem des Toolsets nicht teilt. Behandle den Browser als remote: Entfernte und gehostete Browser gilt.

Behandle alles, was eine Seite zurückgibt, als nicht vertrauenswürdig, einschließlich Seitentext, Screenshots, Konsolen- und Netzwerkeinträgen, Tab-Titeln und Download-Namen.

Entfernte und gehostete Browser

Manche Browser teilen kein Dateisystem mit dem Prozess, der das SDK ausführt. Beispiele sind ein Browser in einem anderen Container, einer, den du über eine DevTools-URL erreichst, und einer von einem gehosteten Browser-Dienst. Bei diesen laufen die URL-Richtlinie, das Abfangen von Anfragen und confirm weiterhin in deinem Prozess.

Bei einem gehosteten Browser kontrolliert der Anbieter Egress und Host-Isolation. Deine eigene Egress-Richtlinie gilt dort nicht, daher ist der Request-Hook des Treibers deine einzige Prüfung der Anfragen des Browsers. Finde heraus, was das Netzwerk des Browsers erreichen kann.

Die mitgelieferten Pfadprüfungen schützen keinen Remote-Browser. BetaLocalFilePolicy prüft Pfade im Dateisystem des Prozesses, der das SDK ausführt, und der Browser liest und schreibt sein eigenes Dateisystem.

Das SDK kann nicht erkennen, dass ein Browser remote ist. Lass daher bei einem Remote-Browser file_upload deaktiviert, es sei denn, dein Treiber prüft Upload-Pfade dort, wo der Browser läuft, mit einer eigenen BetaFilePolicy. Eine BetaFilePolicy prüft die Pfade und Dokument-IDs jedes Uploads und entscheidet, ob Claude den Pfad eines Downloads sieht.

Lass einen entfernten Browser Downloads ablehnen, es sei denn, sein eigener Host verfügt über die Download-Einrichtung aus Uploads und Downloads beschränken.

Halte den API-Key des Anbieters und die Verbindungs-URL der Sitzung, die einen Key enthalten kann, aus Logs, Tool-Ergebnissen und Fehlertexten heraus. Wenn der Anbieter Sitzungen aufzeichnet, ist die Aufzeichnung eine weitere Kopie von allem, was Claude gesehen und eingegeben hat, und die Aufbewahrungsbedingungen des Anbieters gelten dafür.

Computer-Toolset

BetaAbstractComputerToolset20260801 ist die Klasse für das Computer-Use-Tool. Ein Treiber ist eine Unterklasse mit einer Methode pro Tool, etwa screenshot oder left_click. Beginne mit dem Treiber eines Partners unter Partner-Integrationen. Wenn du deinen eigenen schreiben möchtest, enthält das Repository claude-quickstarts ein minimales VNC-Beispiel in Python und TypeScript. Es steuert einen Desktop über VNC und ist kein Produktionscode.

Die Klasse nimmt configs, confirm und tool_configs entgegen, und du übergibst sie wie bei der Browser-Klasse. Einen Treiber anpassen, Fehler behandeln, Aufrufe starten, während die Antwort gestreamt wird und Ohne den Tool-Runner ausführen gelten auch für sie, mit diesen Unterschieden:

  • Nur-Browser-Optionen: Die Klasse hat keine URL-Richtlinie, keine Datei-Richtlinie und keinen Zustandsbericht.
  • configs: Jedes Tool, das du implementierst, ist standardmäßig aktiviert. Die Einstellungen pro Tool sind unter Tool-Parameter aufgeführt.
  • confirm: Wenn deine Klasse type, key oder hold_key implementiert, löst der Konstruktor einen Konfigurationsfehler aus, es sei denn, du übergibst ein confirm-Callable oder deaktivierst diese Tools mit configs. Eine Klasse, die execute überschreibt, gilt als Implementierung aller drei.
  • Übersprungene Aufrufe: Nach einem fehlgeschlagenen Computer-Aufruf beantwortet der Tool-Runner die späteren Computer-Aufrufe des Turns mit Not executed: an earlier computer action in this turn failed. Browser-Aufrufe erhalten einen anderen Text. Beantworte in einer selbst geschriebenen Schleife die übersprungenen Computer-Aufrufe mit genau diesem Text (Batch-Aktionen).
  • execute: In TypeScript typisiert eine execute-Überschreibung name als BetaComputerMemberName und input als BetaComputerMemberInput. Beide stammen aus @anthropic-ai/sdk/resources/beta.
  • Fehlertext: Das SDK verbirgt keine lokalen Pfade im Fehlertext eines Computer-Tools oder im Text, den ein Tool zurückgibt. Claude liest beides, abgeschnitten bei 4.096 Zeichen. Fange Exceptions in deinen Tools ab und löse ToolError (in TypeScript mit throw) mit deinem eigenen Text aus.

Einen Desktop-Treiber schreiben

Dieser Treiber implementiert fünf der 17 Tools, die unter Verfügbare Aktionen aufgeführt sind. Ein Tool, das du nicht implementierst, wird als deaktiviert an die API gesendet, wie Einen Treiber implementieren erklärt. Im Beispiel steht display für deinen eigenen Wrapper um das, was den Desktop steuert, etwa einen VNC-Client oder xdotool, und ask_user steht für deine eigene Funktion, die eine Person fragt.

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:
        # Claudes Koordinaten sind Pixelpositionen in diesem Bild.
        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 ist [x, y] oder None, um an der Cursorposition zu klicken. input.text ist eine
        # gedrückt zu haltende Modifikatortaste, z. B. "shift". Keine Rückgabe: Claude liest "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,
    # erforderlich für type und key; siehe 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)

Vier Tools haben keine Eingabefelder: screenshot, cursor_position, left_mouse_down und left_mouse_up. Jeder Eingabetyp, einschließlich ihrer, stammt aus anthropic.types.beta.

Schreibe Tools in TypeScript als Methoden, nicht als Arrow-Function-Felder. Einen Treiber implementieren nennt den Grund.

Koordinaten skalieren und Bildgrößen festlegen

Jeder Punkt, den Claude sendet, ist eine Pixelposition in den Screenshots, die du zurückgibst: jede coordinate und start_coordinate sowie beide Ecken einer zoom-region. Das SDK übergibt sie unverändert an deine Tools.

Wenn deine Screenshots eine andere Größe als das Display haben, rechne in deinem Treiber in beide Richtungen um: Claudes Punkte in Display-Pixel und die Cursorposition, die du meldest, in Screenshot-Pixel. Behalte die Screenshot-Größe für die gesamte Sitzung bei und gib ihr das Seitenverhältnis des Displays:

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):
        # Punkt ablehnen. Ihn in den gültigen Bereich zu verschieben, würde dort klicken, wo Claude es nicht gewählt hat.
        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)

Rufe to_display für jede coordinate und start_coordinate auf, bevor du darauf reagierst, und to_screenshot für die Position, die cursor_position meldet. Ein Klick oder Scrollen ohne coordinate wirkt an der Cursorposition, daher gibt es nichts umzurechnen. Skaliere eine zoom-region mit demselben Faktor. Ein zoom-Bild ändert daran nichts: Die nächsten Punkte, die Claude sendet, sind weiterhin Pixelpositionen im vollständigen Screenshot.

Das SDK liest oder skaliert niemals ein Bild. Die API lehnt ein Bild ab, das die Limits des Modells überschreitet, wodurch der Lauf endet. Skaliere jeden Screenshot und jedes zoom-Bild im Treiber. Siehe Screenshots an Bildlimits anpassen. Bei einem langen Lauf kann eine Anfrage mehr als 20 Bilder enthalten, und die API wendet dann auf jedes ein strengeres Limit an. Siehe Screenshot-Verlauf verwalten.

Eingabeprüfungen und Ergebnisse

Verlass dich nicht darauf, dass das SDK die Eingabe eines Aufrufs prüft. Lass deinen Treiber die Felder prüfen, die er verwendet, und einen Aufruf ablehnen, den er nicht ausführen kann: Löse ToolError aus (in TypeScript mit throw). Claude liest die Nachricht und kann einen korrigierten Aufruf senden.

Keines der beiden SDKs begrenzt eine duration, eine repeat-Anzahl oder einen scroll_amount. Schritt 4 unter Das Computer-Toolset sicher ausführen behandelt diese.

Was ein Tool zurückgibt, bestimmt, was Claude liest:

ToolGibt zurückClaude liest
screenshot, zoomBetaScreenshotResultEinen Bildblock
cursor_positionBetaComputerCursorPositionResultX={x},Y={y}
Jedes andere ToolNichts oder eine TextzeileEine kurze Bestätigung, etwa Clicked., gefolgt von der zurückgegebenen Zeile in einem eigenen Textblock

Das Computer-Toolset sicher ausführen

Was auf dem Bildschirm zu sehen ist, steuert Claudes nächste Aktion. Eine Seite oder eine Benachrichtigung kann versuchen, Claude dazu zu bringen, in das falsche Fenster zu tippen oder eine Aktion mit realen Auswirkungen auszulösen.

Bevor du einen Treiber auf etwas anderem als einer Wegwerf-Maschine ausführst, befolge diese fünf Schritte:

  1. Isoliere den Desktop. Führe ihn für jede Sitzung in einem dedizierten Container oder einer VM mit minimalen Rechten aus, ohne Anmeldedaten und ohne Host-Mounts. Installiere nur die Anwendungen, die die Aufgabe benötigt. Führe den Code, der die API aufruft, außerhalb davon aus.
  2. Sichere Aktionen mit realen Auswirkungen mit confirm ab. Ein Kauf oder eine gesendete Nachricht ist ein gewöhnliches left_click, type oder key, und confirm erhält das Tool und seine Eingabe, nicht den Bildschirm. Wähle also anhand der Anwendungen, die Claude auf dem Desktop erreichen kann, aus, welche Tools eine Genehmigung benötigen.
  3. Halte Terminals, Ausführen-Dialoge und Launcher vom Desktop fern, es sei denn, die Aufgabe benötigt einen davon. Wenn einer davon den Tastaturfokus hat, führen type, key und hold_key alles aus, was Claude tippt, und ein Klick kann ein Programm starten. Wenn die Aufgabe einen benötigt, frage vor Tastatureingaben und Klicks eine Person und isoliere den Desktop so, dass ein Befehl nicht weiter reichen kann, als die Aufgabe erfordert.
  4. Lehne Eingaben ab, die dein Desktop nicht ausführen sollte, etwa einen Punkt außerhalb des Screenshots, ein wait von mehreren Minuten oder einen sehr großen repeat- oder scroll_amount-Wert. Löse ToolError aus (in TypeScript mit throw), anstatt den Wert in den gültigen Bereich zu verschieben.
  5. Behandle alles auf dem Bildschirm als nicht vertrauenswürdig, einschließlich Fenstertiteln und Text in der Zwischenablage. Führe es nicht aus und gib es nicht ungeprüft weiter.

Das SDK setzt Schritt 2 über das Callable um, das du übergibst. Die anderen Schritte liegen bei deinem Treiber und deinem Deployment. Die Vorsichtsmaßnahmen unter Sicherheitsüberlegungen gelten ebenfalls.

Das folgende confirm fragt vor jeder Tastatureingabe über deine eigene Funktion ask_user eine Person und genehmigt jeden anderen Aufruf. context.member ist der Name des Tools. Damit eine Person auch Klicks genehmigt, setze ASK_FIRST auf 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
    # gezeigt ist die Funktion aus „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)

Ein abgelehnter Aufruf erhält ein Fehlerergebnis, und der Lauf wird fortgesetzt. Bei einem abgelehnten type-Aufruf liest Claude The user did not grant permission to run 'type'. Do not retry it unless the user asks you to.

Dieses confirm genehmigt jedes Tool, das nicht in ASK_FIRST enthalten ist, einschließlich aller Tools, die du später hinzufügst. Um im Zweifel abzulehnen (fail closed), genehmige nur die Tools ohne Auswirkung auf den Desktop (screenshot, zoom, cursor_position und wait) und frage bei jedem anderen Tool nach.

Beide Toolsets in einer Anfrage verwenden

Übergib beide Instanzen in tools. Jeder Aufruf trägt seinen toolset_name, browser oder computer, und der Tool-Runner sendet den Aufruf an die passende Instanz. Nach einem fehlgeschlagenen Aufruf überspringt der Tool-Runner nur die späteren Aufrufe desselben Toolsets in diesem Turn, sodass ein fehlgeschlagener Computer-Aufruf die Browser-Aufrufe des Turns nicht überspringt.

# MyBrowser, backend und url_policy stammen aus dem fortgeschrittenen Beispiel.
# confirm ist die Funktion aus „Run the computer toolset safely“.
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())

Beantworte in einer selbst geschriebenen Schleife jeden Aufruf mit tool_result auf der Instanz, deren toolset_name mit dem toolset_name des Aufrufs übereinstimmt. Verfolge in jedem Turn fehlgeschlagene Aufrufe für jedes Toolset separat und beantworte einen übersprungenen Aufruf mit dem eigenen Text des jeweiligen Toolsets. Ohne den Tool-Runner ausführen enthält den Text des Browsers, und der Aufzählungspunkt zu übersprungenen Aufrufen am Anfang dieses Abschnitts enthält den des Computers.

Referenz

Die Konstruktoroptionen haben in beiden SDKs dieselbe Bedeutung:

PythonTypeScriptLegt fest
configsconfigsWelche Member aktiviert sind
confirmconfirmDas Callable, das jeden Aufruf genehmigt oder ablehnt
url_policyurlPolicyDeine URL-Richtlinie, die das SDK vor jedem navigate zu einer URL aufruft
file_policyfilePolicyUpload-Roots und Offenlegung von Download-Pfaden
tool_configstoolConfigsFelder für den tools-Eintrag, etwa cache_control
Die Methode _browser_statebrowserStateDen Zustandsbericht

Die Browser-Klasse akzeptiert jede Option in der Tabelle. Die Computer-Klasse akzeptiert configs, confirm und tool_configs.

Du kannst eine Option nach der Konstruktion nicht mehr ändern. Der Python-Leitfaden zum Browser-Toolset, der TypeScript-Leitfaden zum Browser-Toolset, der Python-Leitfaden zum Computer-Toolset und der TypeScript-Leitfaden zum Computer-Toolset beschreiben jede Option. Die Python-Leitfäden behandeln außerdem die asynchronen Klassen BetaAsyncAbstractBrowserToolset20260801 und BetaAsyncAbstractComputerToolset20260801.

Einschränkungen

  • Das SDK enthält keinen Browser, keinen Desktop, keinen fertigen Treiber und keine URL-Richtlinie: Du stellst sie bereit. Siehe Partner-Integrationen, Einen Treiber implementieren und Eine URL-Richtlinie schreiben.
  • Die URL-Richtlinie sieht nur navigate-Aufrufe: Sie sieht weder die Anfragen, die eine Seite stellt, noch Seiten, die auf andere Weise geöffnet werden, etwa über einen Link, den Claude anklickt, oder eine Weiterleitung. Siehe Anfragen im Treiber abfangen.
  • Eine Genehmigung basiert auf dem letzten Zustandsbericht: Die Seite kann sich nach diesem Bericht ändern. Das SDK prüft die Seite nicht erneut, bevor der Aufruf ausgeführt wird.
  • Aufrufe auf einem Toolset laufen nacheinander: Du kannst das nicht abschalten.
  • Das SDK prüft weder, ob Tab-IDs eindeutig sind, noch wie viele Tabs es gibt, und prüft nicht immer, ob genau ein Tab aktiv ist: Die API lehnt einen Bericht ab, der gegen diese Regeln verstößt.
  • Bei einem Computer-Aufruf erhält confirm das Tool und seine Eingabe, nicht den Bildschirm: Daraus kannst du nicht erkennen, was ein Klick an einem bestimmten Punkt bewirkt.
  • Das SDK skaliert die Bilder eines Computer-Toolsets nicht: Die API lehnt ein Bild ab, das die Limits des Modells überschreitet. Siehe Koordinaten skalieren und Bildgrößen festlegen.

Nächste Schritte

Die Member-Tools, der browser_state-Block und die Sicherheitsüberlegungen des Tools.

Die 17 Tools, Koordinaten und Bildlimits sowie die Sicherheitsüberlegungen des Tools.

Wie das SDK die Schleife ausführt und wie du die Nachrichten änderst, die es sendet.

Schutzmaßnahmen für jede Anwendung, die nicht vertrauenswürdige Inhalte liest.

Was this page helpful?