Tool middleware#
Tool middleware lets you wrap one function tool with async hooks that run immediately around its execution—the same idea as an agent's on_tool_execution() Middleware, but attached at tool definition time with plain callables (no BaseMiddleware subclass).
Use this pattern when behavior is specific to a single tool. For policies that apply to every tool on an agent, register BaseMiddleware with on_tool_execution() instead.
Why use it#
Tool-scoped hooks are optional. They help when:
- Colocation — Validation, redaction, or metrics for one tool live next to that implementation instead of in shared agent middleware.
- Clear contracts — Libraries can ship a tool with hooks that always run (normalize arguments, scrub secrets) without requiring consumers to register matching agent middleware.
- Simpler agents — You avoid a large
on_tool_executionfull ofif event.name == ...when only a few tools need special handling.
Use agent middleware=[...] when the policy is global or shared across most tools. Use middleware=[...] on @tool, @agent.tool, or @toolkit.tool when the behavior belongs to that tool only.
Typical cases#
| Scenario | Reason to use tool-scoped hooks |
|---|---|
| Normalize or validate arguments for one function | Only that tool’s schema needs the transform; keeps agent middleware small. |
| Redact or reshape results before they return to the model | Per-tool privacy or formatting (for example strip internal IDs). |
| Light auditing or metrics for a sensitive action | The hook is bundled with the tool so it is hard to forget at agent setup. |
| Retry or fallback tied to one integration | Failure handling stays next to the API client without naming the tool in global middleware. |
| Approve or reject before the tool body runs | Gate dangerous or irreversible tools on policy, session flags, or a human decision without scattering checks inside the implementation. |
API#
The public type alias is ToolMiddleware in autogen.beta.middleware. A hook is an async callable with the same parameters as BaseMiddleware.on_tool_execution, except the first argument is the inner ToolExecution (the next step in the chain), not self.
- Pass
middleware=[hook, ...]to@tool,Agent.tool, orToolkit.tool. - Multiple hooks use the same nesting as agent tool middleware: the first entry in the list is the outermost layer around the tool body.
- If the agent also registers
BaseMiddlewarewithon_tool_execution, agent middleware runs outside tool-scoped hooks (it sees the full execution, including hooks).
Note
Tool-scoped hooks are plain callables. They do not use the Middleware(...) factory or BaseMiddleware.