Tool Budget
Use tool-budget to block a request when max_tokens is more than a configured tool limit. The policy checks declared tools. It does not meter actual use.
It does not observe actual tool calls or token use. It does not calculate request cost.
Outcome
| Condition | Verdict | Cause code |
|---|---|---|
| A minimum of one listed tool exceeds its token ceiling | block | tool-budget.exceeded |
| No listed tool exceeds | allow | tool-budget.ok |
The nested policy-result details contain:
{
"exceeded_tools": [
"web_search"
]
}
An allowed chain keeps last cause ok. Examine tool-budget.ok for policy evidence. A block occurs before the upstream call. It returns an HTTP 400 policy-violation response.
Prerequisites
- Add
tool-budgetto the effective request chain. - Use exactly the same tool names as the client sends in
request.tools. - Make sure that clients send a non-negative integer in
max_tokens. This policy does not read other token-limit fields. - Use Usage, key-budget, rate-limit, or billing controls for actual or cumulative consumption.
Configuration
pack:
name: tool-budget-example-1
version: "1.0.0"
enabled: true
policies:
chain:
- tool-budget
policy:
tool-budget:
budgets:
web_search:
max_tokens: 5000
database_query:
max_tokens: 2000
Supported fields
| Field | Type | Default | Notes |
|---|---|---|---|
budgets | object | {} | Maps tool names to limit objects. |
budgets.<tool>.max_tokens | integer ≥ 1 | — | Enforced against the request's top-level max_tokens. |
tool-budget does not accept max_cost_usd. Use max_tokens here. Use the supported Usage or billing controls for cost governance.
Specified evaluation
The evaluator does these steps:
- Reads top-level
request.max_tokensas an unsigned integer. - Reads each item in top-level
request.tools. - Gets
tools[*].function.name. If this field is missing, it getstools[*].name. - Looks up each name in
policy.tool-budget.budgets. - Adds the name to
exceeded_toolswhenrequest.max_tokens > budgets.<name>.max_tokens.
The comparison is strict. A request equal to the configured limit is allowed.
Request shapes
The policy recognizes the two tool-name forms:
{
"max_tokens": 250,
"tools": [
{
"type": "function",
"function": {
"name": "web_search"
}
},
{
"name": "database_query"
}
]
}
The list contains tools available to the request. It does not contain tool calls that the model executed.
Missing and other fields
The policy reads these cases as a zero-token request. They do not exceed a positive limit:
- Missing
max_tokens. - Negative, fractional, or string
max_tokens. - request families that send only
max_completion_tokensormax_output_tokens.
A missing or non-array tools field gives no examined tools. This policy allows unconfigured tool names.
Do not use these allow paths as safe defaults. Normalize the request to the supported fields. For other fields, use the owning control.
Test the blocking contract
Create tests/blocks-search-over-budget.json:
{
"name": "blocks-search-over-budget",
"input": {
"messages": [
{
"role": "user",
"content": "Search the documentation."
}
],
"request": {
"model": "replace-with-model-id",
"max_tokens": 250,
"tools": [
{
"type": "function",
"function": {
"name": "web_search"
}
}
]
}
},
"expected": {
"verdict": "block",
"reason_code": "tool-budget.exceeded"
}
}
With web_search.max_tokens: 100, run:
verdictan policy lint --file policy-config.yaml
verdictan policy test --json
Examine details.policy_results[]. Make sure that it contains exceeded_tools: ["web_search"].
Add boundary fixtures for:
max_tokens: 100givesallow/ok.max_tokens: 101givesblock/tool-budget.exceeded.- An unconfigured tool gives
allow/ok. - Missing
max_tokensgivesallow/ok. - Multiple tools where only one exceeds the limit.
What this policy does not give
- Cumulative session, user, team, agent, or organization counters.
- Actual prompt, completion, or tool-execution token metering.
- Limits on the number of tool calls.
- Validation of tool arguments, schemas, safety, or authorization.
- Enforcement against tool calls returned subsequently in a model response.
Use tool-validation, tool-security, agent-firewall, token rate limits, and
the product's Usage/budget surfaces for those different purposes.
Rollout guidance
- Capture the specified
toolsand token-limit shape from each client library. - Normalize tool names and case. Budget lookup uses a specified map-key match.
- Set each limit to the maximum permitted completion size for that available tool.
- Add equality, one-above-limit, missing-field, and multi-tool fixtures.
- Send a live request. Verify the HTTP response and nested
exceeded_toolsdetails. - Monitor false blocks when applications include many tools that are not used in each request.
Troubleshooting
| Symptom | Likely cause | Resolution |
|---|---|---|
| Large request was allowed | The client used max_completion_tokens/max_output_tokens, did not send max_tokens, or sent a non-integer value. | Send supported top-level max_tokens or use a control that owns the other request family. |
| Tool was not examined | Its name was missing, nested differently, or did not exactly match a budget key. | Examine request.tools and align function.name or name. |
| Request blocked although no tool was called | The policy checks tools declared as available, not model-emitted invocations. | Decrease the request tool list or select a different control for actual calls. |
Config with max_cost_usd fails validation | Removed cost-budget field is configured in a tool entry. | Replace it with max_tokens here, or move the requirement to the product's supported Usage/billing workflow. |
| Equality did not block | The evaluator uses > rather than >=. | Set the ceiling one unit lower if that is the specified contract and retest. |
Allowed event has last cause ok | Allow-only nested causes do not replace the last decision cause. | Examine the nested result for tool-budget.ok. |
Next steps
- Tool Validation — validate declared tools and schemas
- Tool Security — examine tool request risk
- Agent Firewall — limit tool access and actions
- Rate Limits Configuration — govern request/token rates
- Policy Controls Catalog — compare adjacent controls