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

# Working with tools

> Give your flows access to tools that call APIs, run code, search the web, generate images, and connect to external services

Tools let your flows call APIs, run code, search the web, generate images, and connect to external services.

## Tool types

The following table lists the tool types that this guide covers. For every tool type and where you create each one, see [What are Tools?](/user-guide/tools/what-are-tools).

| Type         | Description                                      | Use case                                |
| ------------ | ------------------------------------------------ | --------------------------------------- |
| **External** | Send HTTP requests.                              | Integrate with a REST API.              |
| **Custom**   | Run your code in an isolated environment.        | Transform data or perform calculations. |
| **Flow**     | Call another flow as a tool.                     | Reuse a multi-step workflow.            |
| **Built-in** | Use platform and provider tools.                 | Generate images or search the web.      |
| **MCP**      | Connect to Model Context Protocol (MCP) servers. | Use tools from external services.       |

You can save an external, custom, or flow tool in your account and reference it by ID, or define it inline for one request as a runtime tool. To save a tool, use the dashboard or send the definition to `POST /v1/tools`. Built-in tools and MCP tools use their own IDs.

## Add tools to a flow

Configure tools in the `tools` object of a prompt step. The following examples attach a saved tool to a prompt step.

Use the TypeScript SDK to attach a saved tool:

**`TypeScript SDK`**

```typescript title="TypeScript SDK"
import { FlowBuilder, RuntypeClient } from '@runtypelabs/sdk'

const client = new RuntypeClient({
  apiKey: process.env.RUNTYPE_API_KEY,
})

const result = await new FlowBuilder()
  .createFlow({ name: 'Agent with tools' })
  .prompt({
    name: 'Agent',
    model: 'claude-sonnet-5',
    userPrompt: 'Help the user with their request',
    tools: {
      toolIds: ['YOUR_TOOL_ID'],
      maxToolCalls: 10,
      toolCallStrategy: 'auto',
    },
  })
  .run(client, { streamResponse: true })
```

Use the Python SDK to attach the same saved tool:

**`Python SDK`**

```python title="Python SDK"
import os

from runtype import RuntypeClient

client = RuntypeClient(api_key=os.environ["RUNTYPE_API_KEY"])

for event in client.dispatch({
    "flow": {
        "name": "Agent with tools",
        "steps": [{
            "type": "prompt",
            "config": {
                "model": "claude-sonnet-5",
                "userPrompt": "Help the user with their request",
                "tools": {
                    "toolIds": ["YOUR_TOOL_ID"],
                    "maxToolCalls": 10,
                    "toolCallStrategy": "auto"
                }
            }
        }]
    }
}):
    print(event)
```

Send the same flow definition with cURL:

**`cURL`**

```bash title="cURL"
curl https://api.runtype.com/v1/dispatch \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "flow": {
      "name": "Agent with tools",
      "steps": [{
        "type": "prompt",
        "config": {
          "model": "claude-sonnet-5",
          "userPrompt": "Help the user with their request",
          "tools": {
            "toolIds": ["YOUR_TOOL_ID"],
            "maxToolCalls": 10,
            "toolCallStrategy": "auto"
          }
        }
      }]
    }
  }'
```

Replace the placeholders in these examples with your values:

* `YOUR_TOOL_ID`: The ID of the saved tool that you want to use.
* `YOUR_API_KEY`: Your Runtype API key.
* `RUNTYPE_API_KEY`: The environment variable that contains your Runtype API key.

The `toolIds` array accepts the following ID forms:

* A saved tool ID.
* `builtin:TOOL_ID` for a built-in tool, such as `builtin:gpt-image-2-5`.
* `platform:orthogonal:VENDOR:OPERATION` for a platform catalog tool.
* `mcp:SERVER_NAME:TOOL_NAME` for a tool on a saved MCP server.

To confirm that the model called a tool, watch the streamed events. Each tool call emits a `tool_start` event when it begins and a `tool_complete` event that carries the result.

## Configure tool call strategy

Set `toolCallStrategy` to control how the model uses the available tools. The following table describes each strategy:

| Strategy   | Behavior                                  |
| ---------- | ----------------------------------------- |
| `auto`     | The model chooses whether to call a tool. |
| `required` | The model must call an available tool.    |
| `none`     | The model cannot call tools.              |

`maxToolCalls` limits the number of tool-call rounds in the step. In each round, the model can call one or more tools. When you set `maxToolCalls`, it also limits the total number of tool calls in the step, and each call past the limit returns a limit error to the model. If you don't set it, the step allows 10 rounds.

Use `required` only for one forced tool call, such as a structured-output or routing tool, and set `maxToolCalls` to `1`. With any other `maxToolCalls` value, including an unset one, `required` forces a tool call on every round. The model can't finish with a text answer, so the step output is empty. An agent whose `loopConfig.maxTurns` is greater than `1` has the same problem. For multi-step prompts and agents, set `toolCallStrategy` to `auto`.

To run a specific tool with parameters that you choose on every run, use a [Tool Call step](/user-guide/flows/flow-step-types-overview#tool-call) instead of a prompt step. The model doesn't select the tool or its parameters.

## Use external tools

External tools send HTTP requests to an API. Define the tool's parameters with JSON Schema and configure the request. Save the definition as a tool and add its ID to `toolIds`, or pass it in `tools.runtimeTools`:

**`External tool`**

```typescript title="External tool"
const externalTool = {
  name: 'get_weather',
  description: 'Get the weather for a city',
  toolType: 'external',
  parametersSchema: {
    type: 'object',
    properties: {
      city: { type: 'string', description: 'City name' },
    },
    required: ['city'],
  },
  config: {
    url: 'https://api.example.com/weather?city={{city}}',
    method: 'GET',
    headers: {
      Authorization: 'Bearer {{secret:WEATHER_API_KEY}}',
    },
  },
}
```

Use `{{city}}` in the URL or headers to insert the matching tool-call parameter. Use `{{secret:WEATHER_API_KEY}}` to resolve the managed secret named `WEATHER_API_KEY`. For the dashboard editor and response handling, see [Creating external tools](/user-guide/tools/creating-external-tools).

## Use custom tools

Custom tools run your code in an isolated environment. Save the definition as a tool and add its ID to `toolIds`, or pass it in `tools.runtimeTools`. The following tool calculates a discount and returns the original price, discounted price, and savings:

**`Custom tool`**

```typescript title="Custom tool"
const customTool = {
  name: 'calculate_discount',
  description: 'Calculate a discounted price',
  toolType: 'custom',
  parametersSchema: {
    type: 'object',
    properties: {
      price: { type: 'number' },
      discount: { type: 'number' },
    },
    required: ['price', 'discount'],
  },
  config: {
    code: `
      const { price, discount } = parameters;
      const discounted = price * (1 - discount / 100);
      return {
        original: price,
        discounted: discounted.toFixed(2),
        savings: (price - discounted).toFixed(2),
      };
    `,
    timeout: 5000,
    allowedApis: [],
  },
}
```

The code reads the model's tool-call arguments from the `parameters` object and returns the tool result. The `timeout` value is in milliseconds, and a runtime custom tool accepts at most 30,000. In the tool editor, the default **Execution Environment** is **Cloudflare Worker (Default)**. For other execution environments and languages, see [Creating custom tools](/user-guide/tools/creating-custom-tools).

## Use flow tools

A flow tool runs another flow and returns its output to the model. The following tool passes the model's `text` argument to the flow as the `input_text` variable:

**`Flow tool`**

```typescript title="Flow tool"
const flowTool = {
  name: 'analyze_sentiment',
  description: 'Run sentiment analysis on text',
  toolType: 'flow',
  parametersSchema: {
    type: 'object',
    properties: {
      text: { type: 'string' },
    },
    required: ['text'],
  },
  config: {
    flowId: 'YOUR_FLOW_ID',
    parameterMapping: { text: 'input_text' },
    outputMapping: 'sentiment',
  },
}
```

Replace `YOUR_FLOW_ID` with the ID of the flow that you want to run. For how `outputMapping` selects the returned value, see [Flow tools](/developer-guides/guides/runtime-tools#flow-tools).

## Use built-in tools

Built-in tools provide platform and provider capabilities. Add their IDs to `toolIds`:

**`Built-in tools`**

```typescript title="Built-in tools"
tools: {
  toolIds: [
    'builtin:gpt-image-2-5',
    'builtin:openai_web_search',
  ],
}
```

Platform tools, such as `builtin:gpt-image-2-5`, work with any model. Provider tools, such as `builtin:openai_web_search`, work only with compatible models from that provider. In the dashboard, a prompt step lists only the built-in tools that its model supports.

Some built-in tools need settings. Put the settings in `toolConfigs`, keyed by tool ID:

**`Built-in tool settings`**

```typescript title="Built-in tool settings"
tools: {
  toolIds: ['builtin:decision_judge#triage'],
  toolConfigs: {
    'builtin:decision_judge#triage': {
      questionSetId: 'qset_YOUR_QUESTION_SET_ID',
    },
  },
}
```

Replace `qset_YOUR_QUESTION_SET_ID` with the ID of your question set. For each tool's ID, compatible models, and settings, see [Built-in tools](/user-guide/tools/built-in-tools).

## Use MCP server tools

Model Context Protocol (MCP) servers expose tools through a standard protocol. Configure an MCP server as a saved server or as a runtime server.

### Use saved MCP servers

Save an MCP server in your account, then reference its tools by ID:

**`Saved MCP server tools`**

```typescript title="Saved MCP server tools"
tools: {
  toolIds: [
    'mcp:YOUR_SERVER_NAME:YOUR_TOOL_NAME',
  ],
}
```

The tool ID format is `mcp:SERVER_NAME:TOOL_NAME`. Replace `YOUR_SERVER_NAME` with the saved server name and `YOUR_TOOL_NAME` with the MCP tool name.

### Use runtime MCP servers

Pass an MCP server configuration inline when the server or its credentials vary by request. The following example sends the managed secret `MCP_TOKEN` as a bearer token:

**`Runtime MCP server`**

```typescript title="Runtime MCP server"
tools: {
  mcpServers: [
    {
      id: 'YOUR_MCP_SERVER_ID',
      url: 'https://example.com/mcp',
      auth: {
        type: 'headers',
        headers: {
          Authorization: 'Bearer {{secret:MCP_TOKEN}}',
        },
      },
      allowedTools: ['search_docs'],
      timeout: 30000,
    },
  ],
}
```

Replace `YOUR_MCP_SERVER_ID` with an ID for this server, and replace `MCP_TOKEN` with the name of the managed secret that holds the server's token.

Runtype discovers the server's tools when the request runs and gives them to the model. To expose only some of them, list their names in `allowedTools`. Each tool gets the ID `mcp:custom_YOUR_MCP_SERVER_ID:TOOL_NAME`. To require approval for these tools, list a tool name, such as `search_docs`, or use the `mcp:*` pattern for every MCP tool.

The server configuration also accepts these settings:

* `auth.type`: `none`, `bearer`, `api_key`, `basic`, `custom_header`, or `headers`. A runtime server can't use `oauth2`. To connect an OAuth server, save it as an MCP server and reference its tools by ID.
* `timeout`: The request timeout in milliseconds. The default is 30,000.

For each authentication type, see [MCP servers](/developer-guides/integrations/mcp-servers).

## Use runtime tools

Define a tool inline without saving it to your account. Runtime tools use the same fields as saved tools. The following example sends a request to an external API:

**`Runtime tool`**

```typescript title="Runtime tool"
const runtimeTool = {
  name: 'fetch_user',
  description: 'Fetch a user by ID',
  toolType: 'external',
  parametersSchema: {
    type: 'object',
    properties: {
      userId: { type: 'string' },
    },
    required: ['userId'],
  },
  config: {
    url: 'https://api.example.com/users/{{userId}}',
    method: 'GET',
  },
}

const tools = {
  runtimeTools: [runtimeTool],
}
```

Runtime tools fit these use cases:

* Dynamic tool definitions.
* Testing a tool before saving it.
* User-provided tool configurations.

## Combine tool sources

Add saved tools, built-in tools, runtime tools, and runtime MCP servers to one prompt. The following example combines these tool sources:

**`Combined tool configuration`**

```typescript title="Combined tool configuration"
tools: {
  toolIds: [
    'YOUR_TOOL_ID',
    'builtin:gpt-image-2-5',
    'mcp:YOUR_SERVER_NAME:YOUR_TOOL_NAME',
  ],
  runtimeTools: [
    {
      name: 'fetch_user',
      description: 'Fetch a user by ID',
      toolType: 'external',
      parametersSchema: {
        type: 'object',
        properties: {
          userId: { type: 'string' },
        },
        required: ['userId'],
      },
      config: {
        url: 'https://api.example.com/users/{{userId}}',
        method: 'GET',
      },
    },
  ],
  mcpServers: [
    {
      id: 'YOUR_MCP_SERVER_ID',
      url: 'https://example.com/mcp',
      auth: {
        type: 'headers',
        headers: {
          Authorization: 'Bearer {{secret:MCP_TOKEN}}',
        },
      },
      timeout: 30000,
    },
  ],
  maxToolCalls: 15,
  toolCallStrategy: 'auto',
}
```

Replace `YOUR_TOOL_ID`, `YOUR_SERVER_NAME`, `YOUR_TOOL_NAME`, and `YOUR_MCP_SERVER_ID` with your values. Replace `MCP_TOKEN` with the name of the managed secret that holds the runtime MCP server token.

## Require approval before a tool runs

To pause the run until a person approves a tool call, set `tools.approval` on the prompt step:

**`Tool approval`**

```typescript title="Tool approval"
tools: {
  toolIds: ['YOUR_TOOL_ID', 'mcp:YOUR_SERVER_NAME:YOUR_TOOL_NAME'],
  approval: {
    require: ['send_email', 'mcp:*'],
    timeout: 300000,
  },
}
```

The `approval` object accepts the following fields:

* `require`: The tools that need approval. Set it to `true` for all tools, or to an array of tool names and patterns, such as `send_email`, `mcp:*`, or `builtin:*`.
* `timeout`: How long the run waits for a decision, in milliseconds. The default is 300,000 (5 minutes).
* `requestReason`: Whether the model gives a reason for each gated call. The default is `true`.
* `choices`: Persistent choices in the approval prompt. Set `alwaysAllow` or `alwaysDeny` to `true` to offer them. Both are off by default.

A gated call pauses the run and emits an `approval_start` event. Someone must answer the prompt, for example through an interactive dispatch request or a Surface with approval controls. Unattended runs, such as schedules, batches, evals, and webhooks, have nobody to answer, so they fail with an error that names the approval policy, and no gated tool runs. Client Chat rejects an approval-gated request before it starts. To approve or deny a paused call, see [Pause and resume on an agent dispatch](/developer-guides/guides/runtime-tools#pause-and-resume-on-an-agent-dispatch).

## Pass secrets

Keep API keys out of tool configuration. Store each credential on the **Secrets** page in **Settings**, then reference its managed name in the tool configuration:

**`Managed secret reference`**

```typescript title="Managed secret reference"
headers: {
  Authorization: 'Bearer {{secret:WEATHER_API_KEY}}',
}
```

Replace `WEATHER_API_KEY` with the name of the managed secret in your account. Use `{{secret:NAME}}`, where `NAME` is the managed secret name, for every flow credential. Don't pass credentials in the top-level `secrets` map of a dispatch request: a non-empty map returns a `400` error. Managed secret values are encrypted at rest and are not logged or returned in responses. For the fields that accept references, see [Managing secrets](/user-guide/settings/managing-secrets).

## Apply best practices

Use these practices when you configure tools:

#### Limit tool calls

Set `maxToolCalls` to limit tool-call rounds and tool calls. If you don't set it, the step allows 10 rounds. Use a lower value for short tasks and a higher value for tasks that need more steps.

#### Write clear descriptions

Describe the tool's inputs, outputs, and intended use. Clear descriptions provide the model with
information for tool selection and calls.

#### Use the auto strategy

Set `toolCallStrategy` to `auto` for multi-step prompts and agents. For when to use `required`,
see [Configure tool call strategy](#configure-tool-call-strategy).

#### Handle errors

When a tool call fails, the model receives the error message and an error type as the tool result. Tell the model in the prompt how to respond, for example by retrying with corrected arguments or by reporting the failure. For the errors that an external tool reports, see [Handle errors](/user-guide/tools/creating-external-tools#handle-errors).

## Next steps

Use these guides to continue working with tools:

#### [Built-in tools](/user-guide/tools/built-in-tools)

Find built-in tool IDs, settings, and compatible models.

#### [MCP servers](/developer-guides/integrations/mcp-servers)

Connect an MCP server to your tools.

#### [Runtime tools](/developer-guides/guides/runtime-tools)

Define tools inline for a request.

#### [Manage tools as code](/user-guide/tools/manage-tools-as-code)

Keep saved tool definitions in source control.

#### [Tools API](/api-reference/runtype-api/tools/list-tools)

Manage saved tools through the API.

#### [Creating and editing flows](/user-guide/flows/creating-and-editing-flows)

Build and run a flow that uses tools.