---
title: Utilisation du navigateur et de l'ordinateur avec les ensembles d'outils du SDK
url: https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk
description: Exécutez l'outil d'utilisation du navigateur ou l'outil d'utilisation de l'ordinateur depuis le SDK Python ou TypeScript. Le SDK exécute la boucle et les vérifications que vous configurez, et vous fournissez le navigateur ou le bureau.
---

Avec les « toolsets » (ensembles d'outils) du navigateur et de l'ordinateur des SDK Python et TypeScript, vous pouvez créer un agent qui travaille dans un navigateur web ou sur un bureau. Il peut utiliser des applications dépourvues d'interface programmatique. Grâce à l'[outil d'utilisation du navigateur](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-tool), Claude navigue, lit et interagit avec des pages web. Grâce à l'[outil d'utilisation de l'ordinateur](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/computer-use-tool), Claude voit un bureau sous forme de captures d'écran et utilise la souris et le clavier.

Le SDK connecte Claude à votre navigateur ou à votre bureau. Il exécute les vérifications que vous configurez, comme une « URL policy » (politique d'URL) pour `navigate` ou un callback d'approbation. Il envoie à votre « driver » (pilote) chaque appel qui passe ces vérifications et construit le `tool_result` à partir de ce que le pilote renvoie. Le [« tool runner » (exécuteur d'outils)](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/tool-runner) peut exécuter la « agentic loop » (boucle agentique) pour vous.

Vous fournissez le pilote, qui fait fonctionner le navigateur ou le bureau. Commencez par le [pilote d'un fournisseur](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#use-a-driver-from-a-provider). Avec l'un d'eux, un agent ne demande que quelques lignes de code. Si aucun ne convient, écrivez le vôtre avec une bibliothèque d'automatisation de navigateur comme Playwright.

La majeure partie de cette page porte sur l'ensemble d'outils du navigateur. [Ensemble d'outils de l'ordinateur](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#computer-toolset) couvre ce qui diffère.

Écrire votre propre pilote est un cas avancé, traité dans [Implémenter un pilote](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver). Le dépôt claude-quickstarts contient également un [exemple de pilote de navigateur](https://github.com/anthropics/claude-quickstarts/tree/main/browser-toolset) en Python et en TypeScript. Il contrôle Chromium via le Chrome DevTools Protocol (CDP), et ce n'est pas du code de production.

<Note>
  La classe de l'ensemble d'outils du navigateur est en bêta et disponible dans le [SDK Python](https://github.com/anthropics/anthropic-sdk-python/blob/main/browser-toolset.md) et le [SDK TypeScript](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/browser-toolset.md). La classe de l'ensemble d'outils de l'ordinateur est en bêta dans les mêmes SDK, qui proposent un [guide de l'ensemble d'outils de l'ordinateur pour Python](https://github.com/anthropics/anthropic-sdk-python/blob/main/computer-toolset.md) et un [guide de l'ensemble d'outils de l'ordinateur pour TypeScript](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/computer-toolset.md).
</Note>

## Utiliser le pilote d'un fournisseur

Avec le pilote d'un fournisseur, comme ceux présentés sous [Intégrations partenaires](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#partner-integrations), vous pouvez créer un agent en quelques lignes de code. Le fournisseur de cet exemple est fictif. Remplacez `example_browser_provider` (typescript: `example-browser-provider`) et `ExampleBrowser` par le paquet et la classe de pilote d'[un vrai fournisseur](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#partner-integrations), et configurez-les comme l'indique la documentation du fournisseur.

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

Passez l'instance du pilote elle-même comme entrée de `tools`. Le runner ne ferme jamais l'ensemble d'outils, de sorte qu'une même instance peut servir à plusieurs exécutions. Fermez-la lorsque vous avez terminé.

Pour écrire votre propre pilote, consultez [Implémenter un pilote](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver).

## Intégrations partenaires

Browser Use, Browserbase, Daytona et E2B publient leurs propres pilotes pour les ensembles d'outils du SDK.

<CardGroup cols={2}>
  <Card id="browser-use" title="Browser Use" image="https://platform.claude.com/docs/images/partner-logos/browser-use.svg" imageDark="https://platform.claude.com/docs/images/partner-logos/browser-use-dark.svg" href="https://docs.browser-use.com/open-source/customize/integrations/toolsets-for-claude">
    Pilote Browser Use pour l'ensemble d'outils du navigateur.
  </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">
    Pilote Browserbase Stagehand pour l'ensemble d'outils du navigateur.
  </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/">
    Pilote Daytona pour l'ensemble d'outils de l'ordinateur.
  </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">
    Pilotes E2B pour les ensembles d'outils du navigateur et de l'ordinateur.
  </Card>
</CardGroup>

Nos partenaires publient également du code sur GitHub : le [démarrage rapide Browser Use](https://github.com/browser-use/browser-use/tree/main/examples/integrations/toolsets-for-claude), l'[ensemble d'outils Browserbase](https://github.com/browserbase/claude-cua-toolset) et l'[exemple E2B](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/anthropic-computer-use-orangehrm-js).

<Warning>
  Faites ces deux choses avant d'exécuter un pilote sur autre chose qu'un navigateur jetable :

  * [Écrivez une politique d'URL](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy). Sans elle, le SDK ne vérifie aucune URL. Pour un navigateur hébergé par un fournisseur, lisez également [Navigateurs distants et hébergés](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#remote-and-hosted-browsers).
  * Utilisez un profil de navigateur qui n'est connecté à aucun compte dont vous ne confieriez pas les données ou les actions à Claude. Consultez [Isoler l'hôte du navigateur](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#isolate-the-browser-host).

  [Exécuter l'ensemble d'outils en toute sécurité](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#run-the-toolset-safely) couvre le reste.
</Warning>

## Personnaliser un pilote

Les exemples de cette section utilisent `MyBrowser`, le pilote de l'[exemple avancé](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#quick-start) présenté plus loin sur cette page.

### Activer ou désactiver des membres

`configs` accepte les paramètres par membre décrits sous [Configurer l'ensemble d'outils](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-tool#configure-the-toolset). Ne listez que les membres que vous modifiez :

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  # Un MyBrowser qui implémente également read_console.
  browser = MyBrowser(
      backend, configs={"read_console": {"enabled": True}, "navigate": {"enabled": False}}
  )
  ```

  ```typescript TypeScript
  // Un MyBrowser qui implémente également read_console
  const browser = new MyBrowser(backend, {
    configs: { read_console: { enabled: true }, navigate: { enabled: false } }
  });
  ```
</CodeGroup>

Le SDK refuse un appel à un membre désactivé avant que votre code ne s'exécute. Activer un membre que votre classe n'implémente pas est une erreur de configuration, sauf si la classe redéfinit `execute`.

### Ajouter des hooks avant et après

Redéfinissez `execute` et appelez le `execute` du parent. Le code placé avant cet appel s'exécute après la [politique d'URL](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy), la [politique de fichiers](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#confine-uploads-and-downloads) et [`confirm`](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#gate-consequential-members), et il peut modifier l'entrée. Le SDK ne vérifie pas à nouveau l'entrée modifiée. Le code placé après l'appel reçoit le résultat et peut le modifier. Levez `ToolError` (lancez-la avec throw en TypeScript) pour refuser l'appel.

Redéfinir `execute` change les membres proposés à Claude. Le SDK considère chaque membre comme implémenté, de sorte que Claude se voit proposer tous les membres activés par défaut. Le `MyBrowser` de l'[exemple avancé](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#quick-start) sert trois membres, donc le `TracedBrowser` suivant propose à Claude des membres qu'il ne peut pas servir. Désactivez ces membres avec `configs` avant de l'utiliser.

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

## Implémenter un pilote

Un pilote est votre sous-classe de `BetaAbstractBrowserToolset20260801`. Dans celle-ci, redéfinissez les membres que votre navigateur prend en charge. Un membre que vous n'implémentez pas est envoyé à l'API comme désactivé. Si Claude l'appelle malgré tout, le SDK renvoie une erreur et l'exécution continue. Redéfinir `execute` change les membres envoyés comme désactivés ([Ajouter des hooks avant et après](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#add-before-and-after-hooks)). Chaque membre reçoit le contexte de l'appel et l'entrée du membre sous forme d'objet typé, comme `BetaBrowserNavigateInput`. Les types d'entrée proviennent de `anthropic.types.beta` (typescript: `@anthropic-ai/sdk/resources/beta`). [Outils membres](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-tool#member-tools) liste les champs de chaque entrée.

En TypeScript, écrivez les membres sous forme de méthodes, et non de champs de fonctions fléchées, car le SDK les trouve sur le prototype. Écrivez le membre `type` sous la forme `type_` en TypeScript. En Python, c'est `type`.

### Renvoyer des résultats

Ce que renvoie un membre détermine ce que Claude lit. Un résultat réussi se termine par un bloc `browser_state` construit à partir de votre rapport d'état. Un résultat d'erreur ne contient aucun bloc.

| Membre                                                                                  | Renvoie                                                                                                           | Claude lit                                                                                       |
| --------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `screenshot`, `zoom`                                                                    | `BetaScreenshotResult`                                                                                            | Un bloc image                                                                                    |
| `navigate`                                                                              | `BetaBrowserNavigateResult`                                                                                       | `Navigated to {url} — {title} (HTTP {status})`                                                   |
| `new_tab`, `switch_tab`, `list_tabs`, `close_tab`                                       | Une entrée d'onglet (`new_tab`, `switch_tab`), une liste d'entrées d'onglets (`list_tabs`), ou rien (`close_tab`) | Le bloc `browser_state` seul                                                                     |
| `read_page`, `get_page_text`, `find`, `read_console`, `read_network`, `javascript_exec` | Une chaîne                                                                                                        | La chaîne, ou `(empty)` pour une chaîne vide                                                     |
| Tous les autres membres                                                                 | Rien, ou une ligne de texte                                                                                       | Une courte confirmation, comme `Clicked.`, puis la ligne renvoyée dans un bloc de texte distinct |

### Signaler l'état du navigateur

Le SDK appelle `_browser_state` (typescript: `browserState`) après chaque appel qui renvoie un résultat, y compris les appels refusés et échoués. Renvoyez chaque onglet ouvert, ainsi que ce qui a changé depuis le dernier rapport :

* Les onglets ouverts et les événements de téléchargement.
* Un `BetaNavigationRefused` pour chaque navigation bloquée par votre [hook de requête](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#intercept-requests-in-the-driver).
* Un `BetaDialogDismissed` pour chaque boîte de dialogue native fermée par votre pilote.

Placez tous ces éléments dans `state_changes`. En Python, `BetaNavigationRefused()` et `BetaDialogDismissed(kind=..., message=...)` proviennent de `anthropic.tools.browser`. En TypeScript, ce sont `{ type: "navigation_refused" }` et `{ type: "dialog_dismissed", kind, message }`.

Les deux derniers ne sont pas des changements d'état de l'API. Le SDK les signale à Claude sous forme de texte en dehors du bloc `browser_state` : une ligne pour toutes les navigations refusées, qui n'inclut pas d'URL, et une ligne pour chacune des trois premières boîtes de dialogue fermées, puis le nombre des autres.

Lorsqu'un onglet est ouvert, exactement un doit être actif. Chaque membre qui accepte un `tab_id` doit agir sur l'onglet qu'il désigne. Les limites de l'API concernant le rapport sont listées dans [Suivre les onglets avec `browser_state`](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-tool#track-tabs-and-page-state).

Claude lit l'URL de chaque onglet, l'URL de chaque téléchargement et l'URL que renvoie votre `navigate`, telles que votre pilote les a écrites. Le SDK n'analyse pas ces URL. Il transforme les sauts de ligne et autres caractères de contrôle en espaces, supprime les espaces aux extrémités et coupe chaque URL à 4 096 caractères. Ainsi, le contenu d'une URL `data:`, le chemin d'une URL `file:`, et un nom d'utilisateur ou un mot de passe dans une URL parviennent tous à Claude.

### Gérer les erreurs

| Levée par un membre ou le SDK | Claude lit                                                                                        | L'exécution |
| ----------------------------- | ------------------------------------------------------------------------------------------------- | ----------- |
| `ToolError`                   | Son message, sous forme de résultat d'erreur                                                      | Continue    |
| `ToolsetUsageError`           | Rien                                                                                              | S'arrête    |
| Toute autre exception         | `ClassName: message` en Python ou `Error: message` en TypeScript, sous forme de résultat d'erreur | Continue    |

Le SDK lève `ToolsetUsageError` pour une erreur de configuration, une mauvaise utilisation du SDK pendant un appel, ou un appel après `close`. Il en lève également une lorsque `_browser_state` (typescript: `browserState`) lève une exception quelconque, même une `ToolError`. Le tool runner et `tool_result` (typescript: `toolResult`) ne l'interceptent pas, elle parvient donc à leur appelant.

Le SDK ne masque pas les chemins locaux dans le texte d'erreur d'un membre, dans la ligne que renvoie une action comme `left_click`, dans l'erreur d'un téléchargement échoué, ni dans le message d'une boîte de dialogue fermée.

Une `ToolError` provenant de votre politique d'URL, de votre politique de fichiers ou de votre callable `confirm` parvient à Claude telle qu'elle est écrite, donc n'incluez pas de chemins locaux dans son texte. Interceptez les exceptions dans vos membres, et levez `ToolError` (lancez-la avec throw en TypeScript) avec votre propre texte.

### Démarrer les appels pendant le streaming de la réponse

Par défaut, le tool runner exécute les appels d'un tour après la fin de la réponse de Claude. Avec le démarrage anticipé, il peut démarrer un appel au navigateur ou à l'ordinateur pendant que la réponse est encore en streaming.

Pour l'activer, passez `stream=True` (typescript: `stream: true`) et `run_tools_eagerly=True` (typescript: `runToolsEagerly: true`) au tool runner, comme le fait l'[exemple avancé](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#quick-start). Avec `stream`, chaque passage dans votre boucle sur le runner vous donne un stream au lieu d'un message.

Le démarrage anticipé modifie le moment où un appel peut démarrer, et non la façon dont l'ensemble d'outils l'exécute :

* **Les vérifications s'exécutent toujours en premier :** Le SDK appelle votre politique d'URL, votre politique de fichiers et `confirm` avant l'exécution d'un appel. Avec le démarrage anticipé, il peut appeler `confirm` pendant que la réponse est encore en streaming.
* **Les appels s'exécutent toujours un par un :** Un ensemble d'outils exécute ses appels dans l'ordre où Claude les a écrits. Un appel échoué arrête toujours les appels suivants de cet ensemble d'outils dans le tour.
* **Un appel qui a démarré ne peut pas être annulé :** Si la réponse est ensuite interrompue, par exemple à `max_tokens`, ou si votre boucle s'arrête prématurément, l'action a quand même lieu, et Claude ne lit jamais son résultat.

### Exécuter sans le tool runner

Passez l'instance dans `tools` (`browser.toJSON()` en TypeScript), et répondez à chaque appel de membre avec `tool_result` (typescript: `toolResult`). L'outil d'utilisation du navigateur exige que vous vous arrêtiez au premier appel échoué ([Actions par lot](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-tool#batch-actions)).

Cette boucle utilise `MyBrowser` et la politique d'URL de l'[exemple avancé](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#quick-start). Après un appel échoué, elle répond aux appels suivants du tour sans les exécuter :

<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:
                  # Après un appel en échec, le reste du tour reçoit une réponse sans être exécuté.
                  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) {
          // Après un appel en échec, le reste du tour reçoit une réponse sans être exécuté.
          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>

Chaque réponse à un appel ignoré comporte `is_error`, le `toolset_name` de l'appel et le texte exact qu'exige la section [Actions par lot](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-tool#batch-actions). Le tool runner envoie la même réponse.

## Exemple avancé : écrire votre propre pilote

Cet exemple réunit les principaux éléments d'[Implémenter un pilote](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver) dans un seul pilote, `MyBrowser`, et l'exécute avec le tool runner et le [démarrage anticipé](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#start-calls-while-the-response-streams). `MyBrowser` implémente `navigate`, `screenshot` et `left_click`, ainsi que `_browser_state` (typescript: `browserState`), le rapport d'état dont chaque pilote a besoin.

`backend` représente votre propre wrapper autour d'une bibliothèque d'automatisation de navigateur, comme Playwright. La politique d'URL est un exemple. [Écrire une politique d'URL](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy) l'explique.

<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 vaut "back", "forward", "reload", ou une URL que votre politique d'URL
          # a autorisée, telle que Claude l'a écrite. Comme is_allowed, backend.goto ajoute https://
          # à une URL qui ne commence pas par un schéma.
          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:
          # Rien à renvoyer : Claude lit "Clicked."
          self.backend.click(input.target, input.tab_id)

      def close(self) -> None:
          super().close()  # first, so no call is still using the browser when it closes
          if not self.backend.closed:
              self.backend.close()


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


  def is_allowed(url: str) -> bool:
      # Un exemple, pas une politique de production. Voir "Write a URL policy".
      if url.lower() == "about:blank":
          return True
      with_scheme = url if SCHEME_PREFIX.match(url) else f"https://{url}"
      # Un navigateur lit "\" comme "/" dans une URL web.
      try:
          parts = urlsplit(with_scheme.replace("\\", "/"))
      except ValueError:
          return False
      host = parts.hostname or ""
      listed = any(host == name or host.endswith(f".{name}") for name in ALLOWED_HOSTS)
      return parts.scheme in ("http", "https") and listed


  def url_policy(context: BetaURLContext, url: str) -> None:
      if not is_allowed(url):
          raise ToolError(f"blocked: {url} is not on an allowed host")


  client = Anthropic()
  with MyBrowser(backend, url_policy=url_policy) as browser:
      runner = client.beta.messages.tool_runner(
          model="claude-opus-5-5",
          max_tokens=1024,
          tools=[browser],
          messages=[
              {
                  "role": "user",
                  "content": "Open example.com and tell me the page heading.",
              }
          ],
          stream=True,
          run_tools_eagerly=True,  # so a call can start before the response ends
      )
      for stream in runner:
          print(stream.get_final_message())
  ```

  ```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,
        // Obligatoire : chaque onglet ouvert, et ce qui a changé depuis le dernier rapport.
        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 vaut "back", "forward", "reload", ou une URL que votre politique d'URL
      // a autorisée, telle que Claude l'a écrite. Comme isAllowed, backend.goto ajoute https:// à
      // une URL qui ne commence pas par un schéma.
      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> {
      // Rien à renvoyer : Claude lit "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;

  // Un exemple, pas une politique de production. Voir "Écrire une politique d'URL".
  function isAllowed(url: string): boolean {
    if (url.toLowerCase() === "about:blank") {
      return true;
    }
    const withScheme = SCHEME_PREFIX.test(url) ? url : `https://${url}`;
    // Un navigateur interprète "\" comme "/" dans une URL web.
    const parsed = URL.parse(withScheme.replaceAll("\\", "/"));
    const host = parsed?.hostname ?? "";
    const listed = ALLOWED_HOSTS.some((name) => host === name || host.endsWith(`.${name}`));
    return (parsed?.protocol === "http:" || parsed?.protocol === "https:") && listed;
  }

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

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

## Exécuter l'ensemble d'outils en toute sécurité

La prochaine action de Claude dépend des pages qu'il lit. Une page, ou du texte injecté dans une page, peut tenter d'atteindre des services internes ou d'extraire des fichiers de l'hôte. Elle peut aussi tenter de déclencher des actions aux effets réels. Avant d'exécuter un pilote sur autre chose qu'un navigateur jetable, suivez ces six étapes :

1. [Écrire une politique d'URL](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy) qui refuse chaque URL de `navigate` dont la tâche n'a pas besoin.
2. [Intercepter les requêtes dans le pilote](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#intercept-requests-in-the-driver), et vérifier chaque requête avec les mêmes règles.
3. [Appliquer une « egress policy » (politique de sortie) au conteneur](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container), afin que le réseau bloque ce que le pilote ne peut pas voir.
4. [Confiner les téléversements et les téléchargements](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#confine-uploads-and-downloads), ou laisser les téléversements désactivés.
5. [Contrôler les membres lourds de conséquences](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#gate-consequential-members) avec `confirm`.
6. [Isoler l'hôte du navigateur](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#isolate-the-browser-host) dans un conteneur ou une VM dédié(e) pour chaque session.

Le SDK applique les étapes 1, 4 et 5 via les politiques et le callable que vous transmettez. Les étapes 2, 3 et 6 relèvent de votre pilote et de votre déploiement. Les précautions décrites dans [Considérations de sécurité](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-tool#security-considerations) s'appliquent également.

<Note>
  Les classificateurs de « prompt injection » (injection de prompt) d'Anthropic, décrits sous [Considérations de sécurité](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-tool#security-considerations), ne s'exécutent actuellement pas sur les requêtes de l'ensemble d'outils de navigateur envoyées via Amazon Bedrock.
</Note>

### Écrire une politique d'URL

<Warning>
  Sans politique d'URL, le SDK ne vérifie aucune URL, et l'API ne filtre pas les URL que Claude ouvre. Même avec une politique, un lien sur lequel Claude clique ou une redirection peut mener le navigateur vers le loopback (`localhost`, `127.0.0.1`), vers des adresses link-local comme l'adresse de métadonnées cloud `169.254.169.254`, et vers des plages de réseau privé. Des [règles de sortie sur le conteneur](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container) peuvent bloquer les adresses link-local et privées. Les règles appliquées en dehors du conteneur ne peuvent pas bloquer le loopback.

  Le SDK ne vérifie pas le schéma d'une URL, votre pilote doit donc refuser chaque schéma qu'il n'a pas l'intention d'ouvrir, comme `javascript:`, `view-source:`, `data:` et `file:`. La plupart des pilotes n'ont besoin que de `http` et `https`. L'exemple de politique ci-dessous n'autorise que les URL `http` et `https`, ainsi que `about:blank`.

  Une URL `file:` permet à Claude de lire des fichiers sur l'hôte du navigateur. Une URL `javascript:` exécute un script dans la page courante, même lorsque `javascript_exec` est désactivé. Les règles de sortie n'arrêtent ni l'une ni l'autre.
</Warning>

Une politique d'URL est une fonction que vous transmettez en tant que `url_policy` (typescript: `urlPolicy`). Vous écrivez la politique, et il vous incombe de la rendre prête pour la production. Avant l'exécution de votre `navigate`, le SDK appelle la politique avec le contexte de l'appel et l'URL telle que Claude l'a écrite.

Ne renvoyez rien pour autoriser l'URL. Pour la refuser, levez `ToolError` (lancez-la avec throw en TypeScript). Claude lit le message de l'erreur, et `navigate` ne s'exécute pas. La politique ne vérifie que l'URL de chaque appel `navigate`. Ce n'est pas un contrôle réseau.

Voici la politique de l'[exemple avancé](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#quick-start). Elle autorise `about:blank`, ainsi que deux sites et leurs sous-domaines en `http` et `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:
      # Un exemple, pas une politique de production.
      if url.lower() == "about:blank":
          return True
      with_scheme = url if SCHEME_PREFIX.match(url) else f"https://{url}"
      # Un navigateur interprète « \ » comme « / » dans une URL web.
      try:
          parts = urlsplit(with_scheme.replace("\\", "/"))
      except ValueError:
          return False
      host = parts.hostname or ""
      listed = any(host == name or host.endswith(f".{name}") for name in ALLOWED_HOSTS)
      return parts.scheme in ("http", "https") and listed


  def url_policy(context: BetaURLContext, url: str) -> None:
      if not is_allowed(url):
          raise ToolError(f"blocked: {url} is not on an allowed host")


  browser = MyBrowser(backend, url_policy=url_policy)
  ```

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

  // Un exemple, pas une politique de production.
  function isAllowed(url: string): boolean {
    if (url.toLowerCase() === "about:blank") {
      return true;
    }
    const withScheme = SCHEME_PREFIX.test(url) ? url : `https://${url}`;
    // Un navigateur interprète « \ » comme « / » dans une URL web.
    const parsed = URL.parse(withScheme.replaceAll("\\", "/"));
    const host = parsed?.hostname ?? "";
    const listed = ALLOWED_HOSTS.some((name) => host === name || host.endsWith(`.${name}`));
    return (parsed?.protocol === "http:" || parsed?.protocol === "https:") && listed;
  }

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

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

Le SDK n'appelle la politique que pour `navigate`, et non pour `"back"`, `"forward"` ou `"reload"`. Claude peut omettre le schéma, comme dans `example.com`, donc avant de lire l'hôte, l'exemple ajoute `https://` à une URL qui ne commence pas par un schéma. Votre `navigate` reçoit l'URL telle que Claude l'a écrite. Ajoutez-y le schéma avec le même test, afin que le navigateur ouvre l'URL que votre politique a vérifiée.

L'exemple n'est pas de qualité production. Une politique de production nécessite bien davantage, dans des domaines comme ceux-ci :

* Les liens sur lesquels Claude clique, les formulaires qu'il soumet, les redirections et les requêtes qu'une page effectue d'elle-même. Seul votre pilote les voit. Consultez [Intercepter les requêtes dans le pilote](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#intercept-requests-in-the-driver).
* Les noms d'hôte et les URL provenant du contenu des pages, qu'un attaquant peut choisir.
* L'analyse de chaque URL de la même manière que le navigateur, et non la recherche du nom d'hôte par un simple découpage de chaîne.
* Les schémas d'URL. Le SDK ne les vérifie pas, refusez donc chaque schéma que votre pilote n'a pas l'intention d'ouvrir.
* Les règles de sortie sur le conteneur, pour ce qu'aucune vérification d'URL ne peut voir. Consultez [Appliquer une politique de sortie au conteneur](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container).

Si vous transmettez `url_policy=None` (typescript: `urlPolicy: null`), le SDK refuse chaque `navigate` vers une URL, de sorte qu'un `None` (typescript: `null`) lu depuis votre configuration ne peut pas laisser des URL non vérifiées par accident. En TypeScript, `undefined` laisse l'option non définie, comme si vous ne la transmettiez pas.

### Intercepter les requêtes dans le pilote

La politique d'URL ne juge que l'URL d'un appel `navigate`. Un lien sur lequel Claude clique, un formulaire qu'il soumet, une redirection, ou une image ou un script sur une page autorisée peut toujours atteindre un hôte que la politique refuserait. Utilisez des [règles de sortie](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container) pour bloquer ces hôtes.

Un pilote doté de l'interception des requêtes peut également vérifier ces requêtes dans son hook de requête, la fonction que votre bibliothèque d'automatisation appelle avant chaque requête. Appelez la fonction qu'utilise votre politique, afin d'écrire les règles une seule fois :

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

      # Enregistré sur un contexte de navigateur Playwright avec context.route("**/*", self._guard).
      def _guard(self, route):
          # is_allowed est la fonction de « Write a URL policy ».
          if not is_allowed(route.request.url):
              return route.abort("blockedbyclient")
          route.continue_()
  ```

  ```typescript TypeScript
  // Dans la classe du pilote, sur un contexte de navigateur Playwright.
  // isAllowed est la fonction de « Écrire une politique d'URL ».
  await context.route("**/*", (route) =>
    isAllowed(route.request().url()) ? route.continue() : route.abort("blockedbyclient")
  );
  ```
</CodeGroup>

Un hook de requête comme celui-ci ne voit pas les handshakes WebSocket, les requêtes des service workers ni les étapes de redirection. Bloquez les service workers. Vérifiez les handshakes WebSocket et les étapes de redirection avec un hook qui les voit. L'exemple de politique refuse les URL `ws://` et `wss://`, donc remplacez `ws://` par `http://` et `wss://` par `https://` avant de transmettre une URL WebSocket à `is_allowed` (typescript: `isAllowed`).

### Appliquer une politique de sortie au conteneur

L'interception ne peut pas voir chaque requête que fait le navigateur, et la politique d'URL ne voit pas vers quoi un nom se résout. Une politique de sortie appliquée par le réseau du conteneur couvre les deux :

* Bloquez les plages d'adresses link-local et privées en IPv4 et IPv6. Cela inclut l'adresse de métadonnées cloud `169.254.169.254`.
* N'autorisez les connexions sortantes que vers les hôtes dont la tâche a besoin. Si vos règles portent sur des adresses IP, résolvez les noms d'hôte que vous autorisez au démarrage du conteneur.
* N'autorisez le DNS que vers le résolveur du conteneur.

Les règles appliquées en dehors du conteneur du navigateur, comme une NetworkPolicy Kubernetes ou un pare-feu cloud, ne voient pas le loopback à l'intérieur de celui-ci. Avec ces seules règles, une page peut atteindre tout ce qui y écoute, y compris un port DevTools, qui affiche la liste des onglets ouverts du navigateur et l'adresse qui contrôle chacun d'eux.

### Confiner les téléversements et les téléchargements

`file_upload` est désactivé par défaut. Sans `file_policy` (typescript: `filePolicy`), le SDK refuse tout téléversement qui fournit un chemin ou un [ID de document](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-tool#upload-files). Pour activer les téléversements, passez une `BetaLocalFilePolicy` (typescript: `BetaNodeFilePolicy`) avec un répertoire de téléversement qui ne contient que les fichiers de la tâche :

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

  # Un MyBrowser qui implémente également file_upload.
  browser = MyBrowser(
      backend,
      configs={"file_upload": {"enabled": True}},
      confirm=make_confirm(),  # required for file_upload; see Gate consequential members
      url_policy=url_policy,
      file_policy=BetaLocalFilePolicy(
          upload_roots=["/task/uploads"],
          download_dir="/task/downloads",
          expose_download_paths=False,
      ),
  )
  ```

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

  // Un MyBrowser qui implémente également file_upload
  const browser = new MyBrowser(backend, {
    configs: { file_upload: { enabled: true } },
    confirm: makeConfirm(), // required for file_upload; see Gate consequential members
    urlPolicy,
    filePolicy: new BetaNodeFilePolicy({
      uploadRoots: ["/task/uploads"],
      downloadDir: "/task/downloads",
      exposeDownloadPaths: false
    })
  });
  ```
</CodeGroup>

Le SDK résout chaque chemin de téléversement, en suivant les liens symboliques, et refuse tout chemin situé en dehors des racines de téléversement. La politique de fichiers lève une erreur de configuration pour un répertoire de téléchargement qui est une racine de téléversement, qui se trouve à l'intérieur de l'une d'elles ou qui en contient une. Cette vérification peut manquer un lien symbolique, donc ne laissez pas un lien symbolique rendre le répertoire de téléchargement identique à une racine de téléversement, ni placer l'un à l'intérieur de l'autre. Le SDK n'inclut le `path` d'un téléchargement dans le rapport d'état que lorsque `expose_download_paths` (typescript: `exposeDownloadPaths`) est vrai et que le fichier se trouve dans le répertoire de téléchargement.

La politique de fichiers fournie vérifie les chemins sur le système de fichiers du processus qui exécute le SDK. Elle ne protège qu'un navigateur qui partage ce système de fichiers. Pour un navigateur distant, suivez plutôt [Navigateurs distants et hébergés](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#remote-and-hosted-browsers).

Configurez les téléchargements de cette façon :

* Créez vous-même le répertoire de téléchargement avec le mode `0700`, et montez-le en `noexec,nosuid,nodev`.
* Gardez le répertoire hors de portée des autres outils que Claude peut appeler, comme un shell ou un outil de fichiers.
* Dans un changement d'état `download_failed`, écrivez `error` sous forme de phrase fixe. Le texte d'une exception peut contenir le chemin ou l'URL.
* Ne lisez pas un fichier téléchargé dans la conversation, et ne l'exécutez pas, tant qu'une personne n'en a pas décidé ainsi.

### Contrôler les membres lourds de conséquences

`javascript_exec` et `file_upload` sont désactivés par défaut. Si vous activez l'un ou l'autre sans callable `confirm`, le constructeur lève une erreur de configuration. Avec un callable `confirm`, le SDK l'appelle avant chaque appel sur le point de s'exécuter. Sans callable, rien n'est demandé. En TypeScript, `confirm: null` ne lève pas l'erreur de configuration, et le SDK refuse chaque appel à n'importe quel membre.

Renvoyez `True` (typescript: `true`) pour exécuter l'appel, ou `False` (typescript: `false`) pour le refuser. Lorsque votre callable interroge une personne, montrez-lui le membre, l'URL de la page et l'entrée de l'appel. Au préalable, échappez chaque caractère situé en dehors de l'ASCII imprimable dans l'URL et l'entrée, car les deux peuvent contenir du texte provenant de la page.

Cet exemple demande confirmation pour les deux membres soumis à approbation via votre propre fonction `ask_user` (typescript: `askUser`), et approuve le reste :

<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
          # Une approbation ne couvre que cette entrée exacte sur cette page.
          key = (name, page, detail)
          if key not in granted and ask_user(question):
              granted.add(key)
          return key in granted

      return confirm


  # Un MyBrowser qui implémente aussi javascript_exec et file_upload
  # file_upload nécessite aussi la file_policy de « Confine uploads and downloads »
  browser = MyBrowser(
      backend,
      configs={"javascript_exec": {"enabled": True}, "file_upload": {"enabled": True}},
      confirm=make_confirm(),
      url_policy=url_policy,
  )
  ```

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

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

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

  const makeConfirm = () => {
    const granted = new Set<string>();
    return async (ctx: BetaConfirmContext): Promise<boolean> => {
      const name = ctx.member;
      if (!GATED.has(name)) {
        return true;
      }
      const detail = shown(ctx.input);
      const page = ctx.tabURL;
      const where = page ? shown(page) : "a tab with no URL";
      const question = `Allow ${name} on ${where}?\n${detail}`;
      const scheme = page === undefined ? undefined : URL.parse(page)?.protocol;
      if (scheme !== "http:" && scheme !== "https:") {
        return askUser(question); // no web origin: ask every time
      }
      // Une approbation ne couvre que cette entrée exacte sur cette page.
      const key = `${name} ${page} ${detail}`;
      if (!granted.has(key) && (await askUser(question))) {
        granted.add(key);
      }
      return granted.has(key);
    };
  };

  // Un MyBrowser qui implémente aussi javascript_exec et file_upload
  // file_upload nécessite aussi le filePolicy de « Confine uploads and downloads »
  const browser = new MyBrowser(backend, {
    configs: { javascript_exec: { enabled: true }, file_upload: { enabled: true } },
    confirm: makeConfirm(),
    urlPolicy
  });
  ```
</CodeGroup>

Chaque appel à `make_confirm()` (typescript: `makeConfirm()`) renvoie un callable sans aucune approbation. Appelez-le une fois pour chaque ensemble d'outils, et donnez à chaque utilisateur son propre ensemble d'outils.

Une approbation couvre la page telle que le dernier rapport d'état l'a montrée, et la page peut changer avant que l'appel ne s'exécute. Les achats, les messages envoyés et les conditions acceptées passent par des membres ordinaires comme `left_click` et `type`, donc `confirm` ne peut pas les identifier par leur nom. Pour qu'une personne les approuve, interrogez-la aussi sur ces membres.

N'activez pas `javascript_exec` à moins que des [règles de sortie](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container) ne limitent les connexions sortantes du navigateur aux hôtes dont la tâche a besoin. Un script peut envoyer le contenu de la page à n'importe quelle adresse que le navigateur peut atteindre.

Une URL `javascript:` dans un appel `navigate` exécute également un script dans la page courante, même lorsque `javascript_exec` est désactivé. `confirm` voit cet appel comme un `navigate`, donc le callable `confirm` de l'exemple l'approuverait. Votre pilote doit refuser les URL `javascript:`. Dans l'exemple, la [politique d'URL](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy) les refuse avant l'exécution de `confirm`.

### Isoler l'hôte du navigateur

Exécutez le navigateur dans un conteneur ou une VM dédié(e), à privilèges minimaux, pour chaque session :

* Exécutez-le en tant qu'utilisateur non root, avec un système de fichiers racine en lecture seule lorsque le navigateur le permet.
* Ne montez rien depuis l'hôte au-delà des répertoires de téléversement et de téléchargement que vous avez configurés, le cas échéant.
* Gardez les identifiants hors de l'environnement, et partez d'un profil de navigateur vierge.
* Ne partagez aucun système de fichiers avec d'autres outils que Claude peut appeler.

<Warning>
  Le profil de navigateur ne doit être connecté à aucun compte dont vous ne confieriez pas les données ou les actions à Claude. `javascript_exec` s'exécute avec l'autorité propre de la page : ses cookies, son stockage et ses sessions connectées.
</Warning>

Exécutez le code qui appelle l'API en dehors du conteneur du navigateur, car ce code détient votre clé API et la conversation. Le tool runner et `tool_result` (typescript: `toolResult`) exécutent tous deux l'ensemble d'outils dans le processus de ce code, de sorte que le navigateur ne partage pas le système de fichiers de l'ensemble d'outils. Traitez le navigateur comme distant : [Navigateurs distants et hébergés](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#remote-and-hosted-browsers) s'applique.

Traitez tout ce qu'une page renvoie comme non fiable, y compris le texte de la page, les captures d'écran, les entrées de console et de réseau, les titres d'onglets et les noms de téléchargements.

### Navigateurs distants et hébergés

Certains navigateurs ne partagent pas de système de fichiers avec le processus qui exécute le SDK. C'est le cas d'un navigateur dans un autre conteneur, d'un navigateur que vous atteignez via une URL DevTools ou d'un navigateur fourni par un service de navigateur hébergé. Avec ceux-ci, la politique d'URL, l'interception des requêtes et `confirm` s'exécutent toujours dans votre processus.

Avec un navigateur hébergé, le fournisseur contrôle la sortie réseau et l'isolation de l'hôte. Votre propre politique de sortie ne s'y applique pas, donc le hook de requête du pilote est votre seule vérification des requêtes du navigateur. Renseignez-vous sur ce que le réseau du navigateur peut atteindre.

Les vérifications de chemins fournies ne protègent pas un navigateur distant. `BetaLocalFilePolicy` (typescript: `BetaNodeFilePolicy`) vérifie les chemins sur le système de fichiers du processus qui exécute le SDK, alors que le navigateur lit et écrit sur son propre système de fichiers.

Le SDK ne peut pas détecter qu'un navigateur est distant. Ainsi, pour un navigateur distant, laissez `file_upload` désactivé, sauf si votre pilote vérifie les chemins de téléversement là où le navigateur s'exécute, avec son propre `BetaFilePolicy`. Un `BetaFilePolicy` contrôle les chemins et les ID de document de chaque téléversement, et décide si Claude voit le chemin d'un téléchargement.

Faites en sorte qu'un navigateur distant refuse les téléchargements, sauf si son propre hôte dispose de la configuration de téléchargement décrite dans [Confiner les téléversements et les téléchargements](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#confine-uploads-and-downloads).

Gardez la clé API du fournisseur et l'URL de connexion de la session, qui peut contenir une clé, hors des journaux, des résultats d'outils et des textes d'erreur. Si le fournisseur enregistre les sessions, l'enregistrement est une autre copie de tout ce que Claude a vu et tapé, et les conditions de conservation du fournisseur s'y appliquent.

## Ensemble d'outils de l'ordinateur

`BetaAbstractComputerToolset20260801` est la classe pour l'[outil d'utilisation de l'ordinateur](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/computer-use-tool). Un pilote est une sous-classe avec une méthode par outil, comme `screenshot` ou `left_click`. Partez du pilote d'un partenaire dans [Intégrations partenaires](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#partner-integrations). Pour écrire le vôtre, le dépôt claude-quickstarts contient un [exemple VNC minimal](https://github.com/anthropics/claude-quickstarts/tree/main/computer-toolset) en Python et TypeScript. Il contrôle un bureau via VNC, et ce n'est pas du code de production.

La classe accepte `configs`, `confirm` et `tool_configs` (typescript: `toolConfigs`), et vous les transmettez comme pour la classe du navigateur. [Personnaliser un pilote](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#customize-a-driver), [Gérer les erreurs](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#handle-errors), [Démarrer les appels pendant le streaming de la réponse](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#start-calls-while-the-response-streams) et [Exécuter sans le tool runner](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#run-without-the-tool-runner) s'y appliquent également, avec ces différences :

* **Options propres au navigateur :** La classe n'a ni [politique d'URL](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy), ni [politique de fichiers](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#confine-uploads-and-downloads), ni [rapport d'état](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#report-browser-state).
* **`configs` :** Chaque outil que vous implémentez est activé par défaut. Les paramètres par outil sont listés dans [Paramètres de l'outil](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/computer-use-tool#tool-parameters).
* **`confirm` :** Si votre classe implémente `type`, `key` ou `hold_key`, le constructeur lève une erreur de configuration, sauf si vous transmettez un callable `confirm` ou désactivez ces outils avec `configs`. Une classe qui redéfinit `execute` est considérée comme implémentant les trois.
* **Appels ignorés :** Après un appel à l'ordinateur échoué, le tool runner répond aux appels à l'ordinateur suivants du tour avec `Not executed: an earlier computer action in this turn failed.` Les appels au navigateur reçoivent un texte différent. Dans une boucle que vous écrivez, répondez aux appels à l'ordinateur ignorés avec ce texte exact ([Actions groupées](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/computer-use-tool#batch-actions)).
* **`execute` :** En TypeScript, une redéfinition de `execute` type `name` comme `BetaComputerMemberName` et `input` comme `BetaComputerMemberInput`. Les deux proviennent de `@anthropic-ai/sdk/resources/beta`.
* **Texte d'erreur :** Le SDK ne masque pas les chemins locaux dans le texte d'erreur d'un outil de l'ordinateur ni dans le texte que renvoie un outil. Claude lit les deux, coupés à 4 096 caractères. Interceptez les exceptions dans vos outils, et levez `ToolError` (lancez-la avec throw en TypeScript) avec votre propre texte.

### Écrire un pilote de bureau

Ce pilote implémente cinq des 17 outils listés dans [Actions disponibles](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/computer-use-tool#available-actions). Un outil que vous n'implémentez pas est envoyé à l'API comme désactivé, comme l'explique [Implémenter un pilote](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver). Dans l'exemple, `display` représente votre propre wrapper autour de ce qui contrôle le bureau, comme un client VNC ou `xdotool`, et `ask_user` (typescript: `askUser`) représente votre propre fonction qui interroge une personne.

<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:
          # Les coordonnées de Claude sont des positions en pixels dans cette image.
          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 vaut [x, y], ou None pour cliquer au curseur. input.text est une
          # touche de modification à maintenir, comme "shift". Rien à renvoyer : Claude lit "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,
      # requis pour type et key ; voir Exécuter l'ensemble d'outils computer en toute sécurité.
      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> {
      // Les coordonnées de Claude sont des positions en pixels dans cette image.
      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 vaut [x, y], ou null ou undefined pour cliquer à la position du curseur. input.text
      // est une touche de modification à maintenir, comme "shift". Rien à renvoyer : Claude lit "Clicked."
      await this.display.click(input.coordinate, input.text);
    }

    // L'outil type s'écrit type_ en TypeScript.
    protected override async type_(
      ctx: BetaToolsetCallContext,
      input: BetaComputerTypeInput
    ): Promise<void> {
      await this.display.type(input.text);
    }

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

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

  const client = new Anthropic();
  const desktop = new MyDesktop(display, {
    // requis pour type et key ; voir Exécuter l'ensemble d'outils computer en toute sécurité
    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>
  Le `confirm` de cet exemple interroge une personne avant chaque appel exécuté par le pilote. Avant d'exécuter le pilote sur autre chose qu'une machine jetable, lisez [Exécuter l'ensemble d'outils de l'ordinateur en toute sécurité](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#run-the-computer-toolset-safely).
</Warning>

Quatre outils n'ont aucun champ d'entrée : `screenshot`, `cursor_position`, `left_mouse_down` et `left_mouse_up`. Chaque type d'entrée, y compris les leurs, provient de `anthropic.types.beta` (typescript: `@anthropic-ai/sdk/resources/beta`).

En TypeScript, écrivez les outils sous forme de méthodes, et non de champs de fonctions fléchées. [Implémenter un pilote](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver) en donne la raison.

### Mettre à l'échelle les coordonnées et dimensionner les images

Chaque point que Claude envoie est une position en pixels dans les captures d'écran que vous renvoyez : chaque `coordinate` et `start_coordinate`, ainsi que les deux coins d'une `region` de `zoom`. Le SDK les transmet à vos outils sans modification.

Si vos captures d'écran ont une taille différente de celle de l'écran, effectuez la conversion dans les deux sens dans votre pilote : les points de Claude vers les pixels de l'écran, et la position du curseur que vous signalez vers les pixels de la capture d'écran. Conservez la même taille de capture d'écran pendant toute la session, et donnez-lui le rapport d'aspect de l'écran :

<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):
          # Refuser le point. Le ramener dans la plage cliquerait là où Claude n'a pas choisi.
          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) {
      // Refuser le point. Le ramener dans la plage cliquerait là où Claude n'a pas choisi.
      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>

Appelez `to_display` (typescript: `toDisplay`) sur chaque `coordinate` et `start_coordinate` avant d'agir en conséquence, et `to_screenshot` (typescript: `toScreenshot`) sur la position que `cursor_position` signale. Un clic ou un défilement sans `coordinate` agit à l'emplacement du curseur, il n'y a donc rien à convertir. Mettez à l'échelle une `region` de `zoom` selon le même facteur. Une image de `zoom` ne change rien à cela : les points suivants que Claude envoie restent des positions en pixels dans la capture d'écran complète.

Le SDK ne lit ni ne redimensionne jamais une image. L'API rejette une image qui dépasse les limites du modèle, ce qui met fin à l'exécution. Mettez à l'échelle chaque capture d'écran et chaque image de `zoom` dans le pilote. Consultez [Dimensionner les captures d'écran pour respecter les limites d'image](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/computer-use-tool#handle-coordinate-scaling-for-higher-resolutions). Lors d'une longue exécution, une requête peut contenir plus de 20 images, et l'API applique alors à chacune une limite plus stricte. Consultez [Gérer l'historique des captures d'écran](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/computer-use-tool#manage-screenshot-history).

### Vérifications des entrées et résultats

Ne comptez pas sur le SDK pour vérifier l'entrée d'un appel. Faites en sorte que votre pilote vérifie les champs qu'il utilise et refuse un appel qu'il ne peut pas exécuter : levez `ToolError` (lancez-la avec throw en TypeScript). Claude lit le message et peut envoyer un appel corrigé.

Aucun des deux SDK ne limite une `duration`, un nombre de `repeat` ou un `scroll_amount`. L'étape 4 de [Exécuter l'ensemble d'outils de l'ordinateur en toute sécurité](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#run-the-computer-toolset-safely) les traite.

Ce qu'un outil renvoie détermine ce que Claude lit :

| Outil                  | Renvoie                            | Claude lit                                                                                           |
| ---------------------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `screenshot`, `zoom`   | `BetaScreenshotResult`             | Un bloc image                                                                                        |
| `cursor_position`      | `BetaComputerCursorPositionResult` | `X={x},Y={y}`                                                                                        |
| Tous les autres outils | Rien, ou une ligne de texte        | Une courte confirmation, telle que `Clicked.`, puis la ligne renvoyée dans un bloc de texte distinct |

### Exécuter l'ensemble d'outils de l'ordinateur en toute sécurité

Ce qui s'affiche à l'écran oriente la prochaine action de Claude. Une page ou une notification peut tenter de faire taper Claude dans la mauvaise fenêtre, ou de déclencher une action aux effets réels.

<Warning>
  Un pilote qui implémente `type`, `key` ou `hold_key` nécessite un callable `confirm`. Sans celui-ci, le constructeur lève une erreur de configuration, sauf si `configs` désactive ces outils. Le SDK lui-même ne demande rien à personne : il exécute chaque appel que votre `confirm` approuve, et sans `confirm`, il exécute chaque appel, y compris chaque clic. Chaque outil de l'ordinateur que vous implémentez est activé par défaut.
</Warning>

Avant d'exécuter un pilote sur autre chose qu'une machine jetable, suivez ces cinq étapes :

1. Isolez le bureau. Exécutez-le dans un conteneur ou une VM dédié(e) à privilèges minimaux pour chaque session, sans identifiants ni montages de l'hôte. N'installez que les applications dont la tâche a besoin. Exécutez le code qui appelle l'API en dehors de celui-ci.
2. Soumettez les actions aux effets réels à `confirm`. Un achat ou un message envoyé est un `left_click`, un `type` ou un `key` ordinaire, et `confirm` reçoit l'outil et son entrée, pas l'écran. Choisissez donc les outils qui nécessitent une approbation en fonction des applications que Claude peut atteindre sur le bureau.
3. Gardez les terminaux, les boîtes de dialogue d'exécution et les lanceurs hors du bureau, sauf si la tâche en a besoin. Lorsque l'un d'eux a le focus clavier, `type`, `key` et `hold_key` exécutent tout ce que Claude tape, et un clic peut lancer un programme. Si la tâche en a besoin, demandez à une personne avant toute saisie au clavier et tout clic, et isolez le bureau afin qu'une commande ne puisse pas aller plus loin que ce dont la tâche a besoin.
4. Refusez les entrées que votre bureau ne devrait pas accepter, comme un point en dehors de la capture d'écran, un `wait` de plusieurs minutes, ou un `repeat` ou `scroll_amount` très élevé. Levez `ToolError` (lancez-la avec throw en TypeScript) au lieu de ramener la valeur dans la plage autorisée.
5. Considérez tout ce qui s'affiche à l'écran comme non fiable, y compris les titres de fenêtres et le texte du presse-papiers. Ne l'exécutez pas et ne le transmettez pas sans vérification.

Le SDK applique l'étape 2 par le biais du callable que vous transmettez. Les autres étapes relèvent de votre pilote et de votre déploiement. Les précautions décrites dans [Considérations de sécurité](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/computer-use-tool#security-considerations) s'appliquent également.

Le `confirm` suivant demande à une personne avant toute saisie au clavier, via votre propre fonction `ask_user` (typescript: `askUser`), et approuve tous les autres appels. `context.member` (typescript: `ctx.member`) est le nom de l'outil. Pour qu'une personne approuve également les clics, définissez `ASK_FIRST` sur `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
      # la fonction présentée est celle de « Gate consequential members ».
      detail = shown(context.input.to_dict())
      return ask_user(f'Allow the "{context.member}" tool with this input?\n{detail}')


  desktop = MyDesktop(display, confirm=confirm)
  ```

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

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

  async function confirm(ctx: BetaComputerConfirmContext): Promise<boolean> {
    if (!ASK_FIRST.has(ctx.member)) {
      return true;
    }
    // la fonction présentée est celle de « Gate consequential members ».
    return askUser(`Allow the "${ctx.member}" tool with this input?\n${shown(ctx.input)}`);
  }

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

Un appel refusé reçoit un résultat d'erreur, et l'exécution se poursuit. Pour un appel `type` refusé, Claude lit `The user did not grant permission to run 'type'. Do not retry it unless the user asks you to.`

Ce `confirm` approuve tous les outils qui ne figurent pas dans `ASK_FIRST`, y compris tout outil que vous ajouterez plus tard. Pour échouer de manière fermée, n'approuvez que les outils sans effet sur le bureau (`screenshot`, `zoom`, `cursor_position` et `wait`) et demandez pour tous les autres outils.

### Utiliser les deux ensembles d'outils dans une même requête

Transmettez les deux instances dans `tools`. Chaque appel porte son `toolset_name`, `browser` ou `computer`, et le tool runner envoie l'appel à l'instance correspondante. Après un appel en échec, le tool runner ignore uniquement les appels ultérieurs du tour vers le même ensemble d'outils, de sorte qu'un appel à l'ordinateur en échec n'entraîne pas l'omission des appels au navigateur du tour.

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  # MyBrowser, backend et url_policy proviennent de l'exemple avancé.
  # confirm est la fonction de « Exécuter le jeu d'outils computer en toute sécurité ».
  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 et urlPolicy proviennent de l'exemple avancé.
  // confirm est la fonction issue de « Run the computer toolset safely ».
  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>

Dans une boucle que vous écrivez, répondez à chaque appel avec `tool_result` (typescript: `toolResult`) sur l'instance dont le `toolset_name` (typescript: `toolsetName`) correspond au `toolset_name` de l'appel. À chaque tour, suivez séparément les appels en échec pour chaque ensemble d'outils, et répondez à un appel ignoré avec le texte propre à cet ensemble d'outils. [Exécuter sans le tool runner](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#run-without-the-tool-runner) contient le texte du navigateur, et la puce Appels ignorés en haut de cette section contient celui de l'ordinateur.

## Référence

Les options du constructeur ont la même signification dans les deux SDK :

| Python                      | TypeScript     | Définit                                                                        |
| --------------------------- | -------------- | ------------------------------------------------------------------------------ |
| `configs`                   | `configs`      | Les membres activés                                                            |
| `confirm`                   | `confirm`      | Le callable qui approuve ou refuse chaque appel                                |
| `url_policy`                | `urlPolicy`    | Votre politique d'URL, que le SDK appelle avant chaque `navigate` vers une URL |
| `file_policy`               | `filePolicy`   | Les racines de téléversement et l'exposition du chemin de téléchargement       |
| `tool_configs`              | `toolConfigs`  | Les champs de l'entrée `tools`, tels que `cache_control`                       |
| La méthode `_browser_state` | `browserState` | Le rapport d'état                                                              |

La classe du navigateur accepte toutes les options du tableau. La classe de l'ordinateur accepte `configs`, `confirm` et `tool_configs` (typescript: `toolConfigs`).

Vous ne pouvez pas modifier une option après la construction. Le [guide de l'ensemble d'outils du navigateur pour Python](https://github.com/anthropics/anthropic-sdk-python/blob/main/browser-toolset.md), le [guide de l'ensemble d'outils du navigateur pour TypeScript](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/browser-toolset.md), le [guide de l'ensemble d'outils de l'ordinateur pour Python](https://github.com/anthropics/anthropic-sdk-python/blob/main/computer-toolset.md) et le [guide de l'ensemble d'outils de l'ordinateur pour TypeScript](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/computer-toolset.md) décrivent chaque option. Les guides Python couvrent également les classes asynchrones, `BetaAsyncAbstractBrowserToolset20260801` et `BetaAsyncAbstractComputerToolset20260801`.

## Limitations

* **Le SDK n'inclut ni navigateur, ni bureau, ni pilote prêt à l'emploi, ni politique d'URL :** C'est à vous de les fournir. Consultez [Intégrations partenaires](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#partner-integrations), [Implémenter un pilote](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver) et [Écrire une politique d'URL](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy).
* **La politique d'URL ne voit que les appels `navigate` :** Elle ne voit pas les requêtes qu'une page effectue, ni les pages qui s'ouvrent d'une autre manière, par exemple à partir d'un lien sur lequel Claude clique ou d'une redirection. Consultez [Intercepter les requêtes dans le pilote](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#intercept-requests-in-the-driver).
* **Une approbation est basée sur le dernier rapport d'état :** La page peut changer après ce rapport. Le SDK ne vérifie pas à nouveau la page avant que l'appel ne s'exécute.
* **Les appels sur un même ensemble d'outils s'exécutent un par un :** Vous ne pouvez pas désactiver ce comportement.
* **Le SDK ne vérifie pas que les identifiants d'onglets sont uniques ni le nombre d'onglets, et ne vérifie pas toujours qu'un seul onglet est actif :** L'API rejette un rapport qui enfreint ces règles.
* **Pour un appel à l'ordinateur, `confirm` reçoit l'outil et son entrée, pas l'écran :** Vous ne pouvez pas en déduire ce que fait un clic à un point donné.
* **Le SDK ne redimensionne pas les images d'un ensemble d'outils de l'ordinateur :** L'API rejette une image qui dépasse les limites du modèle. Consultez [Mettre à l'échelle les coordonnées et dimensionner les images](https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-sdk#scale-coordinates-and-size-images).

## Étapes suivantes

<CardGroup cols={2}>
  <Card title="Outil d'utilisation du navigateur" icon="browser" href="https://platform.claude.com/docs/fr/agents-and-tools/tool-use/browser-use-tool">
    Les outils membres, le bloc `browser_state` et les considérations de sécurité de l'outil.
  </Card>

  <Card title="Outil d'utilisation de l'ordinateur" icon="computer" href="https://platform.claude.com/docs/fr/agents-and-tools/tool-use/computer-use-tool">
    Les 17 outils, les coordonnées et les limites d'image, et les considérations de sécurité de l'outil.
  </Card>

  <Card title="Tool Runner (SDK)" icon="arrows-clockwise" href="https://platform.claude.com/docs/fr/agents-and-tools/tool-use/tool-runner">
    Comment le SDK exécute la boucle, et comment modifier les messages qu'il envoie.
  </Card>

  <Card title="Atténuer les jailbreaks et les injections de prompt" icon="shield" href="https://platform.claude.com/docs/fr/test-and-evaluate/strengthen-guardrails/mitigate-jailbreaks">
    Des garde-fous pour toute application qui lit du contenu non fiable.
  </Card>
</CardGroup>
