Skip to main content

Documentation Index

Fetch the complete documentation index at: https://qitor.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

The @function_tool decorator is the quickest path from a plain Python function to a QitOS tool. It inspects your function’s signature, type hints, and docstring, then builds a complete FunctionTool instance with an enriched ToolSpec — no subclassing required.

Step 1: Basic decorator usage

Apply the decorator without arguments. The function name becomes the tool name and the docstring becomes the description:
from qitos import function_tool

@function_tool
def greet(name: str) -> str:
    """Say hello to someone."""
    return f"Hello, {name}!"
You can also use it with parentheses when you need to pass parameters (covered in the next step):
@function_tool()
def greet(name: str) -> str:
    """Say hello to someone."""
    return f"Hello, {name}!"
Both forms return a FunctionTool instance with a populated spec that includes the parameter schema, required fields, and description.

Step 2: Decorator parameters

Pass keyword arguments to the decorator to override defaults and control execution policy:
from qitos import function_tool

@function_tool(
    name="search_files",
    description="Search for files matching a pattern",
    needs_approval=True,
    timeout_s=30.0,
    max_retries=2,
    read_only=True,
    concurrency_safe=True,
)
def search(pattern: str, max_results: int = 10) -> list[str]:
    """Search for files matching a pattern."""
    # ... implementation
    return []
The available parameters are:
ParameterTypeDefaultPurpose
namestrfunction nameOverride the tool name
descriptionstrdocstringOverride the tool description
needs_approvalboolFalseRequire human approval before execution
timeout_sfloatNoneMaximum execution time in seconds
max_retriesint0Number of automatic retries on failure
read_onlyboolFalseMark the tool as side-effect-free
concurrency_safeboolFalseSignal that the tool can run in parallel

Step 3: Type hints become ToolSpec parameters

The decorator inspects your function’s type hints and converts them into JSON Schema entries inside ToolSpec.parameters. Supported types include:
from typing import Literal, Optional
from qitos import function_tool

@function_tool
def configure(
    host: str,                        # {"type": "string"}
    port: int,                        # {"type": "integer"}
    verbose: bool = False,            # {"type": "boolean", "default": false}
    retries: Optional[int] = None,    # {"type": "integer", "nullable": true}
    mode: Literal["fast", "safe"] = "safe",  # {"type": "string", "enum": ["fast", "safe"]}
    tags: list[str] = [],             # {"type": "array", "items": {"type": "string"}}
) -> dict:
    """Configure the service.

    Args:
        host: The server hostname.
        port: The server port number.
        verbose: Enable verbose logging.
        retries: Optional retry count.
        mode: Execution mode.
        tags: List of tags.
    """
    return {"host": host, "port": port, "mode": mode}
Parameters without defaults are marked as required. Parameter descriptions are extracted from the Google-style Args: section of the docstring.

Step 4: Registering function tools in ToolRegistry

Once you have a FunctionTool, register it with a ToolRegistry so agents can discover and call it:
from qitos import ToolRegistry, function_tool

@function_tool(read_only=True)
def lookup_user(user_id: str) -> dict:
    """Look up a user by ID."""
    return {"id": user_id, "name": "Alice"}

registry = ToolRegistry()
registry.register(lookup_user)

# The tool is now available by name
descriptions = registry.get_tool_descriptions()
You can also register multiple tools at once and override names:
@function_tool
def create_user(name: str) -> dict:
    """Create a new user."""
    return {"name": name}

@function_tool
def delete_user(user_id: str) -> dict:
    """Delete a user by ID."""
    return {"deleted": user_id}

registry.register(create_user)
registry.register(delete_user, name="remove_user")

Step 5: Comparing @function_tool with class-based BaseTool

QitOS offers two ways to define tools. Use the one that fits your needs: @function_tool — best for simple, stateless operations:
from qitos import function_tool

@function_tool(read_only=True)
def get_time() -> str:
    """Return the current time."""
    from datetime import datetime
    return datetime.now().isoformat()
BaseTool subclass — best for stateful tools or complex initialization:
from qitos import BaseTool, ToolSpec, ToolMeta

class GetTimeTool(BaseTool):
    def __init__(self):
        meta = ToolMeta(name="get_time", read_only=True)
        spec = ToolSpec(
            name="get_time",
            description="Return the current time.",
            parameters={},
            required=[],
        )
        super().__init__(spec)

    def execute(self, **kwargs) -> str:
        from datetime import datetime
        return datetime.now().isoformat()
When to choose which:
Concern@function_toolBaseTool subclass
Simple functionsBest fitOverkill
State or config in __init__Not supportedSupported
Custom execute logicN/AFull control
Auto schema from signatureAutomaticManual
BoilerplateMinimalMore
Most tools are simple functions. Start with @function_tool and reach for BaseTool only when you need constructor state or custom execution behavior.

What’s next

MCP Integration

Bridge external MCP server tools into QitOS using the same FunctionTool interface.

Build your first agent

Use your tools inside an AgentModule with a real LLM loop.