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

# Designing tools for agents

An Agent chooses and calls a Tool using only three things: the Tool name, the description, and the parameter schema. A Tool that returns correct data still fails in production when the model can't tell when to call it, what to pass, or what to do with the result. This page shows how to pick a Tool type, write the interface, shape results and errors, set timeouts, and test the Tool the way an Agent uses it.

## Pick a tool type

Choose the type by where the work runs and how long it takes. On the **Tools** page, click **Create Tool** and select one of the following types in the **Type** list:

| Type in the dashboard | `toolType` | Use it when                                                                           |
| --------------------- | ---------- | ------------------------------------------------------------------------------------- |
| **Flow Tool**         | `flow`     | The Agent always runs the same sequence of steps, or an API response needs reshaping. |
| **Custom Code**       | `custom`   | The work is a calculation, validation, or transformation of the inputs.               |
| **External API**      | `external` | The work is one HTTP request, with credentials from managed secrets.                  |
| **Subagent**          | `subagent` | A focused task needs its own context window and its own subset of Tools.              |
| **MCP Server**        | `mcp`      | An MCP server already exposes the Tools that you need.                                |

Custom code has no network access unless the Tool sets `networkAccess`, so use an External API Tool or a Flow Tool for HTTP calls, or turn on network access for the one host the code calls. For the setup of each type, see [What are Tools?](/user-guide/tools/what-are-tools).

### Saved subagent tools

A saved Subagent Tool runs one of your saved Agents as a child. The parent receives only the child's final result. To configure one, open the Tool and set the following fields in **Tool Configuration**:

* **Agent**: the saved Agent that runs when the Tool is called.
* **Allowed Tools**: the Tools, from the child Agent's own Tools, that the child can use. When you select an Agent, this list starts empty, and an empty list runs the child without Tools.
* **Output Format**: **Text** returns the final assistant message, **JSON** returns the parsed JSON payload, and **Last message** returns the raw last assistant message.
* **Max Turns**: the child's turn limit. Leave it blank to keep the child Agent's own limit.
* **Timeout (ms)**: the time limit for the child. The default is 300,000 milliseconds.
* **Task Template (Optional)**: a message template, such as `Research {{topic}} and return a summary.` Leave it empty to pass the `task` parameter through unchanged.
* **Inherit parent messages**: turn this on to give the child the parent's current conversation. It's off by default, so write the `task` parameter as a self-contained instruction.

To run the child in the background instead, set `config.execution.mode` to `detached` with the API. For the detached fields, see [Run a subagent in the background](/user-guide/tools/runtime-tools#run-a-subagent-in-the-background).

### GraphQL APIs

The API accepts `graphql` as a `toolType`, and the **Tools** page labels such a Tool **GraphQL**. Runtype has no runner for this type, so every call to a `graphql` Tool fails with an unsupported tool type error.

To call a GraphQL API, create an External API Tool with the `POST` method and send the query in the body template. The following definition looks up an order by ID:

**`lookup-order.json`**

```json title="lookup-order.json"
{
  "name": "lookup_order",
  "description": "Retrieves one order by order ID. Returns status and total. Read-only and safe to retry.",
  "toolType": "external",
  "parametersSchema": {
    "type": "object",
    "properties": {
      "order_id": { "type": "string", "description": "Order ID, for example ord_1042." }
    },
    "required": ["order_id"]
  },
  "config": {
    "url": "https://example.com/graphql",
    "method": "POST",
    "headers": {
      "Content-Type": "application/json",
      "Authorization": "Bearer {{secret:SHOP_API_KEY}}"
    },
    "body": "{\"query\": \"query Order($id: ID!) { order(id: $id) { status total } }\", \"variables\": {\"id\": {{order_id}} } }"
  }
}
```

In this definition, `SHOP_API_KEY` is the name of a managed secret in your account. Leave `{{order_id}}` unquoted, because Runtype encodes each value as JSON before it inserts it. To create the Tool, run the following command:

```bash
runtype tools create --from-file lookup-order.json
```

## Write the name and description

Name each Tool as a verb and an object in lowercase snake\_case, such as `lookup_order` or `create_ticket`. Before the model sees a name, Runtype lowercases it and replaces every character other than letters, digits, and underscores with `_`. A camelCase name such as `sendEmail` reaches the model as `sendemail`.

Write the description as instructions to the model. The description can be at most 500 characters on create, so include these parts in this order:

1. What the Tool does.
2. When to use it, and when to use a different Tool instead.
3. What the Agent must call or know first.
4. What the Tool returns.

The following description covers all four parts:

```plaintext
Retrieves one customer by email or customer ID. Use this when you already
have an identifier. If you only have a name, call search_customers first.
Read-only and safe to retry. Returns id, name, email, and plan.
```

State side effects in the first sentence of a Tool that changes data, for example "Permanently deletes the ticket." Keep each Tool to one responsibility. Two Tools with overlapping descriptions make selection less reliable.

## Design the parameters

The parameter schema is part of the prompt. Apply these rules to each parameter:

* Use an `enum` for any string that has a fixed set of valid values, and list the values in the description too.
* Keep required parameters to the minimum. Every required parameter is a value that the model must produce correctly.
* Accept the identifier that a person has, such as an email address, rather than only an internal ID.
* Use type `object` or `array` for structured input, not a `string` that holds JSON. Models often drop the escape characters in a JSON string.
* Handle a missing optional value in the Tool. Runtype doesn't fill in a parameter's default value when the Agent omits it.

Never ask the model for a credential. Put credentials in managed secrets and reference them as `{{secret:NAME}}` in the Tool configuration.

When you create or update a Tool with the API, read `validation.warnings` and `validation.recommendations` in the response. The save succeeds even when they are present. For example, a string parameter that looks like a JSON payload returns a `TOOL_PARAM_EMBEDDED_JSON_STRING` recommendation.

## Shape the result

Every Tool result becomes part of the model's next input, so return only the fields that the Agent needs for its next step. Use the same field names in every Tool, such as `createdAt` everywhere rather than `created` in one Tool and `ts` in another.

An External API Tool returns the API response unchanged. To trim or rename fields, wrap the request in a Flow, use a transform step to shape the response, and expose the Flow as a Flow Tool. In the Flow Tool configuration, set **Output variable (optional)** to the name of the variable that holds the shaped result. Otherwise, the Tool returns the output of the Flow's last step.

For a list, return a count and a flag such as `hasMore`, so that the Agent can tell a complete list from a partial one.

## Write errors that the Agent can act on

When a Tool fails, the Agent receives the error message and a failure class, such as `timeout` or `invalid_args`, and can retry, correct its arguments, or choose another Tool. Write each error message so that it states what went wrong and what to do next.

The following custom Tool code returns an error that names the valid values:

```javascript
const { priority } = parameters
const valid = ['low', 'medium', 'high']

if (!valid.includes(priority)) {
  return {
    success: false,
    error: `Unknown priority "${priority}". Use one of: ${valid.join(', ')}.`,
  }
}

return { success: true, priority }
```

For the full list of failure classes, see [Error handling](/user-guide/agents/agent-tools#error-handling).

## Set timeouts

Each Tool type has its own time limit. When a call reaches the limit, the call fails and the Agent receives a `timeout` error. The following table lists the limits for saved Tools:

| Tool type    | Setting          | Default                    | Maximum                                                       |
| ------------ | ---------------- | -------------------------- | ------------------------------------------------------------- |
| Custom Code  | **Timeout (ms)** | 30,000 ms in the dashboard | 300,000 ms in the dashboard                                   |
| External API | None             | 30 seconds                 | 30 seconds                                                    |
| Subagent     | **Timeout (ms)** | 300,000 ms                 | 600,000 ms in the dashboard                                   |
| MCP Server   | `timeout`        | 30,000 ms                  | 300,000 ms in background runs; 30,000 ms in synchronous calls |

A custom Tool that you create with the API and without a `timeout` gets a fallback that depends on its execution environment. For the fallbacks, see [Set execution limits](/user-guide/tools/creating-custom-tools#set-execution-limits). For MCP servers, see [Limits](/developer-guides/integrations/mcp-servers#limits).

A Tool call also runs inside the time limits of the step or Agent turn that makes the call. If the work takes minutes, don't raise the Tool timeout. Run it as a detached subagent or as a separate Flow run instead. For these patterns, see [Timeouts and long-running work](/user-guide/flows/timeouts-and-long-running-work).

## Guard Tools that change data

Put rules that protect data in the Tool configuration and the Agent configuration, not in the system prompt. Use these controls:

* Require approval for a Tool that deletes data, sends messages, or spends money. The run pauses until a person approves or denies the call. For setup, see [Require approval for tool calls](/user-guide/agents/agent-tools#require-approval-for-tool-calls).
* Keep credentials in managed secrets. Runtype resolves `{{secret:NAME}}` references when the Tool runs and doesn't return the secret value to the Agent.
* For runtime Tools, hide parameters that come from context, such as a tenant ID, so the model can't supply them. For instructions, see [Hide parameters from the model](/user-guide/tools/runtime-tools#hide-parameters-from-the-model).

## Test the Tool as an Agent uses it

To confirm that an Agent selects and calls the Tool correctly, follow these steps:

1. Open the Tool, enter sample values in the **Test Tool** panel, and click **Run Test**. Include the inputs that a model plausibly sends, such as a name where the Tool expects an ID. Read each error as though you knew only the description.
2. Attach the Tool to an Agent and run a realistic prompt.
3. Open the run in [Logs](/user-guide/logs/working-with-logs) and check which Tools the Agent called, the parameters, and the results.
4. If the Agent picks the wrong Tool or sends the wrong parameters, change the description or the schema first, then run the prompt again.
5. Optional: Add the run to an eval suite, so that each later description or schema change is checked against it. For more information, see [What are Evals?](/user-guide/evals/what-are-evals).

To test from the command line, run `runtype tools execute <tool-id> --input '{"order_id": "ord_1042"}'`. Replace `<tool-id>` with the ID of the Tool.

## Next steps

* [Agent tools](/user-guide/agents/agent-tools): attach Tools and set tool call limits and strategy.
* [Creating custom tools](/user-guide/tools/creating-custom-tools): write code that an Agent can run.
* [Creating external tools](/user-guide/tools/creating-external-tools): configure HTTP requests and body templates.
* [Runtime tools](/user-guide/tools/runtime-tools): define Tools, including subagents, inline in a request.
* [Manage tools as code](/user-guide/tools/manage-tools-as-code): keep Tool definitions in your repository.