> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://platform.bctrl.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://platform.bctrl.ai/_mcp/server.

# Tools

> Give applications and Agents typed capabilities to perform work.

A **Tool** is one callable capability. Tools have input and output schemas, so
an application can call the same capability directly or make it available to
an Agent through a Toolset.

## How Tools fit together

| Concept          | Role                                                             |
| ---------------- | ---------------------------------------------------------------- |
| **Tool**         | One capability, such as opening a page or requesting human input |
| **Toolset**      | A reusable list of Tools                                         |
| **Conversation** | An Agent thread that can use a Toolset                           |
| **Tool call**    | One execution of a Tool                                          |

The relationship is:

```text
Tool → Toolset → Conversation turn → Tool call → Run
```

Every Tool call can be connected to its `runtimeId`, `runId`, `turnId`, and
`spanId`. Use [Runs](/sdk/runs) when you need the complete execution record.

## Call a built-in Tool

[Built-in Tools](/sdk/tools/catalog) are ready-to-use capabilities for browser
pages, Stagehand, challenge solving, human input, files, and Vault.

Call a synchronous Tool directly:

```ts
const pages = await bctrl.tools.call("browser.pages.list", {
}, { runtimeId });

console.log(pages);
```

The SDK uses the Tool's generated input and output types. Runtime-bound Tools
receive their Runtime selector as the third call-options argument; it is not
part of the model-generated Tool input.

For work that may take time or wait for input, start an asynchronous Tool call:

```ts
const call = await bctrl.tools.start("human.request", {
  prompt: "Approve the purchase",
  expiresInSeconds: 300,
}, { runtimeId });

const result = await bctrl.toolCalls.result(call.id, {
  waitSeconds: 60,
});
```

When the call reaches a terminal state before `waitSeconds` expires, the
endpoint returns the tool output. If the call is still pending when the wait
ends, it returns the current `ToolCall` with HTTP `202`; call it again to keep
polling or use `bctrl.toolCalls.get()` to inspect the lifecycle state.

Use `tools.call` for a Tool that can finish within its synchronous limit. Use
`tools.start` when the Tool supports asynchronous execution, may require human
input, or should be monitored as a separate resource.

## Toolsets

A Toolset is a named collection of Tool references. Tool references can be
built-in names or custom Tool IDs. The Space can be omitted to use the
caller’s default Space:

```ts
const toolset = await bctrl.toolsets.create({
  name: "checkout-tools",
  tools: [
    "browser.pages.open",
    "stagehand.observe",
    "human.request",
  ],
});

const conversation = await bctrl.conversations.create({
  runtimeId,
  toolsetId: toolset.id,
});
```

The Agent can use only the Tools included in the Toolset during its turns.

### Toolset fields

| Parameter     | Type             | Required | Description                                                          |
| ------------- | ---------------- | -------- | -------------------------------------------------------------------- |
| `name`        | `string`         | Yes      | Human-readable Toolset name.                                         |
| `spaceId`     | `string`         | No       | Space that owns the Toolset. Defaults to the caller’s default Space. |
| `description` | `string \| null` | No       | What the Toolset is for.                                             |
| `tools`       | `string[]`       | No       | Built-in Tool names or custom Tool IDs. Defaults to an empty list.   |

Manage a Toolset with `bctrl.toolsets.list()`, `iter()`, `get()`, `update()`,
and `delete()`.

## Custom Tools

Create a custom Tool when the capability belongs to your application. The SDK
currently supports code, webhook, and workflow implementations:

```ts
const tool = await bctrl.tools.create({
  name: "crm.lookup",
  description: "Look up a customer in the CRM",
  inputSchema: {
    type: "object",
    properties: { customerId: { type: "string" } },
    required: ["customerId"],
  },
  outputSchema: {
    type: "object",
    properties: { status: { type: "string" } },
  },
  implementation: {
    type: "webhook",
    url: "https://api.example.com/tools/crm-lookup",
    authSecretName: "crm-webhook-secret",
    timeoutMs: 30000,
  },
});
```

For a webhook Tool, use `authSecretName` to reference a stored secret and
`timeoutMs` to set the execution timeout. Code Tools place their source and
timeout inside `implementation: { type: "code", ... }`. Workflow Tools are
created from a published source turn.

### Custom Tool fields

| Parameter        | Type                                          | Required | Description                                                                                                          |
| ---------------- | --------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
| `name`           | `string`                                      | Yes      | Stable namespaced name, such as `crm.lookup`.                                                                        |
| `description`    | `string`                                      | No       | What the Tool does.                                                                                                  |
| `runtimeTypes`   | `("browser" \| "desktop" \| "spreadsheet")[]` | No       | Runtime categories supported by the immutable current revision.                                                      |
| `modes`          | `("sync" \| "async")[]`                       | No       | Execution modes supported by the immutable current revision.                                                         |
| `inputSchema`    | `JsonObject`                                  | Yes      | JSON Schema for the input.                                                                                           |
| `outputSchema`   | `JsonObject`                                  | Yes      | JSON Schema for the output.                                                                                          |
| `implementation` | `object`                                      | Yes      | Immutable implementation revision: `code`, `webhook`, or `workflow`. Implementation-specific fields are nested here. |
| `spaceId`        | `string`                                      | No       | Space that owns the Tool. Defaults to the caller’s default Space.                                                    |

Tool responses include `currentRevisionId`, `runtimeTypes`, `modes`, schemas,
and the current implementation. A Tool's machine `name` is immutable. Updates
must include the current revision ID and create a new revision; stale revision
IDs are rejected so a ToolCall cannot silently observe a changed definition.

Use `bctrl.tools.list()`, `iter()`, `get()`, `update()`, and `delete()` to
manage custom Tools. Built-in Tools are read-only catalog definitions with
`spaceId: null`, `currentRevisionId: null`, and
`implementation: { type: "builtin", name }`.

## Tool calls

Every asynchronous call has a ToolCall resource. It records the execution
state and links the call to the automation that produced it.

```ts
const current = await bctrl.toolCalls.get(call.id);

if (current.status === "requires_input") {
  await bctrl.toolCalls.respond(call.id, { approved: true });
}

if (current.status === "running") {
  console.log("Still running");
}

await bctrl.toolCalls.cancel(call.id);
```

### ToolCall status

| Status           | Meaning                       |
| ---------------- | ----------------------------- |
| `queued`         | Accepted and waiting to start |
| `running`        | Executing                     |
| `requires_input` | Waiting for a response        |
| `succeeded`      | Completed successfully        |
| `failed`         | Completed with an error       |
| `cancelled`      | Cancelled before completion   |
| `timed_out`      | Exceeded its time limit       |

Calls that require input can be answered with `toolCalls.respond()`. A
completed, failed, cancelled, or timed-out call cannot be resumed.

### ToolCall fields

| Field               | Type                                     | Always present | Description                                                                  |
| ------------------- | ---------------------------------------- | -------------- | ---------------------------------------------------------------------------- |
| `id`                | `string`                                 | Yes            | Unique ToolCall identifier.                                                  |
| `tool`              | `string`                                 | Yes            | Built-in Tool name or custom Tool name/ID.                                   |
| `status`            | `ToolCallStatus`                         | Yes            | Current execution state.                                                     |
| `runtimeId`         | `string \| null`                         | Yes            | Runtime used by the call, or `null` when no Runtime was involved.            |
| `runId`             | `string \| null`                         | Yes            | Run that contains the call, or `null` when no Run was created.               |
| `turnId`            | `string \| null`                         | Yes            | Agent turn that contains the call, or `null` for non-Agent calls.            |
| `parentId`          | `string \| null`                         | Yes            | Parent ToolCall, or `null` when the call is not nested.                      |
| `spanId`            | `string \| null`                         | Yes            | Trace span for the call, or `null` when no span is available.                |
| `callerType`        | `"api" \| "agent" \| "system" \| "test"` | Yes            | Who started the call.                                                        |
| `resultAvailable`   | `boolean`                                | Yes            | Whether a result can be read.                                                |
| `prompt`            | `string \| null`                         | Yes            | Input prompt when the call is waiting for a response, or `null` otherwise.   |
| `responseSchema`    | `JsonObject \| null`                     | Yes            | Expected response shape, or `null` when the call does not expect a response. |
| `responseExpiresAt` | `string \| null`                         | Yes            | Input response deadline, or `null` when the call is not waiting for input.   |
| `createdAt`         | `string`                                 | Yes            | Time the ToolCall was created.                                               |
| `startedAt`         | `string \| null`                         | Yes            | Time execution started, or `null` before execution begins.                   |
| `finishedAt`        | `string \| null`                         | Yes            | Time execution finished, or `null` while the call is still active.           |
| `errorCode`         | `string \| null`                         | Yes            | Error code when the call fails, or `null` otherwise.                         |
| `errorMessage`      | `string \| null`                         | Yes            | Error message when the call fails, or `null` otherwise.                      |
| `retryable`         | `boolean \| null`                        | Yes            | Whether the failure can be retried, or `null` when the call has not failed.  |

Use `bctrl.toolCalls.list()` or `iter()` to filter calls by `runtimeId`,
`runId`, `turnId`, `tool`, or `status`.

## Tool execution metadata

Tool definitions expose descriptive execution metadata so callers can choose a
safe execution path. Policy fields such as cancellation support, synchronous
limits, and result persistence remain server-owned and are not part of the
public Tool resource.

| Field          | Values                              | Meaning                                  |
| -------------- | ----------------------------------- | ---------------------------------------- |
| `runtimeTypes` | `browser \| desktop \| spreadsheet` | Runtime categories supported by the Tool |
| `modes`        | `sync \| async`                     | Ways the Tool can be called              |

Use the metadata returned by `bctrl.tools.get()` to decide whether to call a
Tool synchronously or start a ToolCall.