Skip to navigation

Runtime tools

Define runtime tools inline in a dispatch request instead of saving them to your account. Use them for SDK-driven workflows, temporary configurations, testing, and one-off tasks. Runtime tools support five types: external, custom, Flow, subagent, and local.

When to use runtime tools

Use runtime tools in the following cases:

  • Define tools dynamically in an SDK-driven workflow.
  • Test a tool configuration before you save it.
  • Keep a temporary tool out of your account’s saved tool definitions.
  • Define a self-referential tool that calls the Runtype API.

Tool types

Every runtime tool needs a name, a description, a toolType, and a parametersSchema. The parametersSchema must be a JSON Schema object with "type": "object" at the top level. Every type except local also needs a config object.

Choose one of the following runtime tool types.

External

Call an HTTP API. Set config.url and config.method, and optionally set config.headers and config.body. The method is GET, POST, PUT, or DELETE. The body is a JSON string, not an object. Use template variables such as {{city}} for tool parameters and {{secret:WEATHER_API_KEY}} for managed secrets in the URL, headers, and body.

The following example defines an external tool that sends a JSON body:

External tool
{
"name": "create_ticket",
"description": "Create a support ticket",
"toolType": "external",
"parametersSchema": {
"type": "object",
"properties": {
"subject": { "type": "string" },
"priority": { "type": "string", "enum": ["low", "high"] }
},
"required": ["subject", "priority"]
},
"config": {
"url": "https://api.example.com/v1/tickets",
"method": "POST",
"headers": {
"Authorization": "Bearer {{secret:TICKETS_API_KEY}}",
"Content-Type": "application/json"
},
"body": "{\"subject\": \"{{subject}}\", \"priority\": \"{{priority}}\"}"
}
}

The subject and priority parameters supply the {{subject}} and {{priority}} values. Replace TICKETS_API_KEY with the name of the managed secret that stores the API key.

Custom

Run sandboxed JavaScript, TypeScript, or Python code. Set config.code, and optionally set config.language, config.sandboxProvider, and config.timeout. In the default environment, your code reads the tool inputs from the parameters object.

The following example calculates a discounted price with JavaScript:

Custom tool
{
"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": "return { discounted_price: parameters.price * (1 - parameters.discount / 100) }",
"timeout": 5000,
"language": "javascript"
}
}

The timeout value is in milliseconds, up to 30000. Set language to javascript, typescript, or python. The default is javascript.

The sandboxProvider field selects the execution environment. The default, cloudflare-worker, runs JavaScript only. For TypeScript or Python, set sandboxProvider to runtype-sandbox or daytona. Otherwise, the tool call fails with an unsupported-language error. For how each environment passes parameters, see Creating custom tools.

Custom tool code has no network access unless the tool sets networkAccess. To send a managed secret, declare it in secrets: the code receives a placeholder token as secrets.NAME, and the egress proxy replaces it with the real value only on requests to the declared host. For the fields, see Call an API from custom code.

Flow

Run a Flow as a tool. Set config.flowId to the saved Flow ID and use parameterMapping to map tool inputs to Flow inputs.

By default, the tool returns the value that the Flow’s final executed step wrote to its output variable. For example, it returns what a trailing transform-data step returned. The following optional fields narrow that value:

  • outputVariable returns a named Flow variable instead of the final step output. The variable must be assigned during the run, and names that begin with _ are reserved.
  • outputMapping is a dot path into the value that the tool returns. Suppose the final step writes { "score": 0.9 } to its output variable analysis. Both score and analysis.score return 0.9.

The following example maps a tool input to a saved Flow:

Flow tool
{
"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": { "input_text": "text" },
"outputMapping": "sentiment_result"
}
}

Replace YOUR_FLOW_ID with the ID of the Flow that you want to run.

On an agent dispatch, a Flow tool can also define the Flow inline. Replace flowId with a flow object that has a non-empty steps array. Runtype rejects an inline flow on a Flow dispatch prompt step and on an agent execute request. Reference a saved Flow with flowId there.

Subagent

Delegate a focused task to a child agent. The child runs in its own context window and returns only its final result to the parent. By default, the child doesn’t receive the parent’s conversation. Set inheritMessages to true to pass the current messages to the child.

Subagent tools use exactly one of agentId or agent. Set allowedTools to limit the child to a subset of its own configured tools.

The following example runs a saved agent as a subagent tool:

Subagent tool
{
"name": "research_topic",
"description": "Research a topic and return a summary",
"toolType": "subagent",
"parametersSchema": {
"type": "object",
"properties": {
"task": { "type": "string" }
},
"required": ["task"]
},
"config": {
"agentId": "YOUR_AGENT_ID",
"allowedTools": ["builtin:exa", "builtin:firecrawl"],
"maxTurns": 5,
"outputFormat": "text"
}
}

Replace YOUR_AGENT_ID with the ID of the saved agent. To define the child inline, replace agentId with an agent object and keep only one of these fields.

The following fields configure a subagent tool:

FieldDefaultDescription
agentIdNoneSaved agent ID. Set this or agent, not both.
agentNoneInline agent definition. Set this or agentId, not both.
allowedToolsRequired for inline agentsTool IDs from the child’s own tools that the child can use. [] grants no tools. When you set it, the child also loses its MCP servers. Leave it unset on a saved agent to keep all of the child’s tools.
maxTurnsThe child’s own limitMaximum number of agent-loop iterations for the child.
maxCostParent budgetSpend cap for the child. The cost counts toward the parent’s budget.
timeoutMs300000Time limit for an attached child in milliseconds.
outputFormattextReturn text, json, or last_message.
inheritMessagesfalsePass the parent’s current messages to the child when set to true.
taskTemplateNoneTemplate the child’s first message from the tool-call parameters.
execution{ "mode": "attached" }Run the child attached or detached. For the fields, see Run a subagent in the background.

Run a subagent in the background

By default, a subagent tool runs attached: the parent waits for the child’s result. Set execution.mode to detached to start the child in the background. The tool call returns a run handle right away, and the parent continues its turn.

The following fields configure a detached subagent:

FieldDefaultDescription
modeattachedattached or detached.
notifynarratenarrate records the child’s completion in the parent’s conversation. none sends no completion notice.
maxBudgetMs1800000Maximum run time for the detached child, from 1000 to 86400000 milliseconds.
noProgressBudgetMsNoneStop the detached child when it makes no progress for this long, from 1000 to 86400000 milliseconds.

The narrate notice requires a saved parent agent and a conversationId on the request. Without them, set notify to none.

Local

Run a local tool in the client that drives the dispatch. A local tool doesn’t need a config object. When the model calls the tool, Runtype pauses the run and streams the tool name and parameters to your client. Your client runs the handler and resumes the execution with the result.

Use local tools for actions that must run in the caller’s environment, such as prompting the end user or reading local state. An SDK process or CLI session can run local tools. The Runtype dashboard doesn’t run your local tools.

The following example defines a local tool:

Local tool
{
"name": "ask_user",
"description": "Ask the end user a clarifying question",
"toolType": "local",
"parametersSchema": {
"type": "object",
"properties": {
"question": { "type": "string" }
},
"required": ["question"]
}
}

The TypeScript SDK runs the pause-and-resume loop for you. The following example runs a saved agent with one local tool:

Local tool with the TypeScript SDK
import { RuntypeClient } from '@runtypelabs/sdk'
const client = new RuntypeClient({ apiKey: process.env.RUNTYPE_API_KEY! })
const result = await client.agents.executeWithLocalTools(
'YOUR_AGENT_ID',
{ messages: [{ role: 'user', content: 'Plan my trip to Lisbon' }] },
{
ask_user: {
description: 'Ask the end user a clarifying question',
parametersSchema: {
type: 'object',
properties: { question: { type: 'string' } },
required: ['question'],
},
execute: async ({ question }) => promptInTerminal(String(question)),
},
}
)

Replace YOUR_AGENT_ID with the ID of the saved agent, and replace promptInTerminal with your own function that returns the answer. To handle the pause and resume yourself, see Pause and resume on an agent dispatch.

Local tools run only when a live caller drives the execution. On an unattended run, such as a messaging channel, a webhook, or a batch run, Runtype removes local tools from the turn and logs a warning that names them. Use an external, custom, or Flow tool for work that must run unattended.

Let an agent spawn subagents

Let an agent choose what to delegate at runtime by adding subagentConfig to its tools configuration. Runtype adds the built-in spawn_subagent tool. For each call, the model chooses the task, the tools from toolPool, and an optional system prompt.

The following tools configuration enables dynamic subagent spawning:

Dynamic subagent configuration
{
"tools": {
"toolIds": ["builtin:exa", "mcp:linear:create_issue"],
"subagentConfig": {
"toolPool": ["builtin:exa", "mcp:linear:*"],
"maxTurnsLimit": 10,
"maxSpawnsPerRun": 5,
"allowNesting": false
}
}
}

The toolPool must be a subset of the parent’s resolved tools. Set allowNesting to true to let a subagent spawn its own subagents.

The following fields control dynamic subagent spawning:

FieldDefaultDescription
toolPoolRequiredTools that the parent can grant to spawned subagents.
maxTurnsLimit10Maximum turn cap that the model can request for a spawned subagent.
defaultMaxTurnsNoneAccepted, but has no effect. A spawned subagent uses a turn cap only when the model requests one.
maxSpawnsPerRun5Maximum number of spawned subagents per parent run.
defaultModelParent modelModel that spawned subagents use by default.
allowNestingfalseWhether a subagent can spawn its own subagents.
defaultTimeoutMs300000Default time limit for each spawned subagent in milliseconds.
executionModes["attached"]Execution modes that the model can request. Add detached to allow background subagents.
defaultExecutionModeattachedMode to use when the model doesn’t request one.
defaultNotifynarrateCompletion notice for detached subagents: narrate or none.
detachedMaxBudgetMsNoneMaximum run time for a detached subagent, from 1000 to 86400000 milliseconds.
detachedNoProgressBudgetMsNoneStop a detached subagent that makes no progress for this long, from 1000 to 86400000 milliseconds.

Pass runtime tools in a dispatch request

Add runtime tools to the tools.runtimeTools array on a prompt step.

The following request defines one runtime external tool on the Assistant prompt step:

Dispatch request
{
"flow": {
"name": "My Assistant",
"steps": [
{
"id": "step-1",
"type": "prompt",
"name": "Assistant",
"order": 1,
"enabled": true,
"config": {
"model": "claude-haiku-4-5",
"systemPrompt": "You are a helpful assistant.",
"tools": {
"runtimeTools": [
{
"name": "search_web",
"description": "Search the web",
"toolType": "external",
"parametersSchema": {
"type": "object",
"properties": {
"query": { "type": "string" }
},
"required": ["query"]
},
"config": {
"url": "https://api.example.com/search?q={{query}}",
"method": "GET"
}
}
]
}
}
}
]
},
"options": {
"streamResponse": true,
"flowMode": "virtual"
}
}

The {{query}} variable resolves from the tool input. The flowMode value virtual runs the inline Flow without saving it.

Combine runtime tools with saved tools and built-in tools in the same prompt step.

The following configuration combines a saved tool, a built-in tool, and a runtime tool:

Combined tools
{
"tools": {
"toolIds": ["YOUR_TOOL_ID", "builtin:exa"],
"runtimeTools": [
{
"name": "my_runtime_tool",
"description": "Run a runtime tool",
"toolType": "external",
"parametersSchema": {
"type": "object",
"properties": {}
},
"config": {
"url": "https://api.example.com/status",
"method": "GET"
}
}
]
}
}

Replace YOUR_TOOL_ID with the ID of the saved tool.

Add runtime tools to an agent dispatch

To give a saved agent extra tools for one request, add them to agent.tools.runtimeTools. The agent’s saved tools stay available.

The following request adds one runtime tool to a saved agent:

Agent dispatch with a runtime tool
{
"agent": {
"agentId": "YOUR_AGENT_ID",
"tools": {
"runtimeTools": [
{
"name": "get_order_status",
"description": "Get the status of an order",
"toolType": "external",
"parametersSchema": {
"type": "object",
"properties": {
"orderId": { "type": "string" }
},
"required": ["orderId"]
},
"config": {
"url": "https://api.example.com/orders/{{orderId}}",
"method": "GET"
}
}
]
}
},
"messages": [{ "role": "user", "content": "Where is order 1042?" }]
}

Replace YOUR_AGENT_ID with the ID of the saved agent. An agent execute request (POST /v1/agents/{id}/execute) also accepts runtime tools in tools.runtimeTools.

When an agent with Agent loop turned on has 20 or more tools, Runtype holds its runtime tools back behind a tool_search tool, and the model loads a runtime tool by searching for it. To send every runtime tool on every turn, add "toolSearch": { "enabled": false } to agent.tools. For the full behavior, see Tool search.

Secrets

Reference a managed secret by name in an external tool’s URL, headers, or body. To add a managed secret, open Settings and select Secrets.

The following example references a managed secret in an external tool header:

Managed secret reference
{
"headers": {
"Authorization": "Bearer {{secret:WEATHER_API_KEY}}"
}
}

Replace WEATHER_API_KEY with the name of the managed secret. Other tool types can’t reference managed secrets.

The API key that sends the dispatch request must have the SECRETS:READ or SECRETS:* permission to resolve secret references. Otherwise, the request fails with a 403 error.

Dispatch requests don’t accept a top-level secrets map. A non-empty map returns a 400 error. An agent dispatch returns RUNTIME_AGENT_TRANSIENT_SECRETS_UNSUPPORTED, and a Flow dispatch returns RUNTIME_FLOW_TRANSIENT_SECRETS_UNSUPPORTED. An empty map is ignored. Runtype encrypts managed secret values at rest and doesn’t log them or return them in responses.

Hide parameters from the model

Add hiddenParameterNames to a runtime tool to fill a parameter from an execution variable instead of from the model. Runtype removes each listed parameter from the schema that the model sees. When the tool runs, Runtype sets the parameter to the execution variable with the same name, and that value replaces any value the model sent.

The following example hides the _tenant parameter and uses it in a request header:

Hidden parameter
{
"name": "list_orders",
"description": "List recent orders for the current account",
"toolType": "external",
"parametersSchema": {
"type": "object",
"properties": {
"status": { "type": "string" }
},
"required": ["status"]
},
"hiddenParameterNames": ["_tenant"],
"config": {
"url": "https://api.example.com/v1/orders?status={{status}}",
"method": "GET",
"headers": {
"Authorization": "Bearer {{secret:ORDERS_API_KEY}}",
"X-Tenant-Id": "{{_tenant.id}}"
}
}
}

Replace ORDERS_API_KEY with the name of the managed secret that authenticates Runtype to your API. Runtype merges hidden values into the tool arguments before it fills the URL, headers, and body, so {{_tenant.id}} resolves from the hidden _tenant value.

Runtype resolves a hidden parameter based on its name:

NameWhere the value comes fromIf no value is found
No leading _, for example accountIdThe execution variable with that name. If it is not set, the field with that name in _record.metadata, then the field with that name inside any other object-valued variable without a leading _.The parameter stays in the schema, and the model supplies it.
Leading _, for example _tenantOnly the top-level execution variable with that name.Runtype doesn’t call the tool. The model receives an error as the tool result.
Leading _internalNever resolved.Runtype rejects the name in dispatch requests, saved agent definitions, and agent execute requests.

Use _tenant and _endUser for identity values. Runtype sets these variables from the execution’s end-user identity, not from the model, so the model can’t change them. For how Runtype establishes that identity, see End-user identity and credentials.

Only _tenant and _endUser come from Runtype’s identity handling. Do not rely on another name with a leading _ to keep caller-supplied values out of a tool call.

Also authenticate Runtype to your backend with a {{secret:NAME}} header, so your API does not trust a request on the hidden value alone.

Runtype replaces resolved hidden values with [REDACTED] in the tool_start and tool_input_complete stream events. The hiddenParameterNames field on those events lists the redacted parameters. The tool receives the real values.

You can set hiddenParameterNames on runtime tools in dispatch requests, in saved agent definitions, and in the tools.runtimeTools override of an agent execute request.

Use the verified identity in a saved tool

A tool saved in your account and attached by tool ID has no hiddenParameterNames field. Instead, Runtype treats _endUser and _tenant as hidden in a tool that references them in either of these ways:

  • An external tool’s url, headers, or body contains a template such as {{_endUser.id}} or {{_tenant.id}}.
  • The tool’s parametersSchema has a property named _endUser or _tenant.

For such a tool, Runtype applies the rules for a hidden name with a leading _:

  • Runtype removes the property from the schema that the model sees.
  • Runtype fills the value only from the execution’s _endUser or _tenant variable. The model’s tool arguments and the request metadata cannot supply it.
  • If the execution has no value, Runtype doesn’t call the tool. The model receives an error as the tool result. The exception is a root that the tool references only before a template fallback, such as {{_endUser.id || 'guest'}}, {{_endUser.id ?? 'guest'}}, or {{_endUser.id|guest}}. Runtype then calls the tool and the template renders the fallback.
  • Runtype sends the value only where the tool’s templates reference it. Runtype doesn’t add it to the query string or to a body it builds from the remaining arguments.

The same rules apply to an inline runtime tool that references _endUser or _tenant without declaring it in hiddenParameterNames, and to a saved tool that a Flow’s tool-call step calls. A tool-call step’s own _endUser or _tenant parameter never replaces the execution’s value.

A Flow that runs as a tool gets _endUser and _tenant from the execution that called it, never from the tool arguments. If that execution has no verified identity, the nested Flow has no _endUser or _tenant variable.

Pass the verified identity to an MCP server

To send the verified tenant or end user to a remote MCP server, add a header template to the server in the agent’s tools.mcpServers. Use the headers auth type:

MCP server with an identity header
{
"id": "erp",
"name": "ERP orders",
"url": "https://mcp.example.com/mcp",
"auth": {
"type": "headers",
"headers": {
"Authorization": "Bearer {{secret:ERP_MCP_TOKEN}}",
"X-Tenant-Id": "{{_tenant.id}}"
}
}
}

Replace ERP_MCP_TOKEN with the name of the managed secret that authenticates Runtype to your MCP server. You do not declare hiddenParameterNames for an MCP server. Runtype treats every header value that references a variable with a leading _ as a hidden identity value:

  • Runtype fills {{_tenant.id}} only from the execution’s _tenant variable. The model’s tool arguments and the request metadata cannot supply it.
  • If the execution has no _tenant, Runtype does not call the MCP tool. The model receives an error as the tool result.
  • If the model sends an argument named _tenant, Runtype removes it from the call.
  • Runtype sends the header on tool calls only. The tools/list request that discovers the server’s tools does not include it.

This applies to MCP servers on an agent and on a Flow’s prompt step. It does not apply to MCP servers saved in your account and attached by tool ID, because Runtype resolves those headers from the saved server.

To let the MCP server verify the identity itself, use "Authorization": "Bearer {{_identity.token}}". Runtype signs a short-lived assertion for the server’s host on each tool call. For the claims and how to verify them, see Send a verifiable identity assertion to your API.

MCP credentials go on the server, never on a saved MCP tool. A saved MCP tool only points at a server, so its config rejects {{secret:NAME}} references. Put the credential in one of these places instead:

  • The auth of a server you register with POST /v1/mcp/servers. Attach the server’s tools to the agent by tool ID.
  • The auth.headers of the agent’s tools.mcpServers entry, as in the preceding example.

Both accept {{secret:NAME}} references. Only the tools.mcpServers entry can also carry {{_tenant.id}}.

Retry tools safely after an interrupted run

A run can be interrupted while a tool call is in progress. When the run resumes, Runtype doesn’t call that tool again automatically, because it can’t tell whether the first call finished. If the model calls the tool again, it receives an error that asks it to check the result before it retries. The model then decides whether to check, retry, or move on.

A tool that only reads data is safe to call again. To let a resumed run call it again, set idempotent to true:

Tool that is safe to call again
{
"name": "lookup_order",
"description": "Look up an order by ID",
"toolType": "external",
"idempotent": true,
"parametersSchema": {
"type": "object",
"properties": { "orderId": { "type": "string" } },
"required": ["orderId"]
},
"config": { "url": "https://api.example.com/orders/{{orderId}}", "method": "GET" }
}

Leave idempotent unset for any tool that writes, sends, charges, or creates. Built-in tools that only read or search are already marked safe to call again. MCP tools follow the server’s idempotentHint and readOnlyHint annotations.

Require a committed intent before a tool runs

Before an Agent calls a server-side tool, Runtype records an intent: a durable note that the call is about to start. By default the intent is best-effort. If Runtype can’t record it, the tool still runs.

Set tools.intent to mandatory on an Agent so that a tool that isn’t idempotent runs only after its intent is recorded:

Agent that requires a committed intent
{
"tools": {
"intent": "mandatory",
"toolIds": ["builtin:runtype_record_upsert"]
}
}

tools.intent accepts mandatory or best-effort. The default is best-effort.

With mandatory, if Runtype can’t record the intent, it doesn’t call the tool. The tool call fails, and the model receives an error that starts with TOOL_INTENT_NOT_COMMITTED:

TOOL_INTENT_NOT_COMMITTED: the tool call was not started because its intent could not be recorded durably. It had no effect; it is safe to call it again.

The call had no effect, so the model can call the tool again.

The setting has these limits:

  • It applies to Agents only. A flow prompt step doesn’t record tool intents, so the flow validator warns with TOOL_INTENT_POLICY_AGENT_ONLY when a prompt step sets it.
  • It applies only where the run keeps a tool journal, which durable Agent runs do. A run without a tool journal calls the tool as it would without the setting.
  • Tools marked idempotent keep the best-effort intent, because calling them again is safe.
  • A dispatch request can set tools.intent to mandatory for a saved Agent. A request can’t lower a saved Agent’s mandatory to best-effort.

Each intent also records whether the tool was idempotent when the call started. When a run resumes after an interruption, Runtype calls an interrupted tool again only if it was idempotent then and is still idempotent now. Marking a tool idempotent later doesn’t make an interrupted call run again. Removing idempotent stops an interrupted call from running again.

Ask the end user for tool inputs

An external or custom tool can ask the end user for some of its inputs before it runs. The model fills the other parameters, and the end user fills the requested fields in a form.

To ask for input, add config.elicit with a message, a requestedSchema, and "merge": "parameters". The following example asks the end user for the meeting date:

Tool that asks the end user for input
{
"name": "schedule_meeting",
"description": "Schedule a meeting with the customer",
"toolType": "external",
"parametersSchema": {
"type": "object",
"properties": {
"topic": { "type": "string" },
"date": { "type": "string", "title": "Meeting date" }
},
"required": ["topic", "date"],
"additionalProperties": false
},
"config": {
"url": "https://api.example.com/v1/meetings",
"method": "POST",
"body": "{\"topic\": \"{{topic}}\", \"date\": \"{{date}}\"}",
"elicit": {
"message": "Which date works for you?",
"requestedSchema": {
"type": "object",
"properties": {
"date": { "type": "string", "title": "Meeting date" }
},
"required": ["date"],
"additionalProperties": false
},
"merge": "parameters"
}
}
}

The model sees only the topic parameter. When it calls the tool, the end user sees the message and a form for date. Runtype merges the answer into the tool parameters and then calls the API.

The tool definition must follow these rules:

  • Set "additionalProperties": false on both parametersSchema and requestedSchema.
  • Define each requested field exactly as it appears in parametersSchema, with the same required setting.
  • Use only string, number, integer, and boolean fields. Each field can have a title, a description, an enum, string length bounds, and numeric bounds.
  • Don’t request a field whose name starts with _ or that is listed in hiddenParameterNames.

If the end user cancels or declines the form, Runtype doesn’t call the tool. The model receives a cancellation result instead.

Tool input requests work on AG-UI surfaces for top-level agent calls that use a Surface key. They don’t work inside subagent calls, in Claude Managed Agents, or in Persona. For the stream events and the resume request, see Ask for input before dispatch.

Limits

Runtime tools have the following limits:

  • Runtime tools per request: no limit on the number of tools. The runtimeTools in a request can be at most 1 MB in total, across all steps, when serialized as JSON.
  • External tool request timeout: 30 seconds.
  • Custom tool timeout: 30,000 ms at most. Saved custom tools accept longer timeouts. For more information, see Creating custom tools.
  • Subagent tool timeout: timeoutMs, 5 minutes by default.
  • Inline MCP servers per prompt step: 5.

A model provider can reject a long tool list even though Runtype accepts it. For example, OpenAI accepts at most 128 tools. Runtype reports that rejection with the error code TOOL_COUNT_REFUSED_BY_PROVIDER. For a large tool set, use a multi-turn Agent with tool search, which loads runtime tools only when the model searches for them.

For client tool limits and MCP tool call timeouts, see Limits in the developer guide.

Troubleshoot validation errors

Runtype validates runtime tools before the run starts. An invalid tool returns an HTTP error with an error message, and nothing runs. The most common causes are the following:

  • A missing name, description, toolType, parametersSchema, or non-local config.
  • A parametersSchema without "type": "object" at the top level.
  • A tool name that matches the reserved runtype_set_state name after Runtype lowercases it and replaces other characters with _.
  • A custom tool timeout greater than 30000.
  • An inline subagent agent without allowedTools.

For the full list of error messages and fixes, see Troubleshooting in the developer guide.

Next steps

Continue with one of the following pages: