Skip to navigation

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 dashboardtoolTypeUse it when
Flow ToolflowThe Agent always runs the same sequence of steps, or an API response needs reshaping.
Custom CodecustomThe work is a calculation, validation, or transformation of the inputs.
External APIexternalThe work is one HTTP request, with credentials from managed secrets.
SubagentsubagentA focused task needs its own context window and its own subset of Tools.
MCP ServermcpAn 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?.

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.

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
{
"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:

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:

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:

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.

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 typeSettingDefaultMaximum
Custom CodeTimeout (ms)30,000 ms in the dashboard300,000 ms in the dashboard
External APINone30 seconds30 seconds
SubagentTimeout (ms)300,000 ms600,000 ms in the dashboard
MCP Servertimeout30,000 ms300,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. For MCP servers, see 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.

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

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

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