Claude Platform Docs
MessagesHerramientas

Uso del navegador con el conjunto de herramientas del SDK

Ejecuta la herramienta de uso del navegador desde el SDK de Python o TypeScript. El SDK ejecuta el bucle y las comprobaciones de URL, archivos y aprobación que configures, y tú proporcionas el navegador.

Los SDK de Python y TypeScript incluyen una clase para la herramienta de uso del navegador. Creas una subclase de ella y escribes un método por cada herramienta miembro, como navigate o left_click, sobre tu propia automatización del navegador. El SDK enruta cada llamada, comprueba las URL y las rutas de archivo, consulta tu "callback" (función de devolución de llamada) de aprobación y construye cada tool_result.

El SDK no incluye un navegador, un "driver" (controlador) listo para usar ni una "denylist" (lista de bloqueo). Hay controladores de ejemplo para Playwright y el Chrome DevTools Protocol, en Python y TypeScript, en la carpeta browser-toolset del repositorio claude-quickstarts.

Inicio rápido

Un controlador es tu subclase de BetaAbstractBrowserToolset20260801. Este implementa navigate, screenshot y left_click, además de _browser_state (browserState en TypeScript), el informe de estado que todo controlador necesita. En el ejemplo, backend representa tu propio envoltorio alrededor de una biblioteca de automatización del navegador, como 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 es una URL que pasó la política de URL, o "back", "forward",
        # o "reload". El SDK agrega https:// cuando Claude omite el esquema.
        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:
        # No hay nada que devolver: Claude lee "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)

Pasa la propia instancia del controlador como la entrada de tools. Un miembro que no implementes se envía a la API como deshabilitado. Si Claude lo llama de todos modos, el SDK devuelve un error y la ejecución continúa. Sobrescribir execute cambia qué miembros se envían como deshabilitados (Agregar hooks antes y después). El ejecutor nunca cierra el conjunto de herramientas, por lo que una instancia puede servir a varias ejecuciones. Ciérralo cuando termines.

Personalizar un controlador

Habilitar o deshabilitar miembros

configs acepta la configuración por miembro descrita en Configura el conjunto de herramientas. Enumera solo los miembros que cambies:

# Un MyBrowser que también implementa read_console
browser = MyBrowser(
    backend, configs={"read_console": {"enabled": True}, "navigate": {"enabled": False}}
)

El SDK rechaza una llamada a un miembro deshabilitado antes de que se ejecute tu código. Habilitar un miembro que tu clase no implementa es un error de configuración, a menos que la clase sobrescriba execute.

Agregar hooks antes y después

Sobrescribe execute y llama al execute de la clase padre. El código antes de esa llamada se ejecuta después de la comprobación de URL (Establecer una política de URL) y de confirm (Controlar los miembros con consecuencias), y puede cambiar la entrada. El SDK no vuelve a comprobar la entrada modificada. El código después de la llamada recibe el resultado y puede cambiarlo. Genera ToolError (lánzalo con throw en TypeScript) para rechazar la llamada.

Sobrescribir execute cambia qué miembros se le ofrecen a Claude. El SDK considera implementados todos los miembros, por lo que a Claude se le ofrecen todos los miembros que están activados de forma predeterminada. El MyBrowser del inicio rápido sirve tres miembros, por lo que el siguiente TracedBrowser le ofrece a Claude miembros que no puede servir. Desactiva esos miembros con configs antes de usarlo.

import time


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

Implementar un controlador

Sobrescribe los miembros que admite tu navegador. Cada miembro recibe el contexto de la llamada y la entrada del miembro como un objeto tipado, como BetaBrowserNavigateInput. Los tipos de entrada provienen de anthropic.types.beta (@anthropic-ai/sdk/resources/beta en TypeScript). Herramientas miembro enumera los campos de cada entrada.

En TypeScript, escribe los miembros como métodos, no como campos de función flecha, porque el SDK los busca en el prototipo. Escribe el miembro type como type_ en TypeScript. En Python, es type.

Devolver resultados

Lo que devuelve un miembro determina lo que lee Claude. Un resultado exitoso termina con un bloque browser_state construido a partir de tu informe de estado. Un resultado de error no lleva ningún bloque.

MiembroDevuelveClaude lee
screenshot, zoomBetaBrowserScreenshotResultUn bloque de imagen
navigateBetaBrowserNavigateResultNavigated to {url} — {title} (HTTP {status})
new_tab, switch_tab, list_tabs, close_tabUna entrada de pestaña (new_tab, switch_tab), una lista de entradas de pestaña (list_tabs) o nada (close_tab)Solo el bloque browser_state
read_page, get_page_text, find, read_console, read_network, javascript_execUna cadenaLa cadena
Cualquier otro miembroNada, o una línea de textoUna confirmación breve, como Clicked., seguida de la línea devuelta en un bloque de texto propio

Informar el estado del navegador

El SDK llama a _browser_state (la opción browserState en TypeScript) después de cada llamada, incluidas las llamadas rechazadas y fallidas. Devuelve todas las pestañas abiertas y lo que cambió desde el último informe:

  • Las pestañas que se abrieron y los eventos de descarga.
  • Un NavigationRefused por cada navegación que bloqueó tu "request hook" (hook de solicitudes).
  • Un DialogDismissed por cada diálogo nativo que descartó tu controlador.

Coloca todo esto en state_changes. En Python, NavigationRefused(url=...) y DialogDismissed(kind=..., message=...) provienen de anthropic.tools.browser. En TypeScript, son { type: "navigation_refused", url } y { type: "dialog_dismissed", kind, message }.

Los dos últimos no son cambios de estado de la API. El SDK se los informa a Claude como texto fuera del bloque browser_state: una línea para todas las navegaciones rechazadas, que no nombra ninguna URL, y una línea para cada uno de los tres primeros diálogos descartados, seguida de un recuento de los demás.

Cuando hay alguna pestaña abierta, exactamente una debe estar activa. Todo miembro que acepte un tab_id debe actuar sobre la pestaña que nombra. El SDK usa el informe para determinar de qué página provino un resultado. Los límites de la API sobre el informe se enumeran en Rastrea pestañas con browser_state.

Manejar errores

Generado por un miembro o el SDKClaude leeLa ejecución
ToolErrorSu mensaje, como resultado de errorContinúa
Cualquier otra excepciónSu texto, como resultado de errorContinúa
ToolsetUsageError, por un error de configuración, un uso indebido del SDK durante una llamada o una llamada después de closeNadaSe detiene

Antes de que Claude lea el texto de error de un miembro, la línea que devuelve una acción como left_click, el error de una descarga fallida o el mensaje de un diálogo descartado, el SDK reemplaza cada URL que la política rechaza por (blocked). Reemplaza cada ruta local que la política de archivos no expone por (path hidden). La comprobación puede pasar por alto algunas URL y rutas. Un ToolError de tu política de URL, tu política de archivos o tu "callable" (invocable) confirm llega a Claude tal como está escrito, así que deja fuera de su texto las URL rechazadas y las rutas locales. Captura las excepciones en tus miembros y genera ToolError (lánzalo con throw en TypeScript) con tu propio texto.

Ejecutar sin el ejecutor de herramientas

Pasa la instancia en tools (browser.toJSON() en TypeScript) y responde a cada llamada de miembro con tool_result (toolResult en TypeScript). La herramienta de uso del navegador requiere que te detengas en la primera llamada fallida (Acciones por lotes). Después de una llamada fallida, este bucle responde a las llamadas posteriores del turno sin ejecutarlas:

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:
                # Tras una llamada fallida, el resto del turno se responde sin ejecutarse.
                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})

Cada respuesta a una llamada omitida lleva is_error, el toolset_name de la llamada y el texto exacto que requiere Acciones por lotes. El ejecutor de herramientas envía la misma respuesta.

Ejecutar el conjunto de herramientas de forma segura

La siguiente acción de Claude depende de las páginas que lee. Una página, o un texto inyectado en ella, puede intentar llegar a servicios internos o extraer archivos del host. También puede intentar desencadenar acciones con efectos reales. Antes de ejecutar un controlador contra cualquier cosa que no sea un navegador desechable, sigue estos seis pasos:

  1. Establece una política de URL: allowed_domains, blocked_domains o tu propia url_policy.
  2. Intercepta las solicitudes en el controlador y pide un veredicto al conjunto de herramientas.
  3. Aplica una "egress policy" (política de salida) en el contenedor, para que la red bloquee lo que el controlador no puede ver.
  4. Limita las cargas y descargas, o deja las cargas desactivadas.
  5. Controla los miembros con consecuencias con confirm.
  6. Aísla el host del navegador en un contenedor o VM dedicado para cada sesión.

El SDK aplica los pasos 1, 4 y 5 según los configures. Los pasos 2, 3 y 6 dependen de tu controlador y tu implementación. Las precauciones de Consideraciones de seguridad también se aplican.

Establecer una política de URL

Establece allowed_domains (allowedDomains en TypeScript) con los sitios que necesita la tarea. Una entrada en cualquiera de las listas es un dominio (que también abarca sus subdominios), una dirección IP o una red CIDR. Con allowed_domains establecido, el conjunto de herramientas rechaza cualquier otro host.

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

Si la tarea necesita la web abierta, establece blocked_domains (blockedDomains) en su lugar. Las listas comparan nombres de host sin resolverlos. Una entrada de lista de bloqueo 127.0.0.0/8 no bloquea localhost, así que nombra los hosts además de las redes. Cuando estableces ambas listas, prevalece blocked_domains.

Una lista de bloqueo no puede detectar un nombre público que se resuelve en una dirección privada. Por eso, con blocked_domains, la política de salida del contenedor es lo que impide que ese nombre llegue a una dirección privada.

# Enumera los hosts y redes a los que el navegador puede acceder, pero Claude no debe:
# rangos loopback, link-local y privados en IPv4 e IPv6, 0.0.0.0/8, las
# direcciones y nombres de metadatos de tu nube (como metadata.google.internal),
# localhost y tus nombres de host internos.
browser = MyBrowser(backend, blocked_domains=internal_networks)

Una url_policy (urlPolicy) reemplaza ambas listas, y pasarla junto con cualquiera de las listas es un error de configuración. Tu política no devuelve nada para permitir una URL, y genera (lanza) ToolError para rechazarla. Para conservar las reglas predeterminadas, construye la política predeterminada con default_url_policy (defaultURLPolicy) y llámala primero desde la tuya:

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 política se ejecuta en cada URL de navigate antes de tu código, en la URL que informa un resultado y en cada URL de pestaña y de descarga de un informe de estado. En los resultados y los informes de estado, omite las direcciones que no nombran ningún host remoto: una pestaña vacía, about:blank, la página chrome-error: del navegador y los documentos data:.

Con cualquier política, el conjunto de herramientas rechaza un navigate a un esquema distinto de http o https, excepto about:blank. url_policy="allow_all" (urlPolicy: "allow_all") desactiva la política, pero no esta regla de esquemas.

Pasar url_policy=None (urlPolicy: null) hace que el constructor genere un error de configuración, de modo que un None (null) leído de tu configuración no puede desactivar las comprobaciones. En TypeScript, undefined deja la opción sin establecer, igual que no pasarla.

Cuando una página llega a una dirección rechazada, Claude lee que su contenido se retuvo, y la pestaña aparece como (blocked). Hasta que la pestaña vuelva a estar en una dirección permitida, el conjunto de herramientas rechaza las llamadas sobre ella. Las excepciones son navigate (pero no "reload"), new_tab, list_tabs, switch_tab y close_tab.

Interceptar solicitudes en el controlador

La política de URL juzga solo las direcciones que ve el conjunto de herramientas: navegaciones, resultados e informes de estado. No ve los subrecursos, las llamadas fetch(), los WebSockets ni dónde se resuelve un nombre de host.

El conjunto de herramientas detecta una dirección nueva solo cuando termina una llamada, en el resultado o el informe de estado de la llamada. Así que una llamada todavía puede actuar sobre una página rechazada antes de eso, y el SDK como mucho puede retener el resultado de esa llamada. A menos que tu controlador juzgue cada solicitud, incluidos los saltos de redirección, una página que redirige a una dirección rechazada se carga de todos modos.

En el hook de solicitudes de tu controlador (la función que tu biblioteca de automatización llama antes de cada solicitud), llama a check_url (checkURL). Aplica la propia política del conjunto de herramientas, así que no escribes las reglas dos veces:

from anthropic.tools.browser import URLContext


class MyBrowser(BetaAbstractBrowserToolset20260801):
    ...

    # Registrado en un contexto de navegador de Playwright con 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 aplica la misma regla de esquemas y la misma política que navigate. Un hook de solicitudes como este no ve los handshakes de WebSocket, las solicitudes de service workers ni los saltos de redirección. Bloquea los service workers. Juzga los handshakes de WebSocket y los saltos de redirección con un hook que los vea.

En la fase de solicitud, check_url solo permite URL http, https y about:blank. Así que, para un WebSocket, cambia ws:// por http:// y wss:// por https:// antes de llamarlo.

Aplicar una política de salida en el contenedor

La interceptación no puede ver todas las solicitudes que hace el navegador, y la política de URL no ve dónde se resuelve un nombre. Una política de salida que aplique la red del contenedor cubre ambos casos:

  • Bloquea los rangos de direcciones loopback, link-local y privadas en IPv4 e IPv6, y 0.0.0.0/8. Eso incluye la dirección de metadatos de la nube 169.254.169.254.
  • Permite conexiones salientes solo a los hosts que necesita la tarea. Si tus reglas coinciden con direcciones IP, resuelve los nombres de host que permites cuando se inicia el contenedor.
  • Permite DNS solo hacia el resolvedor del contenedor.
  • Si tu controlador llega al navegador a través de un puerto local de DevTools, permite loopback solo en ese puerto. Una regla para todo loopback abriría todos los servicios locales a la página.

Limitar las cargas y descargas

file_upload está desactivado de forma predeterminada. Sin file_policy, el SDK rechaza toda carga que nombre una ruta o un ID de documento. Para habilitar las cargas, pasa una LocalFilePolicy (NodeFilePolicy en TypeScript) con un directorio de carga que contenga solo los archivos de la tarea:

from anthropic.tools.browser import LocalFilePolicy

# Un MyBrowser que también implementa 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,
    ),
)

El SDK resuelve cada ruta de carga, siguiendo los enlaces simbólicos, y rechaza cualquier ruta fuera de las raíces de carga. La política de archivos rechaza un directorio de descarga dentro de una raíz de carga. La ruta de una descarga llega a Claude solo cuando expose_download_paths (exposeDownloadPaths) es true y el archivo está dentro del directorio de descarga.

Las comprobaciones de rutas incluidas resuelven las rutas en el sistema de archivos del proceso que ejecuta el SDK. Solo protegen un navegador que comparte ese sistema de archivos. Para un navegador remoto, sigue Navegadores remotos y alojados en su lugar.

Configura las descargas de esta manera:

  • Crea tú mismo el directorio de descarga con el modo 0700 y móntalo con noexec,nosuid,nodev.
  • Mantén el directorio fuera del alcance de otras herramientas que Claude pueda llamar, como un shell o una herramienta de archivos.
  • En un cambio de estado download_failed, escribe error como una frase fija. El texto de una excepción puede contener la ruta o la URL.
  • No leas un archivo descargado en la conversación, ni lo ejecutes, hasta que una persona lo decida.

Controlar los miembros con consecuencias

javascript_exec y file_upload están desactivados de forma predeterminada. Si habilitas cualquiera de ellos sin un invocable confirm, el constructor genera un error de configuración. Con un invocable confirm, el SDK lo llama antes de cada llamada que está a punto de ejecutarse. Sin uno, no se pregunta nada.

Devuelve True para ejecutar la llamada, o False para rechazarla (true y false en TypeScript). Cuando tu invocable le pregunte a una persona, muéstrale el miembro, la URL de la página y la entrada de la llamada. Primero, escapa todos los caracteres de la entrada que estén fuera del ASCII imprimible, porque la entrada puede contener texto de la página.

Este ejemplo pregunta por los dos miembros controlados mediante tu propia función ask_user (askUser en TypeScript) y aprueba el resto:

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:"):
            # Sin origen, o una página de error: pregunta cada vez.
            return ask_user(
                f"Allow {name} on {page or 'a page with no origin'}?\n{detail}"
            )
        # Una aprobación cubre solo esta entrada exacta en esta página.
        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 que también implementa javascript_exec y file_upload
browser = MyBrowser(
    backend,
    configs={"javascript_exec": {"enabled": True}, "file_upload": {"enabled": True}},
    confirm=make_confirm(),
)

Cada llamada a make_confirm() (makeConfirm() en TypeScript) devuelve un invocable sin aprobaciones. Llámala una vez por cada conjunto de herramientas y dale a cada usuario su propio conjunto de herramientas.

Una aprobación abarca la página tal como la mostró el último informe de estado, y la página puede cambiar antes de que se ejecute la llamada. Las compras, los mensajes enviados y los términos aceptados ocurren a través de miembros ordinarios como left_click y type, por lo que confirm no puede identificarlos por nombre. Para que una persona los apruebe, pregunta también por esos miembros.

No habilites javascript_exec en un controlador que no intercepte solicitudes. Un script que se ejecuta en una página rechazada puede copiar su contenido a un lugar donde una lectura posterior lo devuelva.

Aislar el host del navegador

Ejecuta el navegador en un contenedor o VM dedicado y con privilegios mínimos para cada sesión:

  • Ejecútalo como un usuario que no sea root, con un sistema de archivos raíz de solo lectura donde el navegador lo permita.
  • No montes nada del host más allá de los directorios de carga y descarga que configuraste, si los hay.
  • Mantén las credenciales fuera del entorno y empieza con un perfil de navegador nuevo.
  • No compartas ningún sistema de archivos con otras herramientas que Claude pueda llamar.

Ejecuta el código que llama a la API fuera del contenedor del navegador, porque ese código contiene tu clave de API y la conversación. El ejecutor de herramientas y tool_result ejecutan el conjunto de herramientas en el proceso de ese código, por lo que el navegador no comparte el sistema de archivos del conjunto de herramientas. Trata el navegador como remoto: se aplica Navegadores remotos y alojados.

Trata todo lo que devuelve una página como no confiable, incluidos el texto de la página, las capturas de pantalla, las entradas de consola y de red, los títulos de las pestañas y los nombres de las descargas.

Navegadores remotos y alojados

Algunos navegadores no comparten un sistema de archivos con el proceso que ejecuta el SDK. Algunos ejemplos son un navegador en otro contenedor, uno al que accedes mediante una URL de DevTools y uno de un servicio de navegador alojado. Con estos, la política de URL, la interceptación de solicitudes y confirm siguen ejecutándose en tu proceso.

Con un navegador alojado, el proveedor controla la salida y el aislamiento del host. Tu propia política de salida no se aplica allí, por lo que el hook de solicitudes del controlador es tu única comprobación sobre las solicitudes del navegador. Averigua a qué puede llegar la red del navegador.

Las comprobaciones de rutas incluidas no protegen un navegador remoto. LocalFilePolicy (NodeFilePolicy en TypeScript) comprueba las rutas en el sistema de archivos del proceso que ejecuta el SDK, y el navegador lee y escribe en su propio sistema de archivos.

El SDK no puede detectar que un navegador es remoto. Así que, para un navegador remoto, mantén file_upload desactivado a menos que tu controlador compruebe las rutas de carga donde se ejecuta el navegador, con una FilePolicy propia. Una FilePolicy examina las rutas y los ID de documento de cada carga, y decide si Claude ve la ruta de una descarga.

Haz que un navegador remoto rechace las descargas, a menos que su propio host tenga la configuración de descargas de Limitar las cargas y descargas.

Los controladores de ejemplo siguen esta regla. En un navegador remoto, generan un error de configuración ante una file_policy (filePolicy) o un file_upload habilitado, y configuran el navegador para que rechace las descargas.

Mantén la clave de API del proveedor y la URL de conexión de la sesión, que puede contener una clave, fuera de los registros, los resultados de herramientas y el texto de error. Si el proveedor graba las sesiones, la grabación es otra copia de todo lo que Claude vio y escribió, y los términos de retención del proveedor se aplican a ella.

Referencia

Las opciones del constructor tienen el mismo significado en ambos SDK:

PythonTypeScriptEstablece
configsconfigsQué miembros están habilitados
confirmconfirmEl invocable que aprueba o rechaza cada llamada
allowed_domains, blocked_domainsallowedDomains, blockedDomainsLas listas de la política de URL predeterminada
url_policyurlPolicyTu propia política de URL
file_policyfilePolicyLas raíces de carga y la exposición de las rutas de descarga
tool_configstoolConfigsCampos para la entrada de tools, como cache_control
El método _browser_statebrowserStateEl informe de estado

No puedes cambiar una opción después de la construcción. Los valores predeterminados, los errores, los campos de contexto y la clase asíncrona de Python (BetaAsyncAbstractBrowserToolset20260801) están documentados en el SDK de Python y el SDK de TypeScript.

Limitaciones

  • La política de URL comprueba las navegaciones, no todas las solicitudes: Consulta Interceptar solicitudes en el controlador.
  • Una aprobación se basa en el último informe de estado: La página puede cambiar después de ese informe. El SDK no vuelve a comprobar la página antes de que se ejecute la llamada.
  • Las llamadas en un mismo conjunto de herramientas se ejecutan de una en una: No puedes desactivar esto.
  • El SDK no comprueba que los ID de pestaña sean únicos, que haya exactamente una pestaña activa ni cuántas pestañas hay: La API rechaza un informe que incumpla esas reglas.

Próximos pasos

Las herramientas miembro, el bloque browser_state y las consideraciones de seguridad de la herramienta.

Cómo el SDK ejecuta el bucle y cómo cambiar los mensajes que envía.

Barreras de protección para cualquier aplicación que lea contenido no confiable.

Was this page helpful?