---
title: Использование браузера и компьютера с наборами инструментов SDK
url: https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk
description: Запускайте инструмент использования браузера или инструмент использования компьютера из Python или TypeScript SDK. SDK выполняет цикл и настроенные вами проверки, а браузер или рабочий стол предоставляете вы.
---

С наборами инструментов браузера и компьютера в Python и TypeScript SDK вы можете создать агента, который работает в веб-браузере или на рабочем столе. Он может пользоваться приложениями, у которых нет программного интерфейса. С помощью [инструмента использования браузера](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-tool) Claude переходит по веб-страницам, читает их и взаимодействует с ними. С помощью [инструмента использования компьютера](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/computer-use-tool) Claude видит рабочий стол на снимках экрана и пользуется мышью и клавиатурой.

SDK связывает Claude с вашим браузером или рабочим столом. Он выполняет настроенные вами проверки, например «URL policy» (политику URL) для `navigate` или обратный вызов подтверждения. Каждый вызов, прошедший проверки, он передаёт вашему «driver» (драйверу) и формирует `tool_result` из того, что возвращает драйвер. [«Tool runner» (исполнитель инструментов)](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/tool-runner) может выполнять «agentic loop» (агентный цикл) за вас.

Драйвер, который управляет браузером или рабочим столом, предоставляете вы. Начните с [драйвера от провайдера](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#use-a-driver-from-a-provider). С ним агент занимает несколько строк кода. Если ни один не подходит, напишите собственный с помощью библиотеки автоматизации браузера, например Playwright.

Большая часть этой страницы посвящена набору инструментов браузера. В разделе [Набор инструментов компьютера](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#computer-toolset) описано, чем он отличается.

Написание собственного драйвера — продвинутый сценарий, описанный в разделе [Реализация драйвера](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver). В репозитории claude-quickstarts также есть [пример драйвера браузера](https://github.com/anthropics/claude-quickstarts/tree/main/browser-toolset) на Python и TypeScript. Он управляет Chromium через Chrome DevTools Protocol (CDP) и не является кодом для продакшена.

<Note>
  Класс набора инструментов браузера находится в бета-версии и доступен в [Python SDK](https://github.com/anthropics/anthropic-sdk-python/blob/main/browser-toolset.md) и [TypeScript SDK](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/browser-toolset.md). Класс набора инструментов компьютера находится в бета-версии в тех же SDK, для которых есть [руководство по набору инструментов компьютера для Python](https://github.com/anthropics/anthropic-sdk-python/blob/main/computer-toolset.md) и [руководство по набору инструментов компьютера для TypeScript](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/computer-toolset.md).
</Note>

## Использование драйвера от провайдера

С драйвером от провайдера, например одного из перечисленных в разделе [Интеграции партнёров](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#partner-integrations), вы можете создать агента в несколько строк кода. Провайдер в этом примере вымышленный. Замените `example_browser_provider` (typescript: `example-browser-provider`) и `ExampleBrowser` на пакет и класс драйвера [реального провайдера](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#partner-integrations) и настройте их так, как описано в документации провайдера.

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

Передайте сам экземпляр драйвера в качестве элемента `tools`. Исполнитель инструментов никогда не закрывает набор инструментов, поэтому один экземпляр может обслуживать несколько запусков. Закройте его, когда закончите.

Чтобы написать собственный драйвер, см. раздел [Реализация драйвера](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver).

## Интеграции партнёров

Browser Use, Browserbase, Daytona и E2B публикуют собственные драйверы для наборов инструментов 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">
    Драйвер Browser Use для набора инструментов браузера.
  </Card>

  <Card id="browserbase" title="Browserbase" image="https://platform.claude.com/docs/images/partner-logos/browserbase.svg" imageDark="https://platform.claude.com/docs/images/partner-logos/browserbase-dark.svg" href="https://docs.stagehand.dev/v4/integrations/agent-frameworks/claude-cua-toolset-quickstart">
    Драйвер Browserbase Stagehand для набора инструментов браузера.
  </Card>

  <Card id="daytona" title="Daytona" image="https://platform.claude.com/docs/images/partner-logos/daytona.svg" imageDark="https://platform.claude.com/docs/images/partner-logos/daytona-dark.svg" href="https://www.daytona.io/docs/en/guides/claude/claude-draws-daytona-sandbox/">
    Драйвер Daytona для набора инструментов компьютера.
  </Card>

  <Card id="e2-b" title="E2B" image="https://platform.claude.com/docs/images/partner-logos/e2b.svg" imageDark="https://platform.claude.com/docs/images/partner-logos/e2b-dark.svg" href="https://docs.e2b.dev/agents/claude-toolsets">
    Драйверы E2B для наборов инструментов браузера и компьютера.
  </Card>
</CardGroup>

Наши партнёры также публикуют код на GitHub: [быстрый старт Browser Use](https://github.com/browser-use/browser-use/tree/main/examples/integrations/toolsets-for-claude), [набор инструментов Browserbase](https://github.com/browserbase/claude-cua-toolset) и [пример E2B](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/anthropic-computer-use-orangehrm-js).

<Warning>
  Сделайте эти две вещи, прежде чем запускать драйвер с чем-либо, кроме одноразового браузера:

  * [Напишите политику URL](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy). Без неё SDK не проверяет ни один URL. Для браузера, размещённого у провайдера, также прочитайте раздел [Удалённые и размещённые браузеры](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#remote-and-hosted-browsers).
  * Используйте профиль браузера, в котором не выполнен вход ни в одну учётную запись, данные или действия которой вы не доверили бы Claude. См. [Изоляция хоста браузера](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#isolate-the-browser-host).

  Остальное описано в разделе [Безопасный запуск набора инструментов](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#run-the-toolset-safely).
</Warning>

## Настройка драйвера

В примерах этого раздела используется `MyBrowser` — драйвер из [продвинутого примера](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#quick-start) далее на этой странице.

### Включение и отключение членов

`configs` принимает настройки для каждого члена, описанные в разделе [Настройка набора инструментов](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-tool#configure-the-toolset). Перечислите только те члены, которые вы меняете:

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  # MyBrowser, который также реализует read_console
  browser = MyBrowser(
      backend, configs={"read_console": {"enabled": True}, "navigate": {"enabled": False}}
  )
  ```

  ```typescript TypeScript
  // MyBrowser, который также реализует read_console
  const browser = new MyBrowser(backend, {
    configs: { read_console: { enabled: true }, navigate: { enabled: false } }
  });
  ```
</CodeGroup>

SDK отклоняет вызов отключённого члена до того, как выполнится ваш код. Включение члена, которого ваш класс не реализует, является ошибкой конфигурации, если только класс не переопределяет `execute`.

### Добавление хуков до и после

Переопределите `execute` и вызовите `execute` родителя. Код перед этим вызовом выполняется после [политики URL](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy), [политики файлов](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#confine-uploads-and-downloads) и [`confirm`](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#gate-consequential-members), и он может изменить входные данные. SDK не проверяет изменённые входные данные повторно. Код после вызова получает результат и может его изменить. Вызовите исключение `ToolError` (в TypeScript — через throw), чтобы отклонить вызов.

Переопределение `execute` меняет набор членов, предлагаемых Claude. SDK считает каждый член реализованным, поэтому Claude предлагаются все члены, включённые по умолчанию. `MyBrowser` из [продвинутого примера](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#quick-start) обслуживает три члена, поэтому следующий `TracedBrowser` предлагает Claude члены, которые он не может обслужить. Отключите эти члены с помощью `configs`, прежде чем его использовать.

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

## Реализация драйвера

Драйвер — это ваш подкласс `BetaAbstractBrowserToolset20260801`. В нём переопределите члены, которые поддерживает ваш браузер. Член, который вы не реализуете, отправляется в API как отключённый. Если Claude всё же вызовет его, SDK вернёт ошибку, и запуск продолжится. Переопределение `execute` меняет набор членов, отправляемых как отключённые ([Добавление хуков до и после](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#add-before-and-after-hooks)). Каждый член получает контекст вызова и входные данные члена в виде типизированного объекта, например `BetaBrowserNavigateInput`. Типы входных данных берутся из `anthropic.types.beta` (typescript: `@anthropic-ai/sdk/resources/beta`). В разделе [Инструменты-члены](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-tool#member-tools) перечислены поля каждого типа входных данных.

В TypeScript пишите члены как методы, а не как поля со стрелочными функциями, потому что SDK находит их в прототипе. В TypeScript член `type` пишется как `type_`. В Python — `type`.

### Возврат результатов

То, что возвращает член, определяет, что прочитает Claude. Успешный результат заканчивается блоком `browser_state`, построенным из вашего отчёта о состоянии. Результат с ошибкой не содержит этого блока.

| Член                                                                                    | Возвращает                                                                                              | Claude читает                                                                                       |
| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| `screenshot`, `zoom`                                                                    | `BetaScreenshotResult`                                                                                  | Один блок изображения                                                                               |
| `navigate`                                                                              | `BetaBrowserNavigateResult`                                                                             | `Navigated to {url} — {title} (HTTP {status})`                                                      |
| `new_tab`, `switch_tab`, `list_tabs`, `close_tab`                                       | Запись вкладки (`new_tab`, `switch_tab`), список записей вкладок (`list_tabs`) или ничего (`close_tab`) | Только блок `browser_state`                                                                         |
| `read_page`, `get_page_text`, `find`, `read_console`, `read_network`, `javascript_exec` | Строку                                                                                                  | Строку или `(empty)` для пустой строки                                                              |
| Любой другой член                                                                       | Ничего или одну строку текста                                                                           | Краткое подтверждение, например `Clicked.`, а затем возвращённую строку в отдельном текстовом блоке |

### Отчёт о состоянии браузера

SDK вызывает `_browser_state` (typescript: `browserState`) после каждого вызова, который возвращает результат, включая отклонённые и неудавшиеся вызовы. Возвращайте каждую открытую вкладку и то, что изменилось с момента последнего отчёта:

* Открытые вкладки и события скачивания.
* `BetaNavigationRefused` для каждой навигации, которую заблокировал ваш [хук запросов](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#intercept-requests-in-the-driver).
* `BetaDialogDismissed` для каждого нативного диалога, который закрыл ваш драйвер.

Поместите всё это в `state_changes`. В Python `BetaNavigationRefused()` и `BetaDialogDismissed(kind=..., message=...)` берутся из `anthropic.tools.browser`. В TypeScript это `{ type: "navigation_refused" }` и `{ type: "dialog_dismissed", kind, message }`.

Последние два не являются изменениями состояния API. SDK сообщает о них Claude текстом вне блока `browser_state`: одной строкой для всех отклонённых навигаций, без URL, и по строке для каждого из первых трёх закрытых диалогов, а затем количеством остальных.

Когда открыта хотя бы одна вкладка, ровно одна должна быть активной. Каждый член, принимающий `tab_id`, должен действовать на указанную вкладку. Ограничения API на отчёт перечислены в разделе [Отслеживание вкладок с помощью `browser_state`](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-tool#track-tabs-and-page-state).

Claude читает URL каждой вкладки, URL каждой загрузки и URL, который возвращает ваш `navigate`, в том виде, в каком их записал ваш драйвер. SDK не разбирает эти URL. Он заменяет переводы строк и другие управляющие символы пробелами, обрезает пробелы по краям и обрезает каждый URL до 4 096 символов. Поэтому содержимое URL `data:`, путь URL `file:`, а также имя пользователя или пароль в URL — всё это доходит до Claude.

### Обработка ошибок

| Вызвано членом или SDK  | Claude читает                                                                               | Выполнение      |
| ----------------------- | ------------------------------------------------------------------------------------------- | --------------- |
| `ToolError`             | Его сообщение в виде результата с ошибкой                                                   | Продолжается    |
| `ToolsetUsageError`     | Ничего                                                                                      | Останавливается |
| Любое другое исключение | `ClassName: message` в Python или `Error: message` в TypeScript в виде результата с ошибкой | Продолжается    |

SDK вызывает `ToolsetUsageError` при ошибке конфигурации, неправильном использовании SDK во время вызова или вызове после `close`. Он также вызывает его, когда `_browser_state` (typescript: `browserState`) вызывает любое исключение, даже `ToolError`. Исполнитель инструментов и `tool_result` (typescript: `toolResult`) не перехватывают его, поэтому оно доходит до вызывающего их кода.

SDK не скрывает локальные пути в тексте ошибки члена, в строке, которую возвращает действие вроде `left_click`, в ошибке неудавшейся загрузки или в сообщении закрытого диалога.

`ToolError` из вашей политики URL, политики файлов или вызываемого объекта `confirm` доходит до Claude в исходном виде, поэтому не включайте в его текст локальные пути. Перехватывайте исключения в своих членах и вызывайте `ToolError` (в TypeScript — через throw) с собственным текстом.

### Запуск вызовов во время потоковой передачи ответа

По умолчанию исполнитель инструментов выполняет вызовы хода после завершения ответа Claude. С ранним запуском он может начать вызов браузера или компьютера, пока ответ ещё передаётся потоком.

Чтобы включить его, передайте исполнителю инструментов `stream=True` (typescript: `stream: true`) и `run_tools_eagerly=True` (typescript: `runToolsEagerly: true`), как в [продвинутом примере](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#quick-start). С `stream` каждая итерация вашего цикла по исполнителю даёт вам поток вместо сообщения.

Ранний запуск меняет то, когда вызов может начаться, но не то, как набор инструментов его выполняет:

* **Проверки по-прежнему выполняются первыми:** SDK вызывает вашу политику URL, политику файлов и `confirm` до выполнения вызова. С ранним запуском он может вызвать `confirm`, пока ответ ещё передаётся потоком.
* **Вызовы по-прежнему выполняются по одному:** Набор инструментов выполняет свои вызовы в том порядке, в котором их написал Claude. Неудавшийся вызов по-прежнему останавливает последующие вызовы этого набора инструментов в ходе.
* **Начатый вызов нельзя отменить:** Если ответ затем обрывается, например на `max_tokens`, или ваш цикл останавливается раньше, действие всё равно происходит, а Claude так и не прочитает его результат.

### Запуск без исполнителя инструментов

Передайте экземпляр в `tools` (`browser.toJSON()` в TypeScript) и отвечайте на каждый вызов члена с помощью `tool_result` (typescript: `toolResult`). Инструмент использования браузера требует останавливаться на первом неудавшемся вызове ([Пакетные действия](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-tool#batch-actions)).

Этот цикл использует `MyBrowser` и политику URL из [продвинутого примера](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#quick-start). После неудавшегося вызова он отвечает на последующие вызовы хода, не выполняя их:

<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:
                  # После неудачного вызова остаток хода получает ответы, а не выполняется.
                  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) {
          // После неудачного вызова остаток хода получает ответы, но не выполняется.
          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>

Каждый ответ на пропущенный вызов содержит `is_error`, `toolset_name` вызова и точный текст, которого требует раздел [Пакетные действия](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-tool#batch-actions). Исполнитель инструментов отправляет такой же ответ.

## Продвинутый пример: написание собственного драйвера

Этот пример объединяет основные части раздела [Реализация драйвера](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver) в одном драйвере `MyBrowser` и запускает его с исполнителем инструментов и [ранним запуском](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#start-calls-while-the-response-streams). `MyBrowser` реализует `navigate`, `screenshot` и `left_click`, а также `_browser_state` (typescript: `browserState`) — отчёт о состоянии, который нужен каждому драйверу.

`backend` обозначает вашу собственную обёртку над библиотекой автоматизации браузера, например Playwright. Политика URL приведена в качестве примера. Она объясняется в разделе [Написание политики URL](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy).

<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 — это "back", "forward", "reload" или URL, который ваша политика URL
          # разрешила, в том виде, как его написал Claude. Как и is_allowed, backend.goto добавляет https://
          # к URL, который не начинается со схемы.
          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:
          # Возвращать нечего: Claude читает "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:
      # Это пример, а не политика для продакшена. См. «Написание политики URL».
      if url.lower() == "about:blank":
          return True
      with_scheme = url if SCHEME_PREFIX.match(url) else f"https://{url}"
      # Браузер воспринимает "\" как "/" в веб-URL.
      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,
        // Обязательно: все открытые вкладки и что изменилось с момента последнего отчёта.
        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 — это "back", "forward", "reload" или URL, который ваша политика URL
      // разрешила, в том виде, как его написал Claude. Как и isAllowed, backend.goto добавляет https:// к
      // URL, который не начинается со схемы.
      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> {
      // Возвращать нечего: Claude прочитает "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;

  // Пример, а не политика для продакшена. См. «Написание политики URL».
  function isAllowed(url: string): boolean {
    if (url.toLowerCase() === "about:blank") {
      return true;
    }
    const withScheme = SCHEME_PREFIX.test(url) ? url : `https://${url}`;
    // Браузер читает "\" как "/" в веб-URL.
    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>

## Безопасный запуск набора инструментов

Следующее действие Claude зависит от страниц, которые он читает. Страница или внедрённый в неё текст может попытаться получить доступ к внутренним сервисам или вытащить файлы с хоста. Она также может попытаться запустить действия с реальными последствиями. Прежде чем запускать драйвер с чем-либо, кроме одноразового браузера, выполните эти шесть шагов:

1. [Напишите политику URL](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy), которая отклоняет каждый URL `navigate`, не нужный для задачи.
2. [Перехватывайте запросы в драйвере](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#intercept-requests-in-the-driver) и проверяйте каждый запрос по тем же правилам.
3. [Примените «egress policy» (политику исходящего трафика) к контейнеру](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container), чтобы сеть блокировала то, чего не видит драйвер.
4. [Ограничьте загрузки и скачивания](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#confine-uploads-and-downloads) или оставьте загрузки отключёнными.
5. [Контролируйте значимые члены](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#gate-consequential-members) с помощью `confirm`.
6. [Изолируйте хост браузера](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#isolate-the-browser-host) в выделенном контейнере либо в «virtual machine» (виртуальной машине), или VM, для каждого сеанса.

SDK применяет шаги 1, 4 и 5 через переданные вами политики и вызываемый объект. Шаги 2, 3 и 6 зависят от вашего драйвера и вашего развёртывания. Меры предосторожности из раздела [Соображения безопасности](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-tool#security-considerations) также применяются.

<Note>
  Классификаторы «prompt injection» (внедрения подсказок) от Anthropic, описанные в разделе [Соображения безопасности](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-tool#security-considerations), в настоящее время не работают для запросов набора инструментов браузера, отправляемых через Amazon Bedrock.
</Note>

### Написание политики URL

<Warning>
  Без политики URL SDK не проверяет ни один URL, а API не фильтрует URL, которые открывает Claude. Даже с политикой ссылка, по которой кликает Claude, или перенаправление может привести браузер на loopback (`localhost`, `127.0.0.1`), на link-local адреса, такие как адрес облачных метаданных `169.254.169.254`, и в частные сетевые диапазоны. [Правила исходящего трафика на контейнере](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container) могут заблокировать link-local и частные адреса. Правила, применяемые вне контейнера, не могут заблокировать loopback.

  SDK не проверяет схему URL, поэтому ваш драйвер должен отклонять каждую схему, которую он не собирается открывать, например `javascript:`, `view-source:`, `data:` и `file:`. Большинству драйверов нужны только `http` и `https`. Приведённая ниже политика-пример разрешает только URL `http` и `https`, а также `about:blank`.

  URL `file:` позволяет Claude читать файлы на хосте браузера. URL `javascript:` выполняет скрипт на текущей странице, даже когда `javascript_exec` отключён. Правила исходящего трафика не останавливают ни то, ни другое.
</Warning>

«URL policy» (политика URL) — это функция, которую вы передаёте как `url_policy` (typescript: `urlPolicy`). Вы пишете политику и отвечаете за её готовность к продакшену. Перед выполнением вашего `navigate` SDK вызывает политику с контекстом вызова и URL в том виде, в каком его написал Claude.

Ничего не возвращайте, чтобы разрешить URL. Чтобы отклонить его, вызовите `ToolError` (в TypeScript — через throw). Claude читает сообщение ошибки, и `navigate` не выполняется. Политика проверяет только URL в каждом вызове `navigate`. Она не является средством сетевого контроля.

Это политика из [продвинутого примера](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#quick-start). Она разрешает `about:blank`, а также два сайта и их поддомены по `http` и `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:
      # Пример, а не политика для продакшена.
      if url.lower() == "about:blank":
          return True
      with_scheme = url if SCHEME_PREFIX.match(url) else f"https://{url}"
      # Браузер читает «\» как «/» в веб-URL.
      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;

  // Пример, а не политика для продакшена.
  function isAllowed(url: string): boolean {
    if (url.toLowerCase() === "about:blank") {
      return true;
    }
    const withScheme = SCHEME_PREFIX.test(url) ? url : `https://${url}`;
    // Браузер интерпретирует «\» как «/» в веб-URL.
    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>

SDK вызывает политику только для `navigate`, но не для `"back"`, `"forward"` или `"reload"`. Claude может опустить схему, как в `example.com`, поэтому перед чтением хоста пример добавляет `https://` к URL, который не начинается со схемы. Ваш `navigate` получает URL в том виде, в каком его написал Claude. Добавьте схему там с той же проверкой, чтобы браузер открыл именно тот URL, который проверила ваша политика.

Пример не готов для продакшена. Политике для продакшена нужно гораздо больше, в таких областях, как:

* Ссылки, по которым кликает Claude, формы, которые он отправляет, перенаправления и запросы, которые страница делает сама. Их видит только ваш драйвер. См. [Перехват запросов в драйвере](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#intercept-requests-in-the-driver).
* Имена хостов и URL, взятые из содержимого страницы, которые может выбрать злоумышленник.
* Разбор каждого URL так же, как это делает браузер, а не поиск имени хоста простым разбиением строки.
* Схемы URL. SDK их не проверяет, поэтому отклоняйте каждую схему, которую ваш драйвер не собирается открывать.
* Правила исходящего трафика на контейнере для того, что не видит ни одна проверка URL. См. [Применение политики исходящего трафика к контейнеру](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container).

Если вы передадите `url_policy=None` (typescript: `urlPolicy: null`), SDK отклонит каждый `navigate` на URL, поэтому `None` (typescript: `null`), прочитанный из вашей конфигурации, не может случайно оставить URL непроверенными. В TypeScript `undefined` оставляет параметр незаданным — так же, как если бы вы его не передали.

### Перехват запросов в драйвере

Политика URL оценивает только URL в вызове `navigate`. Ссылка, по которой кликает Claude, отправляемая им форма, перенаправление или изображение либо скрипт на разрешённой странице всё ещё могут достучаться до хоста, который политика отклонила бы. Используйте [правила исходящего трафика](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container), чтобы заблокировать такие хосты.

Драйвер с перехватом запросов также может проверять эти запросы в своём хуке запросов — функции, которую ваша библиотека автоматизации вызывает перед каждым запросом. Вызывайте функцию, которую использует ваша политика, чтобы писать правила один раз:

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

      # Регистрируется в контексте браузера Playwright через context.route("**/*", self._guard)
      def _guard(self, route):
          # is_allowed — функция из раздела «Write a URL policy»
          if not is_allowed(route.request.url):
              return route.abort("blockedbyclient")
          route.continue_()
  ```

  ```typescript TypeScript
  // В классе драйвера, в контексте браузера Playwright.
  // isAllowed — это функция из раздела «Write a URL policy».
  await context.route("**/*", (route) =>
    isAllowed(route.request().url()) ? route.continue() : route.abort("blockedbyclient")
  );
  ```
</CodeGroup>

Хук запросов, подобный этому, не видит рукопожатия WebSocket, запросы service worker или промежуточные шаги перенаправлений. Блокируйте service worker. Проверяйте рукопожатия WebSocket и шаги перенаправлений с помощью хука, который их видит. Политика-пример отклоняет URL `ws://` и `wss://`, поэтому замените `ws://` на `http://` и `wss://` на `https://`, прежде чем передавать URL WebSocket в `is_allowed` (typescript: `isAllowed`).

### Применение политики исходящего трафика к контейнеру

Перехват не может увидеть каждый запрос, который делает браузер, а политика URL не видит, во что разрешается имя. Политика исходящего трафика, которую обеспечивает сеть контейнера, покрывает и то, и другое:

* Блокируйте link-local и частные диапазоны адресов в IPv4 и IPv6. Это включает адрес облачных метаданных `169.254.169.254`.
* Разрешайте исходящие соединения только к хостам, нужным для задачи. Если ваши правила сопоставляют IP-адреса, разрешайте имена разрешённых хостов при запуске контейнера.
* Разрешайте DNS только к резолверу контейнера.

Правила, применяемые вне контейнера браузера, например Kubernetes NetworkPolicy или облачный файрвол, не видят loopback внутри него. Только с такими правилами страница может достучаться до всего, что там слушает, включая порт DevTools, который показывает список открытых вкладок браузера и адрес, управляющий каждой из них.

### Ограничение загрузок и скачиваний

`file_upload` по умолчанию отключён. Без `file_policy` (typescript: `filePolicy`) SDK отклоняет каждую загрузку, в которой указан путь или [ID документа](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-tool#upload-files). Чтобы включить загрузку файлов, передайте `BetaLocalFilePolicy` (typescript: `BetaNodeFilePolicy`) с одним каталогом загрузки, в котором находятся только файлы задачи:

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

  # MyBrowser, который также реализует 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";

  // MyBrowser, который также реализует 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>

SDK разрешает каждый путь загрузки, следуя по символическим ссылкам, и отклоняет любой путь вне корней загрузки. Политика файлов вызывает ошибку конфигурации для каталога скачивания, который является корнем загрузки, находится внутри него или содержит его. Эта проверка может пропустить символическую ссылку, поэтому не допускайте, чтобы ссылка делала каталог скачивания тем же, что и корень загрузки, или помещала один внутрь другого. SDK включает `path` скачивания в отчёт о состоянии, только когда `expose_download_paths` (typescript: `exposeDownloadPaths`) имеет значение true и файл находится внутри каталога скачивания.

Поставляемая политика файлов проверяет пути в файловой системе процесса, в котором работает SDK. Она защищает только браузер, использующий ту же файловую систему. Для удалённого браузера следуйте разделу [Удалённые и размещённые браузеры](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#remote-and-hosted-browsers).

Настройте скачивания следующим образом:

* Создайте каталог скачивания самостоятельно с режимом `0700` и смонтируйте его с `noexec,nosuid,nodev`.
* Держите каталог вне досягаемости других инструментов, которые может вызывать Claude, например оболочки или файлового инструмента.
* В изменении состояния `download_failed` записывайте `error` как фиксированную фразу. Текст исключения может содержать путь или URL.
* Не считывайте скачанный файл в разговор и не запускайте его, пока это не решит человек.

### Контроль значимых членов

`javascript_exec` и `file_upload` по умолчанию отключены. Если вы включите любой из них без вызываемого объекта `confirm`, конструктор вызовет ошибку конфигурации. С вызываемым объектом `confirm` SDK вызывает его перед каждым вызовом, который вот-вот выполнится. Без него ничего не запрашивается. В TypeScript `confirm: null` не вызывает ошибку конфигурации, и SDK отклоняет каждый вызов любого члена.

Верните `True` (typescript: `true`), чтобы выполнить вызов, или `False` (typescript: `false`), чтобы отклонить его. Когда ваш вызываемый объект спрашивает человека, покажите ему член, URL страницы и входные данные вызова. Сначала экранируйте каждый символ вне печатаемого ASCII в URL и входных данных, потому что и то, и другое может содержать текст со страницы.

Этот пример спрашивает о двух защищённых членах через вашу собственную функцию `ask_user` (typescript: `askUser`) и одобряет остальные:

<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
          # Одобрение распространяется только на этот конкретный ввод на этой странице.
          key = (name, page, detail)
          if key not in granted and ask_user(question):
              granted.add(key)
          return key in granted

      return confirm


  # MyBrowser, который также реализует javascript_exec и file_upload
  # Для file_upload также нужна file_policy из раздела «Ограничение загрузок и скачиваний»
  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
      }
      // Одобрение распространяется только на этот конкретный ввод на этой странице.
      const key = `${name} ${page} ${detail}`;
      if (!granted.has(key) && (await askUser(question))) {
        granted.add(key);
      }
      return granted.has(key);
    };
  };

  // MyBrowser, который также реализует javascript_exec и file_upload
  // Для file_upload также нужна filePolicy из раздела «Confine uploads and downloads»
  const browser = new MyBrowser(backend, {
    configs: { javascript_exec: { enabled: true }, file_upload: { enabled: true } },
    confirm: makeConfirm(),
    urlPolicy
  });
  ```
</CodeGroup>

Каждый вызов `make_confirm()` (typescript: `makeConfirm()`) возвращает вызываемый объект без одобрений. Вызывайте его один раз для каждого набора инструментов и давайте каждому пользователю собственный набор инструментов.

Одобрение распространяется на страницу в том виде, в каком её показал последний отчёт о состоянии, а страница может измениться до выполнения вызова. Покупки, отправленные сообщения и принятые условия происходят через обычные члены, такие как `left_click` и `type`, поэтому `confirm` не может выделить их по имени. Чтобы их одобрял человек, спрашивайте и об этих членах.

Не включайте `javascript_exec`, если [правила исходящего трафика](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container) не ограничивают исходящие соединения браузера хостами, нужными для задачи. Скрипт может отправить содержимое страницы на любой адрес, доступный браузеру.

URL `javascript:` в вызове `navigate` также выполняет скрипт на текущей странице, даже когда `javascript_exec` отключён. `confirm` видит этот вызов как `navigate`, поэтому вызываемый объект `confirm` из примера одобрил бы его. Ваш драйвер должен отклонять URL `javascript:`. В примере [политика URL](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy) отклоняет их до выполнения `confirm`.

### Изоляция хоста браузера

Запускайте браузер в выделенном контейнере или VM с минимальными привилегиями для каждого сеанса:

* Запускайте от имени пользователя без прав root, с корневой файловой системой только для чтения, если браузер это допускает.
* Не монтируйте с хоста ничего, кроме настроенных вами каталогов загрузки и скачивания, если они есть.
* Не допускайте учётных данных в окружении и начинайте со свежего профиля браузера.
* Не используйте общую файловую систему с другими инструментами, которые может вызывать Claude.

<Warning>
  В профиле браузера не должен быть выполнен вход ни в одну учётную запись, данные или действия которой вы не доверили бы Claude. `javascript_exec` выполняется с полномочиями самой страницы: её файлами cookie, её хранилищем и её сеансами с выполненным входом.
</Warning>

Запускайте код, который вызывает API, вне контейнера браузера, потому что этот код хранит ваш ключ API и разговор. Исполнитель инструментов и `tool_result` (typescript: `toolResult`) оба выполняют набор инструментов в процессе этого кода, поэтому браузер не использует файловую систему набора инструментов. Считайте браузер удалённым: применяется раздел [Удалённые и размещённые браузеры](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#remote-and-hosted-browsers).

Считайте недоверенным всё, что возвращает страница, включая текст страницы, снимки экрана, записи консоли и сети, заголовки вкладок и имена скачанных файлов.

### Удалённые и размещённые браузеры

Некоторые браузеры не используют общую файловую систему с процессом, который запускает SDK. Примеры — браузер в другом контейнере, браузер, к которому вы обращаетесь по URL DevTools, и браузер из сервиса размещённых браузеров. В этих случаях политика URL, перехват запросов и `confirm` по-прежнему выполняются в вашем процессе.

В случае размещённого браузера исходящий трафик и изоляцию хоста контролирует провайдер. Ваша собственная политика исходящего трафика там не применяется, поэтому хук запросов драйвера — ваша единственная проверка запросов браузера. Выясните, к чему может получить доступ сеть браузера.

Поставляемые проверки путей не защищают удалённый браузер. `BetaLocalFilePolicy` (typescript: `BetaNodeFilePolicy`) проверяет пути в файловой системе процесса, в котором работает SDK, а браузер читает и пишет в собственную файловую систему.

SDK не может определить, что браузер удалённый. Поэтому для удалённого браузера держите `file_upload` отключённым, если только ваш драйвер не проверяет пути загрузки там, где работает браузер, с помощью собственной `BetaFilePolicy`. `BetaFilePolicy` проверяет пути и ID документов каждой загрузки и решает, видит ли Claude путь скачанного файла.

Настройте удалённый браузер так, чтобы он отклонял скачивания, если только на его собственном хосте нет настройки скачиваний из раздела [Ограничение загрузок и скачиваний](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#confine-uploads-and-downloads).

Не допускайте попадания ключа API провайдера и URL подключения сеанса, который может содержать ключ, в журналы, результаты инструментов и текст ошибок. Если провайдер записывает сеансы, запись является ещё одной копией всего, что Claude видел и вводил, и к ней применяются условия хранения данных провайдера.

## Набор инструментов компьютера

`BetaAbstractComputerToolset20260801` — это класс для [инструмента использования компьютера](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/computer-use-tool). Драйвер — это подкласс с одним методом на каждый инструмент, например `screenshot` или `left_click`. Начните с драйвера партнёра из раздела [Интеграции партнёров](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#partner-integrations). Чтобы написать собственный, воспользуйтесь [минимальным примером VNC](https://github.com/anthropics/claude-quickstarts/tree/main/computer-toolset) на Python и TypeScript в репозитории claude-quickstarts. Он управляет рабочим столом через VNC и не является кодом для продакшена.

Класс принимает `configs`, `confirm` и `tool_configs` (typescript: `toolConfigs`), и вы передаёте их так же, как для класса браузера. Разделы [Настройка драйвера](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#customize-a-driver), [Обработка ошибок](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#handle-errors), [Запуск вызовов во время потоковой передачи ответа](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#start-calls-while-the-response-streams) и [Запуск без исполнителя инструментов](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#run-without-the-tool-runner) применяются и к нему, со следующими отличиями:

* **Параметры только для браузера:** У класса нет [политики URL](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy), [политики файлов](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#confine-uploads-and-downloads) и [отчёта о состоянии](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#report-browser-state).
* **`configs`:** Каждый реализованный вами инструмент включён по умолчанию. Настройки для каждого инструмента перечислены в разделе [Параметры инструмента](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/computer-use-tool#tool-parameters).
* **`confirm`:** Если ваш класс реализует `type`, `key` или `hold_key`, конструктор вызывает ошибку конфигурации, если вы не передадите вызываемый объект `confirm` или не отключите эти инструменты с помощью `configs`. Класс, переопределяющий `execute`, считается реализующим все три.
* **Пропущенные вызовы:** После неудавшегося вызова компьютера исполнитель инструментов отвечает на последующие вызовы компьютера в ходе текстом `Not executed: an earlier computer action in this turn failed.` Вызовы браузера получают другой текст. В цикле, который вы пишете сами, отвечайте на пропущенные вызовы компьютера именно этим текстом ([Пакетные действия](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/computer-use-tool#batch-actions)).
* **`execute`:** В TypeScript переопределение `execute` типизирует `name` как `BetaComputerMemberName`, а `input` — как `BetaComputerMemberInput`. Оба берутся из `@anthropic-ai/sdk/resources/beta`.
* **Текст ошибок:** SDK не скрывает локальные пути в тексте ошибки инструмента компьютера или в тексте, который возвращает инструмент. Claude читает и то, и другое, обрезанное до 4 096 символов. Перехватывайте исключения в своих инструментах и вызывайте `ToolError` (в TypeScript — через throw) с собственным текстом.

### Написание драйвера рабочего стола

Этот драйвер реализует пять из 17 инструментов, перечисленных в разделе [Доступные действия](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/computer-use-tool#available-actions). Инструмент, который вы не реализуете, отправляется в API как отключённый, как объясняется в разделе [Реализация драйвера](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver). В примере `display` обозначает вашу собственную обёртку над тем, что управляет рабочим столом, например клиентом VNC или `xdotool`, а `ask_user` (typescript: `askUser`) — вашу собственную функцию, которая спрашивает человека.

<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:
          # Координаты Claude — это позиции в пикселях на этом изображении.
          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 — это [x, y] или None для щелчка в позиции курсора. input.text —
          # удерживаемая клавиша-модификатор, например "shift". Возвращать нечего: Claude читает "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,
      # обязателен для type и key; см. «Безопасный запуск набора инструментов computer»
      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> {
      // Координаты Claude — это позиции пикселей на этом изображении.
      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 — это [x, y], либо null или undefined для клика в позиции курсора. input.text
      // — удерживаемая клавиша-модификатор, например "shift". Возвращать нечего: Claude читает "Clicked."
      await this.display.click(input.coordinate, input.text);
    }

    // Инструмент type в TypeScript записывается как type_.
    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, {
    // обязательно для type и key; см. Run the computer toolset safely
    confirm: (ctx) => askUser(`Allow the "${ctx.member}" tool?`)
  });
  try {
    const runner = client.beta.messages.toolRunner({
      model: "claude-opus-5-5",
      max_tokens: 1024,
      tools: [desktop],
      messages: [{ role: "user", content: "Open the calculator and compute 17 * 23." }]
    });
    for await (const message of runner) {
      console.log(message);
    }
  } finally {
    await desktop.close();
  }
  ```
</CodeGroup>

<Warning>
  `confirm` в этом примере спрашивает человека перед каждым вызовом, который выполняет драйвер. Прежде чем запускать драйвер на чём-либо, кроме одноразовой машины, прочитайте раздел [Безопасный запуск набора инструментов компьютера](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#run-the-computer-toolset-safely).
</Warning>

У четырёх инструментов нет полей входных данных: `screenshot`, `cursor_position`, `left_mouse_down` и `left_mouse_up`. Каждый тип входных данных, включая их, берётся из `anthropic.types.beta` (typescript: `@anthropic-ai/sdk/resources/beta`).

В TypeScript пишите инструменты как методы, а не как поля со стрелочными функциями. Причина объясняется в разделе [Реализация драйвера](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver).

### Масштабирование координат и размер изображений

Каждая точка, которую отправляет Claude, — это позиция в пикселях на скриншотах, которые вы возвращаете: каждая `coordinate` и `start_coordinate`, а также оба угла `region` в `zoom`. SDK передаёт их вашим инструментам без изменений.

Если размер ваших скриншотов отличается от размера дисплея, выполняйте преобразование в обоих направлениях в вашем драйвере: точки Claude — в пиксели дисплея, а позицию курсора, которую вы сообщаете, — в пиксели скриншота. Сохраняйте одинаковый размер скриншота на протяжении всего сеанса и задайте ему соотношение сторон дисплея:

<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):
          # Отклоняем точку. Если сдвинуть её в допустимый диапазон, клик придётся не туда, куда выбрал Claude.
          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) {
      // Отклоняем точку. Если сдвинуть её в допустимый диапазон, клик придётся не туда, куда выбрал Claude.
      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>

Вызывайте `to_display` (typescript: `toDisplay`) для каждой `coordinate` и `start_coordinate`, прежде чем выполнять действие, и `to_screenshot` (typescript: `toScreenshot`) для позиции, которую сообщает `cursor_position`. Щелчок или прокрутка без `coordinate` выполняется в позиции курсора, поэтому преобразовывать нечего. Масштабируйте `region` в `zoom` с тем же коэффициентом. Изображение `zoom` этого не меняет: следующие точки, которые отправляет Claude, по-прежнему являются позициями в пикселях на полном скриншоте.

SDK никогда не читает и не изменяет размер изображений. API отклоняет изображение, превышающее ограничения модели, что завершает выполнение. Масштабируйте каждый скриншот и изображение `zoom` в драйвере. См. [Подбор размера снимков экрана под ограничения изображений](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/computer-use-tool#handle-coordinate-scaling-for-higher-resolutions). При длительном выполнении один запрос может содержать более 20 изображений, и тогда API применяет к каждому из них более строгое ограничение. См. [Управление историей скриншотов](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/computer-use-tool#manage-screenshot-history).

### Проверка входных данных и результаты

Не полагайтесь на SDK в проверке входных данных вызова. Пусть ваш драйвер проверяет используемые им поля и отклоняет вызов, который он не может выполнить: вызовите исключение `ToolError` (в TypeScript — выбросьте его через throw). Claude прочитает сообщение и сможет отправить исправленный вызов.

Ни один из SDK не ограничивает `duration`, количество `repeat` или `scroll_amount`. Они рассматриваются в шаге 4 раздела [Безопасный запуск набора инструментов компьютера](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#run-the-computer-toolset-safely).

То, что возвращает инструмент, определяет, что прочитает Claude:

| Инструмент              | Возвращает                         | Claude читает                                                                                       |
| ----------------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------- |
| `screenshot`, `zoom`    | `BetaScreenshotResult`             | Один блок изображения                                                                               |
| `cursor_position`       | `BetaComputerCursorPositionResult` | `X={x},Y={y}`                                                                                       |
| Любой другой инструмент | Ничего или одну строку текста      | Краткое подтверждение, например `Clicked.`, а затем возвращённую строку в отдельном текстовом блоке |

### Безопасный запуск набора инструментов компьютера

То, что находится на экране, направляет следующее действие Claude. Страница или уведомление могут попытаться заставить Claude ввести текст не в то окно или запустить действие с реальными последствиями.

<Warning>
  Драйверу, реализующему `type`, `key` или `hold_key`, необходим вызываемый объект `confirm`. Без него конструктор выдаёт ошибку конфигурации, если только `configs` не отключает эти инструменты. Сам SDK ни у кого не спрашивает разрешения: он выполняет каждый вызов, который одобряет ваш `confirm`, а без `confirm` выполняет каждый вызов, включая каждый щелчок. Каждый реализованный вами инструмент компьютера включён по умолчанию.
</Warning>

Прежде чем запускать драйвер на чём-либо, кроме одноразовой машины, выполните следующие пять шагов:

1. Изолируйте рабочий стол. Запускайте его в выделенном контейнере или виртуальной машине с минимальными привилегиями для каждого сеанса, без учётных данных и без подключений хоста. Устанавливайте только приложения, необходимые для задачи. Код, вызывающий API, запускайте вне этой среды.
2. Ограничивайте действия с реальными последствиями с помощью `confirm`. Покупка или отправленное сообщение — это обычный `left_click`, `type` или `key`, а `confirm` получает инструмент и его входные данные, а не экран. Поэтому выбирайте, какие инструменты требуют одобрения, исходя из приложений, к которым Claude может получить доступ на рабочем столе.
3. Не держите на рабочем столе терминалы, диалоги запуска и средства запуска приложений, если они не нужны для задачи. Когда один из них находится в фокусе клавиатуры, `type`, `key` и `hold_key` выполняют всё, что вводит Claude, а щелчок может запустить программу. Если задача требует такого средства, спрашивайте человека перед вводом с клавиатуры и щелчками и изолируйте рабочий стол так, чтобы команда не могла выйти за пределы того, что нужно для задачи.
4. Отклоняйте входные данные, которые ваш рабочий стол не должен принимать, например точку за пределами скриншота, `wait` длительностью в несколько минут или очень большое значение `repeat` или `scroll_amount`. Вызывайте `ToolError` (в TypeScript — выбрасывайте через throw) вместо того, чтобы приводить значение к допустимому диапазону.
5. Считайте всё на экране недоверенным, включая заголовки окон и текст буфера обмена. Не выполняйте это и не передавайте дальше без проверки.

SDK применяет шаг 2 через передаваемый вами вызываемый объект. Остальные шаги зависят от вашего драйвера и вашего развёртывания. Меры предосторожности из раздела [Соображения безопасности](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/computer-use-tool#security-considerations) также применимы.

Следующий `confirm` спрашивает человека перед любым вводом с клавиатуры через вашу собственную функцию `ask_user` (typescript: `askUser`) и одобряет все остальные вызовы. `context.member` (typescript: `ctx.member`) — это имя инструмента. Чтобы человек одобрял и щелчки, задайте для `ASK_FIRST` значение `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
      # показана функция из раздела «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;
    }
    // показана функция из раздела «Gate consequential members».
    return askUser(`Allow the "${ctx.member}" tool with this input?\n${shown(ctx.input)}`);
  }

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

Отклонённый вызов получает результат с ошибкой, и выполнение продолжается. Для отклонённого вызова `type` Claude читает `The user did not grant permission to run 'type'. Do not retry it unless the user asks you to.`

Этот `confirm` одобряет каждый инструмент, которого нет в `ASK_FIRST`, включая любой инструмент, который вы добавите позже. Чтобы по умолчанию запрещать (fail closed), одобряйте только инструменты, не влияющие на рабочий стол (`screenshot`, `zoom`, `cursor_position` и `wait`), и спрашивайте обо всех остальных инструментах.

### Использование обоих наборов инструментов в одном запросе

Передайте оба экземпляра в `tools`. Каждый вызов содержит свой `toolset_name` — `browser` или `computer`, и исполнитель инструментов отправляет вызов соответствующему экземпляру. После неудачного вызова исполнитель инструментов пропускает только последующие вызовы того же набора инструментов в этом ходе, поэтому неудачный вызов компьютера не приводит к пропуску вызовов браузера в этом ходе.

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  # MyBrowser, backend и url_policy взяты из продвинутого примера.
  # confirm — это функция из раздела «Run the computer toolset safely».
  with (
      MyBrowser(backend, url_policy=url_policy) as browser,
      MyDesktop(display, confirm=confirm) as desktop,
  ):
      runner = client.beta.messages.tool_runner(
          model="claude-opus-5-5",
          max_tokens=1024,
          tools=[browser, desktop],
          messages=[
              {
                  "role": "user",
                  "content": "Copy the heading on example.com into the open text editor.",
              }
          ],
          stream=True,
          run_tools_eagerly=True,  # so a call can start before the response ends
      )
      for stream in runner:
          print(stream.get_final_message())
  ```

  ```typescript TypeScript
  // MyBrowser, backend и urlPolicy взяты из продвинутого примера.
  // confirm — это функция из раздела «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>

В собственном цикле отвечайте на каждый вызов с помощью `tool_result` (typescript: `toolResult`) на экземпляре, чей `toolset_name` (typescript: `toolsetName`) совпадает с `toolset_name` вызова. В каждом ходе отслеживайте неудачные вызовы для каждого набора инструментов отдельно и отвечайте на пропущенный вызов собственным текстом этого набора инструментов. Текст для браузера приведён в разделе [Запуск без исполнителя инструментов](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#run-without-the-tool-runner), а текст для компьютера — в пункте о пропущенных вызовах в начале этого раздела.

## Справочник

Параметры конструктора имеют одинаковое значение в обоих SDK:

| Python                 | TypeScript     | Задаёт                                                                 |
| ---------------------- | -------------- | ---------------------------------------------------------------------- |
| `configs`              | `configs`      | Какие члены включены                                                   |
| `confirm`              | `confirm`      | Вызываемый объект, который одобряет или отклоняет каждый вызов         |
| `url_policy`           | `urlPolicy`    | Вашу политику URL, которую SDK вызывает перед каждым `navigate` на URL |
| `file_policy`          | `filePolicy`   | Корневые каталоги для загрузки файлов и раскрытие пути скачивания      |
| `tool_configs`         | `toolConfigs`  | Поля для записи `tools`, например `cache_control`                      |
| Метод `_browser_state` | `browserState` | Отчёт о состоянии                                                      |

Класс браузера принимает все параметры из таблицы. Класс компьютера принимает `configs`, `confirm` и `tool_configs` (typescript: `toolConfigs`).

Изменить параметр после создания экземпляра нельзя. Каждый параметр описан в [руководстве по набору инструментов браузера для Python](https://github.com/anthropics/anthropic-sdk-python/blob/main/browser-toolset.md), [руководстве по набору инструментов браузера для TypeScript](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/browser-toolset.md), [руководстве по набору инструментов компьютера для Python](https://github.com/anthropics/anthropic-sdk-python/blob/main/computer-toolset.md) и [руководстве по набору инструментов компьютера для TypeScript](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/computer-toolset.md). Руководства для Python также охватывают асинхронные классы `BetaAsyncAbstractBrowserToolset20260801` и `BetaAsyncAbstractComputerToolset20260801`.

## Ограничения

* **SDK не включает браузер, рабочий стол, готовый драйвер или политику URL:** Их предоставляете вы. См. разделы [Интеграции партнёров](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#partner-integrations), [Реализация драйвера](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver) и [Написание политики URL](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy).
* **Политика URL видит только вызовы `navigate`:** Она не видит запросы, которые делает страница, или страницы, открывающиеся любым другим способом, например по ссылке, на которую щёлкает Claude, или через перенаправление. См. [Перехват запросов в драйвере](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#intercept-requests-in-the-driver).
* **Одобрение основано на последнем отчёте о состоянии:** Страница может измениться после этого отчёта. SDK не проверяет страницу повторно перед выполнением вызова.
* **Вызовы в одном наборе инструментов выполняются по одному:** Это нельзя отключить.
* **SDK не проверяет уникальность идентификаторов вкладок и их количество, а также не всегда проверяет, что активна ровно одна вкладка:** API отклоняет отчёт, нарушающий эти правила.
* **Для вызова компьютера `confirm` получает инструмент и его входные данные, а не экран:** По ним нельзя определить, что делает щелчок в заданной точке.
* **SDK не изменяет размер изображений набора инструментов компьютера:** API отклоняет изображение, превышающее ограничения модели. См. [Масштабирование координат и размер изображений](https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-sdk#scale-coordinates-and-size-images).

## Следующие шаги

<CardGroup cols={2}>
  <Card title="Инструмент использования браузера" icon="browser" href="https://platform.claude.com/docs/ru/agents-and-tools/tool-use/browser-use-tool">
    Инструменты-члены, блок `browser_state` и соображения безопасности для этого инструмента.
  </Card>

  <Card title="Инструмент использования компьютера" icon="computer" href="https://platform.claude.com/docs/ru/agents-and-tools/tool-use/computer-use-tool">
    17 инструментов, координаты и ограничения на изображения, а также соображения безопасности для этого инструмента.
  </Card>

  <Card title="Исполнитель инструментов (SDK)" icon="arrows-clockwise" href="https://platform.claude.com/docs/ru/agents-and-tools/tool-use/tool-runner">
    Как SDK выполняет цикл и как изменить отправляемые им сообщения.
  </Card>

  <Card title="Противодействие джейлбрейкам и инъекциям подсказок" icon="shield" href="https://platform.claude.com/docs/ru/test-and-evaluate/strengthen-guardrails/mitigate-jailbreaks">
    Защитные механизмы для любого приложения, которое читает недоверенный контент.
  </Card>
</CardGroup>
