STATEK Tools
STATEK tools are Python callables available to an agent job. The @tool decorator is the public way to make a callable an agent capability.
They are not the whole agent environment. A STATEK agent can also use Python objects already in context, variables it created earlier, dbzero-backed application objects, and the connected Python codebase. Tools are how you expose controlled capabilities: external APIs, side effects, permission checks, adapters, expensive operations, and other functions you want the agent to call deliberately.
For example, a job might start with these names already available:
user
calendar
todayThe agent can use those objects directly:
events = calendar.events_for(today)It can also call tools when the operation should go through an explicit capability:
meeting = calendar.find_meeting("Weekly planning", day=today)
later_slot = find_empty_slot(calendar, after=meeting.ends_at)
move_meeting(meeting, later_slot)The results are normal Python values. The agent can store them in variables, print them, pass them to other functions, and reuse them in later steps of the same durable job.
Declare a Tool
Use @tool for a function you want STATEK to treat as a tool.
from statek import tool
@tool
def find_empty_slot(calendar, after, duration_minutes=30, **kwargs):
"""Find an available calendar slot after a given time.
Args:
calendar: Calendar object to search.
after: Earliest acceptable start time.
duration_minutes: Required meeting length.
"""
return calendar.find_empty_slot(
after=after,
duration_minutes=duration_minutes,
)This is still ordinary Python, but the decorator does more than attach a label. It wraps the function as a STATEK capability, registers tool metadata, validates the callable shape, and applies STATEK runtime behavior when the tool is called.
Agent-facing tool functions must accept **kwargs. STATEK uses that channel for framework context during job execution.
What the Decorator Adds
@tool adds the machinery STATEK needs to expose and run the function safely inside a job:
- registration metadata for discovery, prompt formatting, provider tool payloads, and tool filtering
- validation that the function can accept STATEK framework keyword arguments
- support for sync and async callables
- scope metadata such as
system=True,hidden=True, andtarget=... - argument conversion before calling the original function
- local-context binding when the model passes the name of a live Python variable
- optional error-handler registration through
@tool(error_handler=...) - tool result and tool error formatting in job history
These behaviors are why STATEK tools are more flexible than plain Python functions called directly from generated code.
Tools Versus Context
Use context for objects the agent should directly reason over:
user
calendar
dataset
reportUse tools for operations that should be controlled:
find_empty_slot(calendar, after=meeting.ends_at)
send_confirmation(user, meeting)
run_expensive_query(dataset, filters)
request_human_approval(summary)This distinction is important. In STATEK, the agent does not need a tool for every read of application state. If your application puts a calendar object in the job workspace, the agent can use calendar as Python. A tool is useful when you want a named capability, validation, permission checks, or a side-effect boundary.
When the model is expected to call an application function by name, make that function a tool or a temporal function. Plain helper functions should stay internal unless they are intentionally part of the agent-facing surface.
Add Tools to an Agent
An agent can receive tools directly:
agent = Agent(
role="calendar_assistant",
_system_prompt=system_prompt,
_tools=[find_empty_slot, move_meeting],
_metadata={"MODEL": "openai/gpt-5-mini"},
)An agent can also keep callable objects in its private context and expose selected functions by name when the application uses that pattern:
agent.context["find_empty_slot"] = find_empty_slot
agent.context["move_meeting"] = move_meetingPrompt definition files are common in real applications. They usually carry the system prompt and model metadata, while application code wires the Python tools and context. Inline examples here are only to make the mechanics visible.
Describe Tools for the Model
Docstrings are the primary LLM-facing description for tools. Keep them short, concrete, and operational. Good tool descriptions say what the tool does, what each argument means, what the tool returns, whether it has side effects, and what permission or approval is expected before calling it.
STATEK system prompts can include tool placeholders:
{brief_tools}for compact tool descriptions{tools}for normal tool descriptions{detailed_tools}for Python-style details
For example:
# System Prompt
You help users manage their calendar by writing Python.
Available variables: {shared_var_names}
Available tools:
{brief_tools}Hidden tools are not included in these prompt expansions.
For the full convention, including VARIANTS/:.../TOOL:... wording, brief(...), docstr(...), @docs_style(brief_types=True), class/object documentation, and STATEK-ACL documentation filtering, see Agent-Exposed Docstring Conventions.
Arguments and Return Values
Tool calls accept positional and keyword arguments, then STATEK prepares them before calling the original Python function.
slot = find_empty_slot(
calendar,
after=meeting.ends_at,
duration_minutes=meeting.duration_minutes,
)STATEK can convert some model-friendly arguments into the Python values the function expects:
- A string passed to a parameter annotated as a
db0.enumis converted to that enum value, such as"ERROR"toSeverity.ERROR. - ISO date, datetime, or time strings are parsed when the parameter annotation expects
date,datetime,time, or a supported union containing those types. - If a string argument is passed to a parameter annotated with a non-string type, STATEK looks for a matching variable in the job context and passes that live value when it can.
- Parameters annotated as
strkeep the literal string. A value such as"user"is not rebound when the tool asks for a string.
That binding makes model code more forgiving when the model refers to a visible Python variable by name instead of passing the object directly:
class User:
...
@tool
def send_to(recipient: User, message: str, **kwargs):
"""Send a message to a user."""
return send_message(recipient, message)send_to("user", "Your meeting moved to 3 PM.")If the job has a local variable named user, STATEK can bind the first argument to that object because recipient expects User. The second argument stays a literal string because message is annotated as str.
Return values are formatted into tool output for the model. If a tool returns None, there is no tool output. If it returns an object with an application-specific representation, STATEK can show a useful representation instead of a raw memory address.
Most importantly, tools can return real Python values:
slot = find_empty_slot(calendar, after=meeting.ends_at)
meeting = move_meeting(meeting, slot)The job can keep slot and meeting in its durable Python state.
Errors
Tool errors are captured as tool results. If a requested tool name is not found, STATEK returns a NameError-style result. If the tool raises an exception, the exception type and message are captured for the agent and job history.
Conceptually:
slot = find_empty_slot(calendar, after=meeting.ends_at)If find_empty_slot raises ValueError("no slot available"), the job records that tool error instead of silently losing the failure. The agent or an operator can inspect the job history and decide what should happen next.
Tool-level error handlers exist for cleanup and follow-up handling. Pass a valid @error_handler function as @tool(error_handler=...) when the tool result should register cleanup work on the current job. If the tool returns a FutureResult, registration is deferred until the future value is resolved.
Most application docs should still start with explicit error returns, clear side-effect design, and inspectable job history. See Error Handling for the job-critical cleanup pattern.
System, Application, and Hidden Tools
STATEK distinguishes tool visibility and scope.
Application tools are the capabilities you expose for your domain:
@tool
def find_empty_slot(calendar, after, **kwargs):
"""Find an available slot in a calendar."""
return calendar.find_empty_slot(after=after)System tools are framework-level or infrastructure tools:
@tool(system=True)
def describe_object(what, **kwargs):
"""Describe an object for the agent."""
return render_description(what)Hidden tools remain callable internally but are excluded from LLM-facing discovery, prompt tool placeholders, and formal provider tool payloads:
@tool(hidden=True)
def _load_calendar_adapter(**kwargs):
"""Load an internal adapter."""
return adapterUse hidden tools for plumbing and internal support functions. Use visible tools only when the model should know the capability exists and choose when to call it.
Tool selection can also be narrowed by metadata and chat style. That is useful when one agent has many possible capabilities but a specific request should expose only a smaller set. See Chat Styles for how Python tools, formal provider tool calls, and the python_cli system tool fit together.
Prompt visibility is not an authorization boundary. In sandboxed execution, tool access should go through capability wrappers or a broker that checks the allowed tool set, blocks hidden or internal tools unless explicitly permitted, validates arguments and return values, and preserves STATEK tool logging and error-handler behavior.
Side Effects
Side-effecting tools should make the boundary obvious.
@tool
def move_meeting(meeting, new_slot, **kwargs):
"""Move a meeting after application-level permission checks.
Args:
meeting: Meeting to move.
new_slot: Approved destination slot.
Returns:
The updated meeting.
"""
meeting.move_to(new_slot)
return meetingThis tool changes durable application state if meeting is dbzero-backed. That is powerful, but it should be designed like any other write path in your application.
Side-effecting tools need authorization, idempotency, auditability, secrets discipline, resource limits, and application-level approval rules. STATEK records durable job history, but it does not automatically make external side effects safe or reversible. See Security for tool and credential boundaries.
Practical Design Rules
Prefer small, named tools over broad generic access. A tool named move_meeting is easier to inspect and permission than a tool named run_any_calendar_operation.
Return useful Python values. If the next step needs the updated meeting, return the meeting. If the next step needs a list of candidate slots, return those slots.
Keep reads close to the object model. If calendar.events_for(today) is already a safe Python method, the agent can call it directly when calendar is in context.
Put risky operations behind tools. External API calls, writes, notifications, payments, file changes, and long-running work should go through explicit functions with clear docstrings and application checks.
Use hidden tools and internal context for plumbing. Do not expose support functions to the model unless they are meaningful capabilities.
When tools or context objects are documented for the model, use Agent-Exposed Docstring Conventions to keep prompt placeholders, brief(...), and docstr(...) aligned.
How Tools Fit Into Jobs
When a tool runs inside a job, it runs against that job's Python context. It can receive objects from local variables, return new objects, and participate in the durable execution history.
events = calendar.events_for(today)
meeting = calendar.find_meeting("Weekly planning", day=today)
slot = find_empty_slot(calendar, after=meeting.ends_at)
updated = move_meeting(meeting, slot)
print(updated.starts_at)On the next step, the same job can reuse events, meeting, slot, and updated. That is the core STATEK pattern: agents execute Python, tools provide controlled capabilities, and dbzero-backed objects make application state durable.
Where to go next
Read Jobs for how tool results persist in a job, Chat Styles for Python tools versus provider tools, Security for side-effect boundaries, and API Reference for the tool decorator entry.