Skip to main content
Guard a single tool’s arguments and results without hand-wrapping the function — declare input_guardrails= / output_guardrails= right on the tool, next to approval=.
The guardrail fires on every invocation of send_email and never for any other tool.

Quick Start

1

Block a tool's arguments by rule

An input guardrail returns (False, "reason") to block. The reason is handed back to the model as the tool result, never raised at the user.
2

Rewrite the arguments before the tool runs

Return (True, new_arguments) to rewrite. The rewritten dict is what the tool receives.
An input guardrail’s rewrite must stay a dict of keyword arguments. Returning any other type is treated as a contract violation and blocks the call, because handing a non-mapping to the tool would raise inside your code instead of producing a message the model can act on.
3

Substitute the result

An output guardrail sees the raw result before it re-enters the LLM context. Return (True, new_result) to substitute, or (False, "reason") to block.

How per-tool guardrails run

Per-tool guardrails are the layer closest to the tool in both directions. The input guardrail runs last on the way in (immediately before dispatch); the output guardrail runs first on the way out (before any agent-wide guardrail and before the trust fence).
The ordering is deliberate. Human approval stays upstream of every automated rewrite, so a reviewer audits exactly what the model proposed. The input guardrail runs immediately before dispatch, so “the tool never runs with arguments its own guardrail did not see” holds. The output guardrail sees the raw result, so it does not have to parse the external-content fence, and a substituted result is fenced exactly like an original one.

Per-tool vs agent-wide guardrails

Per-tool guardrails share the same machinery as agent-wide ones — a guardrail written for one scope drops into the other. A guardrail entry may be a plain callable (value) -> (ok, value), a bare True / False, a GuardrailResult, or any object exposing validate_tool_call / validate_tool_result — so an existing GuardrailChain reuses unchanged. A guardrail that raises, or returns an unreadable verdict, fails closed and blocks the call.

Best Practices

When a rule belongs to one tool — “send_email recipients must be on-domain” — declare it on that tool, not agent-wide. An agent-wide guardrail fires for every tool and has to branch on tool_name.
Returning (True, sanitised_args) is safer than (False, reason) and hoping the model retries correctly. Fix the call rather than bouncing it back.
When a result carries a secret, return (True, redacted) so the model still gets a usable answer, instead of (False, ...) which throws the whole result away.
A guardrail should inspect and transform its input with no side effects — no network calls, no writes. A guardrail that raises fails closed and blocks the tool.

Guardrails

Agent-wide output and tool guardrails.

Approval

Human-in-the-loop sign-off before a tool runs.

Tool Approval

Declare a tool needs approval in one line.