Your first tool
@tool decorator generates the JSON schema from the Pydantic model, which the LLM uses to understand what arguments to pass.
How tool calling works
LLM decides to call a tool
Based on the user’s message and the tool schemas, the LLM emits a
tool_call with the function name and arguments.Validate arguments
AFK parses the arguments through the Pydantic model. Invalid arguments
generate a validation error that’s sent back to the LLM for self-correction.
Check policy gate
If a PolicyEngine is attached, the tool call is
checked against policy rules (
allow, deny, or request_approval).Execute the handler
The tool function runs with validated arguments. Pre/post hooks and
middleware execute around the handler.
Sanitize output
The output is truncated to
tool_output_max_chars, stripped of potential
prompt injection vectors (if sanitize_tool_output=True), and formatted for
the LLM.Tool patterns
- Simple return
- Structured output
- Async tool
- With context
Return a string or dict directly.
Deferred background tool calls
For long-running operations, a tool can return a deferred handle so the run can continue while work completes in the background.- Runner emits
tool_deferred. - Agent continues with other work in the same run.
- Runner emits
tool_background_resolvedortool_background_failed. - Resolved tool output is injected back into conversation for next steps.
bgtool:{run_id}:{ticket_id}:statebgtool:{run_id}:latest
Policy-gated tools
Use the PolicyEngine to gate sensitive tool calls:Hooks and middleware
AFK provides four extension points for tool execution: prehooks, posthooks, tool-level middleware, and registry-level middleware. Each has its own decorator.Prehooks — transform args before execution
Prehooks — transform args before execution
Prehooks run before the tool handler. They receive the tool’s arguments and must return a dict compatible with the tool’s
args_model.Posthooks — transform output after execution
Posthooks — transform output after execution
Posthooks run after the tool handler. They receive a dict
{"output": <tool_output>, "tool_name": "<name>"} and should return a dict with the same shape.Tool-level middleware — wrap execution
Tool-level middleware — wrap execution
Middleware wraps the entire tool execution. It receives
call_next, the validated args, and optionally ctx.Registry-level middleware — wrap all tools
Registry-level middleware — wrap all tools
Registry-level middleware applies to every tool in a
ToolRegistry. Use for audit logging, rate limiting, or global policy enforcement.Execution order
| Layer | Scope | Decorator | Returns |
|---|---|---|---|
| Prehook | Single tool, before handler | @prehook(args_model=...) | dict of transformed args |
| Middleware | Single tool, wraps handler | @middleware(name=...) | Tool output (via call_next) |
| Posthook | Single tool, after handler | @posthook(args_model=...) | dict with output key |
| Registry MW | All tools in registry | @registry_middleware(name=...) | ToolResult (via call_next) |
Common tools cookbook
HTTP request tool
HTTP request tool
File read tool
File read tool
Calculator tool
Calculator tool
Prebuilt tools
AFK ships with ready-to-use tools for common agent capabilities. These are in theafk.tools.prebuilts module.
Runtime tools
Filesystem tools scoped to a directory for safe agent exploration:root_dir.
Skill tools
When an agent hasskills configured, AFK generates four skill tools automatically:
| Tool | Purpose |
|---|---|
list_skills | Return metadata for all enabled skills |
read_skill_md | Read a skill’s SKILL.md content and checksum |
read_skill_file | Read additional files under a skill directory |
run_skill_command | Execute allowlisted commands with timeout and limits |
SkillToolPolicy that controls command allowlists, output limits, and shell operator restrictions: