Tools
sectr.tools is the framework-free tool story: plain functions become
tools with a JSON schema derived from their signature, and approval gating
is evaluated locally — no runtime round-trip to the platform.
from sectr.tools import tool
@toolasync def lookup_order(order_id: str) -> dict[str, str]: """Look up the status of an order.""" return {"order_id": order_id, "status": "shipped"}
@tool(needs_approval=True)async def refund(order_id: str) -> str: """Refund an order (requires manager approval).""" return f"Refunded {order_id}"(Snippet: examples/raw/tools.py — the conformance fixture.)
What @tool builds
Section titled “What @tool builds”name— from the function name (@tool(name="…")to override).description— from the docstring (description=…to override).openai_schema— a chat-completionstools=[…]entry whoseparametersJSON schema is derived from the signature’s type annotations (un-annotated parameters accept any JSON; parameters with defaults are optional). Feed it to your provider’s tool list:[t.openai_schema for t in TOOLS].- Calling a
Toolawaits the function if it’s async.
Approval gating
Section titled “Approval gating”A tool is gated when either applies:
- Code marking —
@tool(needs_approval=True). - Platform policy — the deployment config (
SECTR_APPROVAL_POLICY, a JSON list of tool names the sidecar injects) can gate a tool the author forgot to mark. This is what makes “refund requires approval” enforceable without re-deploying code.
A gated call raises sectr.tools.ApprovalRequired, which the adapter
converts into the final ToolApprovalRequired event — the turn suspends.
See approvals for the full flow, and
SessionContext.execute for how the decision
returns.
is_gated(name) and get_tool(name) are the lookup half of the registry —
mostly adapter machinery, exposed for completeness.