---
title: Browser- und Computernutzung mit den SDK-Toolsets
url: https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk
description: 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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-tool) navigiert Claude auf Webseiten, liest sie und interagiert mit ihnen. Über das [Computer-Use-Tool](https://platform.claude.com/docs/de/agents-and-tools/tool-use/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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#use-a-driver-from-a-provider). 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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#computer-toolset) beschreibt, was sich unterscheidet.

Einen eigenen Treiber zu schreiben ist ein fortgeschrittener Fall, der unter [Einen Treiber implementieren](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver) behandelt wird. Das Repository claude-quickstarts enthält außerdem einen [Beispiel-Browser-Treiber](https://github.com/anthropics/claude-quickstarts/tree/main/browser-toolset) in Python und TypeScript. Er steuert Chromium über das Chrome DevTools Protocol (CDP) und ist kein Produktionscode.

<Note>
  Die Browser-Toolset-Klasse befindet sich in der Beta und ist im [Python-SDK](https://github.com/anthropics/anthropic-sdk-python/blob/main/browser-toolset.md) und im [TypeScript-SDK](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/browser-toolset.md) verfügbar. Die Computer-Toolset-Klasse befindet sich in denselben SDKs in der Beta, die einen [Leitfaden zum Computer-Toolset für Python](https://github.com/anthropics/anthropic-sdk-python/blob/main/computer-toolset.md) und einen [Leitfaden zum Computer-Toolset für TypeScript](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/computer-toolset.md) haben.
</Note>

## Einen Treiber eines Anbieters verwenden

Mit einem Treiber eines Anbieters, etwa einem unter [Partner-Integrationen](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#partner-integrations), kannst du einen Agenten in wenigen Codezeilen bauen. Der Anbieter in diesem Beispiel ist erfunden. Ersetze `example_browser_provider` (typescript: `example-browser-provider`) und `ExampleBrowser` durch das Paket und die Treiberklasse [eines echten Anbieters](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#partner-integrations) und richte sie so ein, wie es die Dokumentation des Anbieters beschreibt.

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

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

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

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

Ü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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver).

## Partner-Integrationen

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

<CardGroup cols={2}>
  <Card id="browser-use" title="Browser Use" image="https://platform.claude.com/docs/images/partner-logos/browser-use.svg" imageDark="https://platform.claude.com/docs/images/partner-logos/browser-use-dark.svg" href="https://docs.browser-use.com/open-source/customize/integrations/toolsets-for-claude">
    Browser-Use-Treiber für das Browser-Toolset.
  </Card>

  <Card id="browserbase" title="Browserbase" image="https://platform.claude.com/docs/images/partner-logos/browserbase.svg" imageDark="https://platform.claude.com/docs/images/partner-logos/browserbase-dark.svg" href="https://docs.stagehand.dev/v4/integrations/agent-frameworks/claude-cua-toolset-quickstart">
    Browserbase-Stagehand-Treiber für das Browser-Toolset.
  </Card>

  <Card id="daytona" title="Daytona" image="https://platform.claude.com/docs/images/partner-logos/daytona.svg" imageDark="https://platform.claude.com/docs/images/partner-logos/daytona-dark.svg" href="https://www.daytona.io/docs/en/guides/claude/claude-draws-daytona-sandbox/">
    Daytona-Treiber für das Computer-Toolset.
  </Card>

  <Card id="e2-b" title="E2B" image="https://platform.claude.com/docs/images/partner-logos/e2b.svg" imageDark="https://platform.claude.com/docs/images/partner-logos/e2b-dark.svg" href="https://docs.e2b.dev/agents/claude-toolsets">
    E2B-Treiber für das Browser- und das Computer-Toolset.
  </Card>
</CardGroup>

Unsere Partner veröffentlichen außerdem Code auf GitHub: den [Browser-Use-Schnellstart](https://github.com/browser-use/browser-use/tree/main/examples/integrations/toolsets-for-claude), das [Browserbase-Toolset](https://github.com/browserbase/claude-cua-toolset) und das [E2B-Beispiel](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/anthropic-computer-use-orangehrm-js).

<Warning>
  Erledige diese zwei Dinge, bevor du einen Treiber gegen etwas anderes als einen Wegwerf-Browser ausführst:

  * [Schreibe eine URL-Richtlinie](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy). Ohne URL-Richtlinie prüft das SDK keine URL. Lies bei einem Browser, den ein Anbieter hostet, außerdem [Entfernte und gehostete Browser](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#remote-and-hosted-browsers).
  * Verwende ein Browserprofil, das bei keinem Konto angemeldet ist, dessen Daten oder Aktionen du Claude nicht überlassen würdest. Siehe [Browser-Host isolieren](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#isolate-the-browser-host).

  [Das Toolset sicher ausführen](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#run-the-toolset-safely) behandelt den Rest.
</Warning>

## Einen Treiber anpassen

Die Beispiele in diesem Abschnitt verwenden `MyBrowser`, den Treiber aus dem [fortgeschrittenen Beispiel](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#quick-start) weiter unten auf dieser Seite.

### Member aktivieren oder deaktivieren

`configs` nimmt die Einstellungen pro Member entgegen, die unter [Das Toolset konfigurieren](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-tool#configure-the-toolset) beschrieben sind. Liste nur die Member auf, die du änderst:

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  # Ein MyBrowser, der zusätzlich read_console implementiert
  browser = MyBrowser(
      backend, configs={"read_console": {"enabled": True}, "navigate": {"enabled": False}}
  )
  ```

  ```typescript TypeScript
  // Ein MyBrowser, der zusätzlich read_console implementiert
  const browser = new MyBrowser(backend, {
    configs: { read_console: { enabled: true }, navigate: { enabled: false } }
  });
  ```
</CodeGroup>

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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy), der [Datei-Richtlinie](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#confine-uploads-and-downloads) und [`confirm`](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#gate-consequential-members), 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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#quick-start) 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.

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


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

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

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

## 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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#add-before-and-after-hooks)). Jeder Member erhält den Aufrufkontext und die Eingabe des Members als typisiertes Objekt, etwa `BetaBrowserNavigateInput`. Die Eingabetypen stammen aus `anthropic.types.beta` (typescript: `@anthropic-ai/sdk/resources/beta`). [Member-Tools](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-tool#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.

| Member                                                                                  | Gibt zurück                                                                                                       | Claude liest                                                                                    |
| --------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `screenshot`, `zoom`                                                                    | `BetaScreenshotResult`                                                                                            | Einen Bildblock                                                                                 |
| `navigate`                                                                              | `BetaBrowserNavigateResult`                                                                                       | `Navigated to {url} — {title} (HTTP {status})`                                                  |
| `new_tab`, `switch_tab`, `list_tabs`, `close_tab`                                       | Einen 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_exec` | Einen String                                                                                                      | Den String oder `(empty)` bei einem leeren String                                               |
| Jedes andere Member                                                                     | Nichts oder eine Textzeile                                                                                        | Eine kurze Bestätigung wie `Clicked.`, dann die zurückgegebene Zeile in einem eigenen Textblock |

### Browserzustand melden

Das SDK ruft `_browser_state` (typescript: `browserState`) 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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#intercept-requests-in-the-driver) 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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-tool#track-tabs-and-page-state) 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 SDK | Claude liest                                                                           | Der Lauf         |
| --------------------------------------- | -------------------------------------------------------------------------------------- | ---------------- |
| `ToolError`                             | Seine Nachricht, als Fehlerergebnis                                                    | Wird fortgesetzt |
| `ToolsetUsageError`                     | Nichts                                                                                 | Stoppt           |
| Jede andere Exception                   | `ClassName: message` in Python oder `Error: message` in TypeScript, als Fehlerergebnis | Wird 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` (typescript: `browserState`) irgendeine Exception auslöst, selbst einen `ToolError`. Der Tool-Runner und `tool_result` (typescript: `toolResult`) 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` (typescript: `stream: true`) und `run_tools_eagerly=True` (typescript: `runToolsEagerly: true`) an den Tool-Runner, wie es das [fortgeschrittene Beispiel](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#quick-start) 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` (typescript: `toolResult`). Das Browser-Use-Tool verlangt, dass du beim ersten fehlgeschlagenen Aufruf stoppst ([Batch-Aktionen](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-tool#batch-actions)).

Diese Schleife verwendet `MyBrowser` und die URL-Richtlinie aus dem [fortgeschrittenen Beispiel](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#quick-start). Nach einem fehlgeschlagenen Aufruf beantwortet sie die späteren Aufrufe des Turns, ohne sie auszuführen:

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

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

  with MyBrowser(backend, url_policy=url_policy) as browser:
      messages: list[BetaMessageParam] = [{"role": "user", "content": "Open example.com"}]
      for _ in range(MAX_TURNS):
          response = client.beta.messages.create(
              model="claude-opus-5-5",
              max_tokens=1024,
              tools=[browser],
              messages=messages,
          )
          messages.append({"role": "assistant", "content": response.content})
          calls = [
              block
              for block in response.content
              if block.type == "tool_use" and block.toolset_name == browser.toolset_name
          ]
          if not calls:
              break
          results: list[BetaToolResultBlockParam] = []
          failed = False
          for call in calls:
              if failed:
                  # 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})
  ```

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

  const browser = new MyBrowser(backend, { urlPolicy });
  try {
    const messages: Anthropic.Beta.BetaMessageParam[] = [
      { role: "user", content: "Open example.com" }
    ];
    for (let turn = 0; turn < MAX_TURNS; turn++) {
      const response = await client.beta.messages.create({
        model: "claude-opus-5-5",
        max_tokens: 1024,
        tools: [browser.toJSON()],
        messages
      });
      messages.push({ role: "assistant", content: response.content });
      const calls = response.content.filter(
        (block): block is Anthropic.Beta.BetaToolUseBlock =>
          block.type === "tool_use" && block.toolset_name === browser.toolsetName
      );
      if (calls.length === 0) {
        break;
      }
      const results: Anthropic.Beta.BetaToolResultBlockParam[] = [];
      let failed = false;
      for (const call of calls) {
        if (failed) {
          // Nach einem fehlgeschlagenen Aufruf wird der Rest des Turns beantwortet, nicht ausgeführt.
          results.push({
            type: "tool_result",
            tool_use_id: call.id,
            toolset_name: call.toolset_name,
            content: NOT_EXECUTED,
            is_error: true
          });
          continue;
        }
        const result = await browser.toolResult(call);
        failed = result.is_error === true;
        results.push(result);
      }
      messages.push({ role: "user", content: results });
    }
  } finally {
    await browser.close();
  }
  ```
</CodeGroup>

Jede Antwort auf einen übersprungenen Aufruf enthält `is_error`, den `toolset_name` des Aufrufs und genau den Text, den der Abschnitt [Batch-Aktionen](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-tool#batch-actions) verlangt. Der Tool-Runner sendet dieselbe Antwort.

## Fortgeschrittenes Beispiel: einen eigenen Treiber schreiben

Dieses Beispiel fasst die Hauptteile von [Einen Treiber implementieren](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver) in einem Treiber, `MyBrowser`, zusammen und führt ihn mit dem Tool-Runner und [frühem Start](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#start-calls-while-the-response-streams) aus. `MyBrowser` implementiert `navigate`, `screenshot` und `left_click` sowie `_browser_state` (typescript: `browserState`), 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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy) erklärt sie.

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

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


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

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

      def navigate(
          self, context: BetaToolsetCallContext, input: BetaBrowserNavigateInput
      ) -> BetaBrowserNavigateResult:
          # input.url 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())
  ```

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

  class MyBrowser extends BetaAbstractBrowserToolset20260801 {
    constructor(
      private backend: Backend,
      options: Omit<BetaBrowserToolsetOptions, "browserState"> = {}
    ) {
      super({
        ...options,
        // Erforderlich: jeder offene Tab und was sich seit dem letzten Bericht geändert hat.
        browserState: () => ({
          tabs: backend.tabs().map((tab) => ({
            tab_id: tab.id,
            title: tab.title,
            url: tab.url,
            active: tab.id === backend.active
          })),
          state_changes: backend.drainChanges()
        })
      });
    }

    protected override async navigate(
      ctx: BetaToolsetCallContext,
      input: BetaBrowserNavigateInput
    ): Promise<BetaBrowserNavigateResult> {
      // input.url ist "back", "forward", "reload" oder eine URL, die deine URL-Richtlinie
      // erlaubt hat, so wie Claude sie geschrieben hat. Wie isAllowed ergänzt backend.goto https:// bei
      // einer URL, die nicht mit einem Schema beginnt.
      const page = await this.backend.goto(input.url, input.tab_id);
      return { url: page.url, status: page.status, title: page.title };
    }

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

    protected override async left_click(
      ctx: BetaToolsetCallContext,
      input: BetaBrowserLeftClickInput
    ): Promise<void> {
      // Keine Rückgabe nötig: Claude liest "Clicked."
      await this.backend.click(input.target, input.tab_id);
    }

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

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

  // Ein Beispiel, keine Richtlinie für die Produktion. Siehe "Write a URL policy".
  function isAllowed(url: string): boolean {
    if (url.toLowerCase() === "about:blank") {
      return true;
    }
    const withScheme = SCHEME_PREFIX.test(url) ? url : `https://${url}`;
    // Ein Browser liest "\" in einer Web-URL als "/".
    const parsed = URL.parse(withScheme.replaceAll("\\", "/"));
    const host = parsed?.hostname ?? "";
    const listed = ALLOWED_HOSTS.some((name) => host === name || host.endsWith(`.${name}`));
    return (parsed?.protocol === "http:" || parsed?.protocol === "https:") && listed;
  }

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

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

## 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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy), die jede `navigate`-URL ablehnt, die die Aufgabe nicht benötigt.
2. [Fange Anfragen im Treiber ab](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#intercept-requests-in-the-driver) und prüfe jede Anfrage mit denselben Regeln.
3. [Lege eine „egress policy" (Richtlinie für ausgehenden Datenverkehr) auf den Container](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container), damit das Netzwerk blockiert, was der Treiber nicht sehen kann.
4. [Beschränke Uploads und Downloads](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#confine-uploads-and-downloads) oder lass Uploads deaktiviert.
5. [Sichere folgenreiche Member](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#gate-consequential-members) mit `confirm` ab.
6. [Isoliere den Browser-Host](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#isolate-the-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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-tool#security-considerations) gelten ebenfalls.

<Note>
  Die Prompt-Injection-Klassifikatoren von Anthropic, beschrieben unter [Sicherheitsüberlegungen](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-tool#security-considerations), laufen derzeit nicht bei Browser-Toolset-Anfragen, die über Amazon Bedrock gesendet werden.
</Note>

### Eine URL-Richtlinie schreiben

<Warning>
  Ohne URL-Richtlinie prüft das SDK keine URL, und die API filtert die URLs nicht, die Claude öffnet. Selbst mit einer Richtlinie kann ein Link, den Claude anklickt, oder eine Weiterleitung den Browser zu Loopback (`localhost`, `127.0.0.1`), zu Link-Local-Adressen wie der Cloud-Metadatenadresse `169.254.169.254` und zu privaten Netzwerkbereichen führen. [Egress-Regeln auf dem Container](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container) können die Link-Local- und privaten Adressen blockieren. Regeln, die außerhalb des Containers durchgesetzt werden, können Loopback nicht blockieren.

  Das SDK prüft das Schema einer URL nicht, daher muss dein Treiber jedes Schema ablehnen, das er nicht öffnen soll, etwa `javascript:`, `view-source:`, `data:` und `file:`. Die meisten Treiber benötigen nur `http` und `https`. Die Beispielrichtlinie unten erlaubt nur `http`- und `https`-URLs sowie `about:blank`.

  Eine `file:`-URL lässt Claude Dateien auf dem Browser-Host lesen. Eine `javascript:`-URL führt Skript in der aktuellen Seite aus, selbst wenn `javascript_exec` deaktiviert ist. Egress-Regeln stoppen keines von beiden.
</Warning>

Eine URL-Richtlinie ist eine Funktion, die du als `url_policy` (typescript: `urlPolicy`) ü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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#quick-start). Sie erlaubt `about:blank` sowie zwei Sites und ihre Subdomains über `http` und `https`:

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

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

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


  def is_allowed(url: str) -> bool:
      # 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)
  ```

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

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

  // Ein Beispiel, keine Richtlinie für den Produktivbetrieb.
  function isAllowed(url: string): boolean {
    if (url.toLowerCase() === "about:blank") {
      return true;
    }
    const withScheme = SCHEME_PREFIX.test(url) ? url : `https://${url}`;
    // Ein Browser liest "\" in einer Web-URL als "/".
    const parsed = URL.parse(withScheme.replaceAll("\\", "/"));
    const host = parsed?.hostname ?? "";
    const listed = ALLOWED_HOSTS.some((name) => host === name || host.endsWith(`.${name}`));
    return (parsed?.protocol === "http:" || parsed?.protocol === "https:") && listed;
  }

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

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

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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#intercept-requests-in-the-driver).
* 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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container).

Wenn du `url_policy=None` (typescript: `urlPolicy: null`) übergibst, lehnt das SDK jedes `navigate` zu einer URL ab, sodass ein aus deiner Konfiguration gelesenes `None` (typescript: `null`) 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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container), 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:

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  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_()
  ```

  ```typescript TypeScript
  // In der Treiberklasse, auf einem Playwright-Browserkontext.
  // isAllowed ist die Funktion aus „Write a URL policy“.
  await context.route("**/*", (route) =>
    isAllowed(route.request().url()) ? route.continue() : route.abort("blockedbyclient")
  );
  ```
</CodeGroup>

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` (typescript: `isAllowed`) ü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` (typescript: `filePolicy`) lehnt das SDK jeden Upload ab, der einen Pfad oder eine [Dokument-ID](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-tool#upload-files) angibt. Um Uploads zu aktivieren, übergib eine `BetaLocalFilePolicy` (typescript: `BetaNodeFilePolicy`) mit einem Upload-Verzeichnis, das nur die Dateien der Aufgabe enthält:

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  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,
      ),
  )
  ```

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

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

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` (typescript: `exposeDownloadPaths`) 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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#remote-and-hosted-browsers).

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` (typescript: `true`) zurück, um den Aufruf auszuführen, oder `False` (typescript: `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` (typescript: `askUser`) nach den zwei abgesicherten Membern und genehmigt den Rest:

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

  from anthropic.tools.browser import BetaConfirmContext

  GATED = {"javascript_exec", "file_upload"}


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


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

      def confirm(context: BetaConfirmContext) -> bool:
          name = context.member
          if name not in GATED:
              return True
          detail = shown(context.input.to_dict())
          page = context.tab_url
          where = shown(page) if page else "a tab with no URL"
          question = f"Allow {name} on {where}?\n{detail}"
          if page is None or urlsplit(page).scheme not in ("http", "https"):
              return ask_user(question)  # no web origin: ask every time
          # 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,
  )
  ```

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

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

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

  const makeConfirm = () => {
    const granted = new Set<string>();
    return async (ctx: BetaConfirmContext): Promise<boolean> => {
      const name = ctx.member;
      if (!GATED.has(name)) {
        return true;
      }
      const detail = shown(ctx.input);
      const page = ctx.tabURL;
      const where = page ? shown(page) : "a tab with no URL";
      const question = `Allow ${name} on ${where}?\n${detail}`;
      const scheme = page === undefined ? undefined : URL.parse(page)?.protocol;
      if (scheme !== "http:" && scheme !== "https:") {
        return askUser(question); // no web origin: ask every time
      }
      // Eine Freigabe gilt nur für genau diese Eingabe auf dieser Seite.
      const key = `${name} ${page} ${detail}`;
      if (!granted.has(key) && (await askUser(question))) {
        granted.add(key);
      }
      return granted.has(key);
    };
  };

  // Ein MyBrowser, der zusätzlich javascript_exec und file_upload implementiert
  // file_upload benötigt außerdem die filePolicy aus "Confine uploads and downloads"
  const browser = new MyBrowser(backend, {
    configs: { javascript_exec: { enabled: true }, file_upload: { enabled: true } },
    confirm: makeConfirm(),
    urlPolicy
  });
  ```
</CodeGroup>

Jeder Aufruf von `make_confirm()` (typescript: `makeConfirm()`) 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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container) 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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy) 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.

<Warning>
  Das Browserprofil darf bei keinem Konto angemeldet sein, dessen Daten oder Aktionen du Claude nicht überlassen würdest. `javascript_exec` läuft mit der eigenen Autorität der Seite: ihren Cookies, ihrem Speicher und ihren angemeldeten Sitzungen.
</Warning>

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` (typescript: `toolResult`) 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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#remote-and-hosted-browsers) 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` (typescript: `BetaNodeFilePolicy`) 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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#confine-uploads-and-downloads).

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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#partner-integrations). Wenn du deinen eigenen schreiben möchtest, enthält das Repository claude-quickstarts ein [minimales VNC-Beispiel](https://github.com/anthropics/claude-quickstarts/tree/main/computer-toolset) in Python und TypeScript. Es steuert einen Desktop über VNC und ist kein Produktionscode.

Die Klasse nimmt `configs`, `confirm` und `tool_configs` (typescript: `toolConfigs`) entgegen, und du übergibst sie wie bei der Browser-Klasse. [Einen Treiber anpassen](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#customize-a-driver), [Fehler behandeln](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#handle-errors), [Aufrufe starten, während die Antwort gestreamt wird](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#start-calls-while-the-response-streams) und [Ohne den Tool-Runner ausführen](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#run-without-the-tool-runner) gelten auch für sie, mit diesen Unterschieden:

* **Nur-Browser-Optionen:** Die Klasse hat keine [URL-Richtlinie](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy), keine [Datei-Richtlinie](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#confine-uploads-and-downloads) und keinen [Zustandsbericht](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#report-browser-state).
* **`configs`:** Jedes Tool, das du implementierst, ist standardmäßig aktiviert. Die Einstellungen pro Tool sind unter [Tool-Parameter](https://platform.claude.com/docs/de/agents-and-tools/tool-use/computer-use-tool#tool-parameters) 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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/computer-use-tool#batch-actions)).
* **`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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/computer-use-tool#available-actions) aufgeführt sind. Ein Tool, das du nicht implementierst, wird als deaktiviert an die API gesendet, wie [Einen Treiber implementieren](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver) 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` (typescript: `askUser`) steht für deine eigene Funktion, die eine Person fragt.

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


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

      def screenshot(
          self, context: BetaToolsetCallContext, input: BetaComputerScreenshotInput
      ) -> BetaScreenshotResult:
          # 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)
  ```

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

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

    protected override async screenshot(
      ctx: BetaToolsetCallContext,
      input: BetaComputerScreenshotInput
    ): Promise<BetaScreenshotResult> {
      // Claudes Koordinaten sind Pixelpositionen in diesem Bild.
      return { data: await this.display.pngBase64(), mediaType: "image/png" };
    }

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

    protected override async left_click(
      ctx: BetaToolsetCallContext,
      input: BetaComputerLeftClickInput
    ): Promise<void> {
      // input.coordinate ist [x, y] oder null bzw. undefined, um an der Cursorposition zu klicken. input.text
      // ist eine gedrückt zu haltende Modifikatortaste, z. B. "shift". Keine Rückgabe nötig: Claude liest "Clicked."
      await this.display.click(input.coordinate, input.text);
    }

    // Das type-Tool wird in TypeScript als type_ geschrieben.
    protected override async type_(
      ctx: BetaToolsetCallContext,
      input: BetaComputerTypeInput
    ): Promise<void> {
      await this.display.type(input.text);
    }

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

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

  const client = new Anthropic();
  const desktop = new MyDesktop(display, {
    // erforderlich für type und key; siehe Run the computer toolset safely
    confirm: (ctx) => askUser(`Allow the "${ctx.member}" tool?`)
  });
  try {
    const runner = client.beta.messages.toolRunner({
      model: "claude-opus-5-5",
      max_tokens: 1024,
      tools: [desktop],
      messages: [{ role: "user", content: "Open the calculator and compute 17 * 23." }]
    });
    for await (const message of runner) {
      console.log(message);
    }
  } finally {
    await desktop.close();
  }
  ```
</CodeGroup>

<Warning>
  Das `confirm` in diesem Beispiel fragt vor jedem Aufruf, den der Treiber ausführt, eine Person. Bevor du den Treiber gegen etwas anderes als eine Wegwerf-Maschine ausführst, lies [Das Computer-Toolset sicher ausführen](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#run-the-computer-toolset-safely).
</Warning>

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` (typescript: `@anthropic-ai/sdk/resources/beta`).

Schreibe Tools in TypeScript als Methoden, nicht als Arrow-Function-Felder. [Einen Treiber implementieren](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver) 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:

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

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


  def to_display(coordinate: list[int]) -> tuple[int, int]:
      x, y = coordinate
      width, height = SCREENSHOT_SIZE
      if not (0 <= x < width and 0 <= y < height):
          # 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)
  ```

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

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

  function toDisplay(coordinate: number[]): [number, number] {
    const [x, y] = coordinate;
    const { width, height } = SCREENSHOT_SIZE;
    if (x < 0 || x >= width || y < 0 || y >= height) {
      // Punkt ablehnen. Ihn in den Bereich zu verschieben, würde dort klicken, wo Claude es nicht gewählt hat.
      throw new ToolError(`[${x}, ${y}] is outside the ${width}x${height} screenshot`);
    }
    return [
      Math.floor((x * DISPLAY_SIZE.width) / width),
      Math.floor((y * DISPLAY_SIZE.height) / height)
    ];
  }

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

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

Rufe `to_display` (typescript: `toDisplay`) für jede `coordinate` und `start_coordinate` auf, bevor du darauf reagierst, und `to_screenshot` (typescript: `toScreenshot`) 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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/computer-use-tool#handle-coordinate-scaling-for-higher-resolutions). 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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/computer-use-tool#manage-screenshot-history).

### 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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#run-the-computer-toolset-safely) behandelt diese.

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

| Tool                 | Gibt zurück                        | Claude liest                                                                                              |
| -------------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `screenshot`, `zoom` | `BetaScreenshotResult`             | Einen Bildblock                                                                                           |
| `cursor_position`    | `BetaComputerCursorPositionResult` | `X={x},Y={y}`                                                                                             |
| Jedes andere Tool    | Nichts oder eine Textzeile         | Eine 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.

<Warning>
  Ein Treiber, der `type`, `key` oder `hold_key` implementiert, benötigt ein `confirm`-Callable. Ohne eines löst der Konstruktor einen Konfigurationsfehler aus, es sei denn, `configs` deaktiviert diese Tools. Das SDK selbst fragt niemanden: Es führt jeden Aufruf aus, den dein `confirm` genehmigt, und ohne `confirm` führt es jeden Aufruf aus, einschließlich jedes Klicks. Jedes Computer-Tool, das du implementierst, ist standardmäßig aktiviert.
</Warning>

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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/computer-use-tool#security-considerations) gelten ebenfalls.

Das folgende `confirm` fragt vor jeder Tastatureingabe über deine eigene Funktion `ask_user` (typescript: `askUser`) eine Person und genehmigt jeden anderen Aufruf. `context.member` (typescript: `ctx.member`) ist der Name des Tools. Damit eine Person auch Klicks genehmigt, setze `ASK_FIRST` auf `KEYBOARD | CLICKS` (typescript: `new Set([...KEYBOARD, ...CLICKS])`).

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

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


  def confirm(context: BetaComputerConfirmContext) -> bool:
      if context.member not in ASK_FIRST:
          return True
      # 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)
  ```

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

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

  async function confirm(ctx: BetaComputerConfirmContext): Promise<boolean> {
    if (!ASK_FIRST.has(ctx.member)) {
      return true;
    }
    // gezeigt ist die Funktion aus „Gate consequential members“.
    return askUser(`Allow the "${ctx.member}" tool with this input?\n${shown(ctx.input)}`);
  }

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

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.

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  # 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())
  ```

  ```typescript TypeScript
  // MyBrowser, backend und urlPolicy stammen aus dem fortgeschrittenen Beispiel.
  // confirm ist die Funktion aus „Das Computer-Toolset sicher ausführen“.
  const browser = new MyBrowser(backend, { urlPolicy });
  const desktop = new MyDesktop(display, { confirm });
  try {
    const runner = client.beta.messages.toolRunner({
      model: "claude-opus-5-5",
      max_tokens: 1024,
      tools: [browser, desktop],
      messages: [
        { role: "user", content: "Copy the heading on example.com into the open text editor." }
      ],
      stream: true,
      runToolsEagerly: true // so a call can start before the response ends
    });
    for await (const stream of runner) {
      console.log(await stream.finalMessage());
    }
  } finally {
    await Promise.all([browser.close(), desktop.close()]);
  }
  ```
</CodeGroup>

Beantworte in einer selbst geschriebenen Schleife jeden Aufruf mit `tool_result` (typescript: `toolResult`) auf der Instanz, deren `toolset_name` (typescript: `toolsetName`) 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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#run-without-the-tool-runner) 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:

| Python                       | TypeScript     | Legt fest                                                                   |
| ---------------------------- | -------------- | --------------------------------------------------------------------------- |
| `configs`                    | `configs`      | Welche Member aktiviert sind                                                |
| `confirm`                    | `confirm`      | Das Callable, das jeden Aufruf genehmigt oder ablehnt                       |
| `url_policy`                 | `urlPolicy`    | Deine URL-Richtlinie, die das SDK vor jedem `navigate` zu einer URL aufruft |
| `file_policy`                | `filePolicy`   | Upload-Roots und Offenlegung von Download-Pfaden                            |
| `tool_configs`               | `toolConfigs`  | Felder für den `tools`-Eintrag, etwa `cache_control`                        |
| Die Methode `_browser_state` | `browserState` | Den Zustandsbericht                                                         |

Die Browser-Klasse akzeptiert jede Option in der Tabelle. Die Computer-Klasse akzeptiert `configs`, `confirm` und `tool_configs` (typescript: `toolConfigs`).

Du kannst eine Option nach der Konstruktion nicht mehr ändern. Der [Python-Leitfaden zum Browser-Toolset](https://github.com/anthropics/anthropic-sdk-python/blob/main/browser-toolset.md), der [TypeScript-Leitfaden zum Browser-Toolset](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/browser-toolset.md), der [Python-Leitfaden zum Computer-Toolset](https://github.com/anthropics/anthropic-sdk-python/blob/main/computer-toolset.md) und der [TypeScript-Leitfaden zum Computer-Toolset](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/computer-toolset.md) 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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#partner-integrations), [Einen Treiber implementieren](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver) und [Eine URL-Richtlinie schreiben](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy).
* **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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#intercept-requests-in-the-driver).
* **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](https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-sdk#scale-coordinates-and-size-images).

## Nächste Schritte

<CardGroup cols={2}>
  <Card title="Browser-Use-Tool" icon="browser" href="https://platform.claude.com/docs/de/agents-and-tools/tool-use/browser-use-tool">
    Die Member-Tools, der `browser_state`-Block und die Sicherheitsüberlegungen des Tools.
  </Card>

  <Card title="Computer-Use-Tool" icon="computer" href="https://platform.claude.com/docs/de/agents-and-tools/tool-use/computer-use-tool">
    Die 17 Tools, Koordinaten und Bildlimits sowie die Sicherheitsüberlegungen des Tools.
  </Card>

  <Card title="Tool-Runner (SDK)" icon="arrows-clockwise" href="https://platform.claude.com/docs/de/agents-and-tools/tool-use/tool-runner">
    Wie das SDK die Schleife ausführt und wie du die Nachrichten änderst, die es sendet.
  </Card>

  <Card title="Jailbreaks und Prompt-Injections abwehren" icon="shield" href="https://platform.claude.com/docs/de/test-and-evaluate/strengthen-guardrails/mitigate-jailbreaks">
    Schutzmaßnahmen für jede Anwendung, die nicht vertrauenswürdige Inhalte liest.
  </Card>
</CardGroup>
