Skip to content

tool

Python · package toolnexus · SPEC §6 · python/src/toolnexus/native.py

def tool(
fn: Callable[..., Any] | None = None,
*,
name: str | None = None,
description: str | None = None,
input_schema: JSONSchema | None = None,
) -> Tool | Callable[[Callable[..., Any]], Tool]

The decorator form of define_tool — same inference, same ToolResult wrapping, same everything, just spelled @tool above a def you are writing right now. define_tool(fn, ..., source="native") is the implementation of tool; @tool calls it with source fixed to "native". The decorated function name is rebound to the resulting Tool, not the original callable.

You are authoring the function specifically to be a tool — a new capability for an agent, not a library function you happen to be wrapping. @tool reads top-to-bottom as “this is a tool”, right where the function is defined.

1. Bare @tool — name and description inferred

Section titled “1. Bare @tool — name and description inferred”
import asyncio
from toolnexus import tool
@tool
def convert_celsius(value: float) -> str:
"""Convert Celsius to Fahrenheit."""
return str(value * 9 / 5 + 32)
# `convert_celsius` IS the Tool now — the decorator rebinds the name.
assert convert_celsius.name == "convert_celsius"
assert convert_celsius.description == "Convert Celsius to Fahrenheit."
assert convert_celsius.source == "native"
assert convert_celsius.input_schema["required"] == ["value"]
async def main():
res = await convert_celsius.execute({"value": 100})
assert res.output == "212.0"
print("ok:", convert_celsius.name, "->", res.output)
asyncio.run(main())

2. @tool(...) with overrides, and calling the original function directly

Section titled “2. @tool(...) with overrides, and calling the original function directly”

The decorator’s parenthesized form takes the same overrides as define_tool — useful when the Python name is not the name you want the model to see, but you still want to call the plain function yourself elsewhere.

import asyncio
from toolnexus import tool
@tool(name="lookup_status", description="Look up an order's shipping status.")
def _status(order_id: str) -> str:
return f"{order_id}: in transit"
assert _status.name == "lookup_status"
assert _status.description == "Look up an order's shipping status."
# `_status` is the Tool object, not the function — call it through .execute.
async def main():
res = await _status.execute({"order_id": "A-42"})
assert res.output == "A-42: in transit"
print("ok:", _status.name, "->", res.output)
asyncio.run(main())

3. An explicit input_schema, and feeding decorated tools to an adapter

Section titled “3. An explicit input_schema, and feeding decorated tools to an adapter”

An explicit schema overrides inference — useful for an enum, a format, or per-property descriptions the type hints cannot express. Decorated tools are ordinary Tools, so they flow into to_openai (or any adapter) exactly like define_tool output.

import asyncio
from toolnexus import tool, to_openai
@tool(
input_schema={
"type": "object",
"properties": {"priority": {"type": "string", "enum": ["low", "high"]}},
"required": ["priority"],
"additionalProperties": False,
}
)
def set_priority(priority: str) -> str:
"""Set the priority of the current ticket."""
return f"priority set to {priority}"
assert set_priority.input_schema["properties"]["priority"]["enum"] == ["low", "high"]
schema = to_openai([set_priority])
assert schema[0]["function"]["name"] == "set_priority"
assert schema[0]["function"]["parameters"]["properties"]["priority"]["enum"] == ["low", "high"]
async def main():
res = await set_priority.execute({"priority": "high"})
assert res.output == "priority set to high"
print("ok:", res.output)
asyncio.run(main())

Identical to define_tool minus fn positioned as a decorator target rather than a first argument, and no source override — @tool always produces source="native".

Option Type What it does
name str | None Overrides fn.__name__.
description str | None Overrides the first docstring line.
input_schema JSONSchema | None Overrides inference entirely.

Inference rules (type hints → JSON-Schema, ctx/context exclusion, async vs. thread dispatch, return handling, error handling) are exactly define_tool’s — see that page for the full table.