Claude Platform Docs
MessagesOutils

Utilisation du navigateur avec l'ensemble d'outils du SDK

Exécutez l'outil d'utilisation du navigateur depuis le SDK Python ou TypeScript. Le SDK exécute la boucle ainsi que les vérifications d'URL, de fichiers et d'approbation que vous configurez, et vous fournissez le navigateur.

Les SDK Python et TypeScript incluent une classe pour l'outil d'utilisation du navigateur. Vous en créez une sous-classe et écrivez une méthode par « member tool » (outil membre), comme navigate ou left_click, en vous appuyant sur votre propre automatisation de navigateur. Le SDK achemine chaque appel, vérifie les URL et les chemins de fichiers, interroge votre « callback » (fonction de rappel) d'approbation et construit chaque tool_result.

Le SDK n'inclut ni navigateur, ni « driver » (pilote) prêt à l'emploi, ni « denylist » (liste de blocage). Des exemples de pilotes pour Playwright et le Chrome DevTools Protocol, en Python et TypeScript, se trouvent dans le dossier browser-toolset du dépôt claude-quickstarts.

Démarrage rapide

Un pilote est votre sous-classe de BetaAbstractBrowserToolset20260801. Celui-ci implémente navigate, screenshot et left_click. Il implémente aussi _browser_state (browserState en TypeScript), le rapport d'état dont chaque pilote a besoin. Dans l'exemple, backend représente votre propre wrapper autour d'une bibliothèque d'automatisation de navigateur, comme Playwright.

from anthropic import Anthropic
from anthropic.tools.browser import (
    BetaAbstractBrowserToolset20260801,
    BetaBrowserNavigateResult,
    BetaBrowserScreenshotResult,
    BrowserState,
    ToolsetCallContext,
)
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: ToolsetCallContext) -> BrowserState:
        return BrowserState(
            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: ToolsetCallContext, input: BetaBrowserNavigateInput
    ) -> BetaBrowserNavigateResult:
        # input.url est une URL ayant passé la politique d'URL, ou "back", "forward",
        # ou "reload". Le SDK ajoute https:// lorsque Claude omet le 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: ToolsetCallContext, input: BetaBrowserScreenshotInput
    ) -> BetaBrowserScreenshotResult:
        data = self.backend.png_base64(input.tab_id)
        return BetaBrowserScreenshotResult(data=data, media_type="image/png")

    def left_click(
        self, context: ToolsetCallContext, 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()


client = Anthropic()
with MyBrowser(backend, allowed_domains=["example.com", "iana.org"]) 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.",
            }
        ],
    )
    for message in runner:
        print(message)

Passez l'instance du pilote elle-même comme entrée tools. Un membre que vous n'implémentez pas est envoyé à l'API comme désactivé. Si Claude l'appelle quand même, le SDK renvoie une erreur et l'exécution continue. Redéfinir execute modifie la liste des membres envoyés comme désactivés (Ajouter des hooks avant et après). 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é.

Personnaliser un pilote

Activer ou désactiver des membres

configs accepte les paramètres par membre décrits sous Configurer l'ensemble d'outils. Ne listez que les membres que vous modifiez :

# Un MyBrowser qui implémente également read_console.
browser = MyBrowser(
    backend, configs={"read_console": {"enabled": True}, "navigate": {"enabled": False}}
)

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 vérification d'URL (Définir une politique d'URL) et confirm (Contrôler les membres lourds de conséquences), 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 il peut modifier le résultat. Levez ToolError (lancez-la avec throw en TypeScript) pour refuser l'appel.

Redéfinir execute modifie les membres proposés à Claude. Le SDK considère chaque membre comme implémenté, de sorte que Claude se voit proposer chaque membre activé par défaut. Le MyBrowser du démarrage rapide 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.

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

Implémenter un pilote

Redéfinissez les membres que votre navigateur prend en charge. Chaque membre reçoit le contexte d'appel et l'entrée du membre sous forme d'objet typé, comme BetaBrowserNavigateInput. Les types d'entrée proviennent de anthropic.types.beta (@anthropic-ai/sdk/resources/beta en TypeScript). Outils membres 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.

MembreRenvoieClaude lit
screenshot, zoomBetaBrowserScreenshotResultUn bloc image
navigateBetaBrowserNavigateResultNavigated to {url} — {title} (HTTP {status})
new_tab, switch_tab, list_tabs, close_tabUne 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_execUne chaîneLa chaîne
Tous les autres membresRien, ou une ligne de texteUne 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 (l'option browserState en TypeScript) après chaque appel, 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 NavigationRefused pour chaque navigation bloquée par votre hook de requête.
  • Un DialogDismissed pour chaque boîte de dialogue native que votre pilote a fermée.

Placez tous ces éléments dans state_changes. En Python, NavigationRefused(url=...) et DialogDismissed(kind=..., message=...) proviennent de anthropic.tools.browser. En TypeScript, ce sont { type: "navigation_refused", url } 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 ne nomme aucune URL, et une ligne pour chacune des trois premières boîtes de dialogue fermées, puis le nombre de toutes les autres.

Lorsqu'au moins 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. Le SDK utilise le rapport pour déterminer de quelle page provient un résultat. Les limites de l'API concernant le rapport sont listées sous Suivre les onglets avec browser_state.

Gérer les erreurs

Levée par un membre ou le SDKClaude litL'exécution
ToolErrorSon message, sous forme de résultat d'erreurContinue
Toute autre exceptionSon texte, sous forme de résultat d'erreurContinue
ToolsetUsageError, pour une erreur de configuration, une mauvaise utilisation du SDK pendant un appel, ou un appel après closeRienS'arrête

Avant que Claude ne lise le texte d'erreur d'un membre, la ligne que renvoie une action comme left_click, l'erreur d'un téléchargement échoué ou le message d'une boîte de dialogue fermée, le SDK remplace chaque URL que la politique refuse par (blocked). Il remplace chaque chemin local que la politique de fichiers n'expose pas par (path hidden). La vérification peut manquer certaines URL et certains chemins. Une ToolError provenant de votre politique d'URL, de votre politique de fichiers ou de votre fonction confirm parvient à Claude telle qu'écrite, donc n'incluez pas les URL refusées ni les chemins locaux dans son texte. Interceptez les exceptions dans vos membres, et levez ToolError (lancez-la avec throw en TypeScript) avec votre propre texte.

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 (toolResult en TypeScript). L'outil d'utilisation du navigateur exige que vous vous arrêtiez au premier appel qui échoue (Actions par lot). Après un appel qui a échoué, cette boucle répond aux appels suivants du tour sans les exécuter :

from anthropic.types.beta import BetaToolResultBlockParam

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

with MyBrowser(backend, allowed_domains=["example.com"]) as browser:
    messages = [{"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})

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. Le tool runner envoie la même réponse.

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. Définir une politique d'URL : allowed_domains, blocked_domains ou votre propre url_policy.
  2. Intercepter les requêtes dans le pilote et demander un verdict à l'ensemble d'outils.
  3. Appliquer une « egress policy » (politique de sortie) au conteneur, 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, ou laisser les téléversements désactivés.
  5. Contrôler les membres lourds de conséquences avec confirm.
  6. Isoler l'hôte du navigateur dans un conteneur ou une VM dédié(e) pour chaque session.

Le SDK applique les étapes 1, 4 et 5 selon votre configuration. Les étapes 2, 3 et 6 relèvent de votre pilote et de votre déploiement. Les précautions décrites sous Considérations de sécurité s'appliquent également.

Définir une politique d'URL

Définissez allowed_domains (allowedDomains en TypeScript) sur les sites dont la tâche a besoin. Une entrée dans l'une ou l'autre liste est un domaine (qui couvre aussi ses sous-domaines), une adresse IP ou un réseau CIDR. Lorsque allowed_domains est défini, l'ensemble d'outils refuse tous les autres hôtes.

browser = MyBrowser(backend, allowed_domains=["example.com", "iana.org"])

Si la tâche nécessite le web ouvert, définissez plutôt blocked_domains (blockedDomains). Les listes comparent les noms d'hôte sans les résoudre. Une entrée de liste de blocage 127.0.0.0/8 ne bloque pas localhost, donc nommez les hôtes ainsi que les réseaux. Lorsque vous définissez les deux listes, blocked_domains l'emporte.

Une liste de blocage ne peut pas intercepter un nom public qui se résout en une adresse privée. Ainsi, avec blocked_domains, c'est la politique de sortie du conteneur qui empêche un tel nom d'atteindre une adresse privée.

# Listez les hôtes et réseaux que le navigateur peut atteindre mais que Claude ne doit pas atteindre :
# plages loopback, link-local et privées en IPv4 et IPv6, 0.0.0.0/8, les
# adresses et noms de métadonnées de votre cloud (comme metadata.google.internal),
# localhost et vos noms d'hôte internes.
browser = MyBrowser(backend, blocked_domains=internal_networks)

Une url_policy (urlPolicy) remplace les deux listes, et la passer avec l'une ou l'autre liste est une erreur de configuration. Votre politique ne renvoie rien pour autoriser une URL, et lève (throw) ToolError pour la refuser. Pour conserver les règles par défaut, construisez la politique par défaut avec default_url_policy (defaultURLPolicy), et appelez-la en premier depuis la vôtre :

from urllib.parse import urlsplit

from anthropic.tools.browser import ToolError, URLContext, default_url_policy

listed = default_url_policy(allowed_domains=["example.com"])


def url_policy(context: URLContext, url: str) -> None:
    listed(context, url)  # the default rules first
    if context.phase == "request" and urlsplit(url).scheme != "https":
        raise ToolError("Only https navigation is allowed.")


browser = MyBrowser(backend, url_policy=url_policy)

La politique s'exécute sur chaque URL de navigate avant votre code, sur l'URL qu'un résultat signale, et sur chaque URL d'onglet et de téléchargement dans un rapport d'état. Dans les résultats et les rapports d'état, elle ignore les adresses qui ne désignent aucun hôte distant : un onglet vide, about:blank, la page chrome-error: du navigateur et les documents data:.

Quelle que soit la politique, l'ensemble d'outils refuse un navigate vers un schéma autre que http ou https, à l'exception de about:blank. url_policy="allow_all" (urlPolicy: "allow_all") désactive la politique, mais pas cette règle de schéma.

Passer url_policy=None (urlPolicy: null) fait lever une erreur de configuration par le constructeur, de sorte qu'un None (null) lu depuis votre configuration ne peut pas désactiver les vérifications. En TypeScript, undefined laisse l'option non définie, comme si vous ne la passiez pas.

Lorsqu'une page aboutit sur une adresse refusée, Claude lit que son contenu a été retenu, et l'onglet est listé comme (blocked). Tant que l'onglet n'est pas de nouveau sur une adresse autorisée, l'ensemble d'outils refuse les appels sur celui-ci. Les exceptions sont navigate (mais pas "reload"), new_tab, list_tabs, switch_tab et close_tab.

Intercepter les requêtes dans le pilote

La politique d'URL ne juge que les adresses que voit l'ensemble d'outils : navigations, résultats et rapports d'état. Elle ne voit pas les sous-ressources, les appels fetch(), les WebSockets, ni l'adresse vers laquelle un nom d'hôte se résout.

L'ensemble d'outils ne détecte une nouvelle adresse qu'à la fin d'un appel, dans le résultat ou le rapport d'état de l'appel. Un appel peut donc encore agir sur une page refusée avant ce moment, et le SDK peut tout au plus retenir le résultat de cet appel. À moins que votre pilote ne juge chaque requête, y compris les sauts de redirection, une page qui redirige vers une adresse refusée se charge quand même.

Dans le hook de requête de votre pilote (la fonction que votre bibliothèque d'automatisation appelle avant chaque requête), appelez check_url (checkURL). Il applique la propre politique de l'ensemble d'outils, de sorte que vous n'écrivez pas les règles deux fois :

from anthropic.tools.browser import URLContext


class MyBrowser(BetaAbstractBrowserToolset20260801):
    ...

    # Enregistré sur un contexte de navigateur Playwright avec context.route("**/*", self._guard).
    def _guard(self, route):
        url_context = URLContext(
            member="navigate", phase="request", tab_id=self.backend.active
        )
        if not self.check_url(url_context, route.request.url).allowed:
            return route.abort("blockedbyclient")
        route.continue_()

check_url applique la même règle de schéma et la même politique que navigate. Un hook de requête comme celui-ci ne voit pas les handshakes WebSocket, les requêtes des service workers ni les sauts de redirection. Bloquez les service workers. Jugez les handshakes WebSocket et les sauts de redirection avec un hook qui les voit.

Dans la phase de requête, check_url n'autorise que les URL http, https et about:blank. Ainsi, pour un WebSocket, remplacez ws:// par http:// et wss:// par https:// avant de l'appeler.

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 loopback, link-local et privées en IPv4 et IPv6, ainsi que 0.0.0.0/8. 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 correspondent à 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.
  • Si votre pilote atteint le navigateur via un port DevTools local, n'autorisez le loopback que sur ce port. Une règle pour tout le loopback ouvrirait chaque service local à la page.

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

file_upload est désactivé par défaut. Sans file_policy, le SDK refuse chaque téléversement qui nomme un chemin ou un ID de document. Pour activer les téléversements, passez une LocalFilePolicy (NodeFilePolicy en TypeScript) avec un répertoire de téléversement qui ne contient que les fichiers de la tâche :

from anthropic.tools.browser import LocalFilePolicy

# 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
    file_policy=LocalFilePolicy(
        upload_roots=["/task/uploads"],
        download_dir="/task/downloads",
        expose_download_paths=False,
    ),
)

Le SDK résout chaque chemin de téléversement, en suivant les liens symboliques, et refuse tout chemin en dehors des racines de téléversement. La politique de fichiers refuse un répertoire de téléchargement situé à l'intérieur d'une racine de téléversement. Le chemin d'un téléchargement ne parvient à Claude que lorsque expose_download_paths (exposeDownloadPaths) est vrai et que le fichier se trouve dans le répertoire de téléchargement.

Les vérifications de chemin fournies résolvent les chemins sur le système de fichiers du processus qui exécute le SDK. Elles ne protègent qu'un navigateur qui partage ce système de fichiers. Pour un navigateur distant, suivez plutôt Navigateurs distants et hébergés.

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 fonction confirm, le constructeur lève une erreur de configuration. Avec une fonction confirm, le SDK l'appelle avant chaque appel sur le point de s'exécuter. Sans elle, rien n'est demandé.

Renvoyez True pour exécuter l'appel, ou False pour le refuser (true et false en TypeScript). Lorsque votre fonction 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 de l'entrée situé en dehors de l'ASCII imprimable, car l'entrée peut contenir du texte provenant de la page.

Cet exemple demande une confirmation pour les deux membres contrôlés via votre propre fonction ask_user (askUser en TypeScript), et approuve les autres :

import json
from collections.abc import Callable

from anthropic.tools.browser import ConfirmContext

GATED = {"javascript_exec", "file_upload"}


def shown(context: ConfirmContext) -> str:
    """The call's input as JSON, with every character outside printable
    ASCII escaped."""
    return json.dumps(context.input.to_dict(), ensure_ascii=True, indent=2)


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

    def confirm(context: ConfirmContext) -> bool:
        name = context.member
        if name not in GATED:
            return True
        detail = shown(context)
        page = context.tab_url
        origin = context.origin
        if page is None or origin is None or origin.startswith("chrome-error:"):
            # Aucune origine, ou une page d'erreur : demander à chaque fois.
            return ask_user(
                f"Allow {name} on {page or 'a page with no origin'}?\n{detail}"
            )
        # Une approbation ne couvre que cette entrée exacte sur cette page.
        key = (name, page, detail)
        if key not in granted and ask_user(f"Allow {name} on {page}?\n{detail}"):
            granted.add(key)
        return key in granted

    return confirm


# Un MyBrowser qui implémente aussi javascript_exec et file_upload
browser = MyBrowser(
    backend,
    configs={"javascript_exec": {"enabled": True}, "file_upload": {"enabled": True}},
    confirm=make_confirm(),
)

Chaque appel à make_confirm() (makeConfirm() en TypeScript) renvoie une fonction sans aucune approbation. Appelez-la 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 sur un pilote qui n'intercepte pas les requêtes. Un script qui s'exécute sur une page refusée peut copier son contenu vers un emplacement où une lecture ultérieure le renvoie.

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.

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. L'exécuteur d'outils et tool_result 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 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 chemin fournies ne protègent pas un navigateur distant. LocalFilePolicy (NodeFilePolicy en TypeScript) vérifie les chemins sur le système de fichiers du processus qui exécute le SDK, et 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 une FilePolicy qui lui est propre. Une FilePolicy 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.

Les exemples de pilotes suivent cette règle. Sur un navigateur distant, ils lèvent une erreur de configuration pour une file_policy (filePolicy) ou un file_upload activé, et configurent le navigateur pour refuser les téléchargements.

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.

Référence

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

PythonTypeScriptDéfinit
configsconfigsLes membres activés
confirmconfirmLa fonction qui approuve ou refuse chaque appel
allowed_domains, blocked_domainsallowedDomains, blockedDomainsLes listes de la politique d'URL par défaut
url_policyurlPolicyVotre propre politique d'URL
file_policyfilePolicyLes racines de téléversement et l'exposition des chemins de téléchargement
tool_configstoolConfigsLes champs de l'entrée tools, comme cache_control
La méthode _browser_statebrowserStateLe rapport d'état

Vous ne pouvez pas modifier une option après la construction. Les valeurs par défaut, les erreurs, les champs de contexte et la classe Python asynchrone (BetaAsyncAbstractBrowserToolset20260801) sont documentés dans le SDK Python et le SDK TypeScript.

Limitations

  • La politique d'URL vérifie les navigations, pas chaque requête : Consultez Intercepter les requêtes dans le pilote.
  • 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 ID d'onglets sont uniques, qu'un seul onglet est actif, ni combien d'onglets il y a : L'API rejette un rapport qui enfreint ces règles.

Étapes suivantes

Les outils membres, le bloc browser_state et les considérations de sécurité de l'outil.

Comment le SDK exécute la boucle, et comment modifier les messages qu'il envoie.

Des garde-fous pour toute application qui lit du contenu non fiable.

Was this page helpful?