Skip to content

Tool Specs

ToolSpec and the @tool decorator wrap plain Python functions with typed metadata — name, description, and a JSON-schema parameter definition. Agents accept ToolSpec objects exactly like raw callables, and you can export them to provider tool-schema formats with one call.

Quick start

from brain.patterns import tool, ReActAgent
from brain.providers import LocalEchoProvider

@tool("Search local notes for relevant information")
def search(query: str) -> str:
    return f"[results: {query}]"

# Drop straight into any agent — no changes needed
agent = ReActAgent(tools={"search": search}, provider=LocalEchoProvider())
result = agent.run("Summarize the AI safety notes")

@tool decorator

from brain.patterns import tool

# Minimal — infers parameter schema from type hints
PAGES = {
    "local://handbook": "SecondBrain tools should be offline-safe by default.",
}

@tool("Fetch the contents of a local fixture page")
def fetch_page(url: str, max_chars: int = 2000) -> str:
    return PAGES.get(url, "Not found")[:max_chars]

# Override with a hand-crafted schema
@tool("Execute a database query", parameters={
    "type": "object",
    "properties": {
        "sql": {"type": "string", "description": "SQL SELECT statement"},
        "limit": {"type": "integer", "description": "Row limit", "default": 100},
    },
    "required": ["sql"],
})
def run_query(sql: str, limit: int = 100) -> str:
    return db.execute(sql, limit=limit)

# Custom name
@tool("Internal alias", name="my_tool")
def my_function(x: str) -> str:
    return x

Type-hint → JSON-schema mapping:

Python type JSON schema type
str "string"
int "integer"
float "number"
bool "boolean"
list "array"
dict "object"
(no hint) "string"

Schema export

# Provider function-calling format
schema = search.to_openai_schema()
# {
#   "type": "function",
#   "function": {
#     "name": "search",
#     "description": "Search the web for current information",
#     "parameters": {"type": "object", "properties": {"query": {"type": "string"}}, "required": ["query"]}
#   }
# }

# Anthropic-compatible tool-use format
schema = search.to_anthropic_schema()
# {"name": "search", "description": "...", "input_schema": {...}}

Bulk helpers

from brain.patterns import tools_to_dict, tools_to_openai_schemas

specs = [search, fetch_page, run_query]

# Convert to {name: callable} dict for agents
tools_dict = tools_to_dict(specs)
agent = ReActAgent(tools=tools_dict)

# Export all schemas for a provider payload
schemas = tools_to_openai_schemas(specs)
assert schemas[0]["type"] == "function"

ToolSpec direct construction

from brain.patterns import ToolSpec

calculator = ToolSpec(
    name="calculator",
    fn=lambda expr: str(eval(expr)),
    description="Evaluate arithmetic expressions",
    parameters={
        "type": "object",
        "properties": {"expr": {"type": "string"}},
        "required": ["expr"],
    },
)

print(calculator("2 + 2"))           # "4"
print(calculator.to_openai_schema()) # {...}

Full example: ReAct with typed tools

from pathlib import Path

from brain.patterns import ReActAgent, tool, tools_to_dict
from brain.providers import LocalEchoProvider

@tool("Search local notes")
def local_search(query: str) -> str:
    notes = {
        "transformer": "Transformer architectures use attention over token sequences.",
        "rag": "RAG combines retrieval with generation.",
    }
    return notes.get(query.lower(), "Not found")

@tool("Read a file from the filesystem")
def read_file(path: str) -> str:
    return Path(path).read_text()

@tool("Look up a fact in the knowledge base")
def kb_lookup(topic: str, namespace: str = "global") -> str:
    return knowledge_base.get(namespace, {}).get(topic, "Not found")

agent = ReActAgent(
    tools=tools_to_dict([local_search, read_file, kb_lookup]),
    provider=LocalEchoProvider(),
)
result = agent.run("Summarize transformer architecture notes")
print(result.answer)

API Reference

brain.patterns.tools.ToolSpec dataclass

ToolSpec(name: str, fn: Callable[..., Any], description: str, parameters: dict[str, Any] = dict())

A callable tool with typed metadata.

Attributes:

Name Type Description
name str

Unique identifier used by the agent (e.g. "search").

fn Callable[..., Any]

The underlying Python callable.

description str

Human-readable description shown to the LLM.

parameters dict[str, Any]

JSON-schema parameters object describing inputs.

__call__

__call__(*args: Any, **kwargs: Any) -> Any
Source code in brain/patterns/tools.py
def __call__(self, *args: Any, **kwargs: Any) -> Any:
    return self.fn(*args, **kwargs)

to_openai_schema

to_openai_schema() -> dict[str, Any]

Return an OpenAI / Anthropic function-calling schema dict.

Compatible with openai.ChatCompletion tools parameter and Anthropic tools parameter.

Returns:

Type Description
dict[str, Any]

dict with keys type, function (name, description,

dict[str, Any]

parameters).

Source code in brain/patterns/tools.py
def to_openai_schema(self) -> dict[str, Any]:
    """Return an OpenAI / Anthropic function-calling schema dict.

    Compatible with ``openai.ChatCompletion`` ``tools`` parameter and
    Anthropic ``tools`` parameter.

    Returns:
        dict with keys ``type``, ``function`` (name, description,
        parameters).
    """
    return {
        "type": "function",
        "function": {
            "name": self.name,
            "description": self.description,
            "parameters": self.parameters,
        },
    }

to_anthropic_schema

to_anthropic_schema() -> dict[str, Any]

Return an Anthropic tool-use schema dict.

Returns:

Type Description
dict[str, Any]

dict with keys name, description, input_schema.

Source code in brain/patterns/tools.py
def to_anthropic_schema(self) -> dict[str, Any]:
    """Return an Anthropic tool-use schema dict.

    Returns:
        dict with keys ``name``, ``description``, ``input_schema``.
    """
    return {
        "name": self.name,
        "description": self.description,
        "input_schema": self.parameters,
    }

to_dict

to_dict() -> dict[str, Any]

Plain dict representation (useful for JSON serialisation).

Source code in brain/patterns/tools.py
def to_dict(self) -> dict[str, Any]:
    """Plain dict representation (useful for JSON serialisation)."""
    return {
        "name": self.name,
        "description": self.description,
        "parameters": self.parameters,
    }

brain.patterns.tools.tool

tool(description: str, *, name: str | None = None, parameters: dict[str, Any] | None = None) -> Callable[[Callable[..., Any]], ToolSpec]

Decorator that wraps a function as a :class:ToolSpec.

Parameters:

Name Type Description Default
description str

Human-readable description shown to the LLM.

required
name str | None

Override the tool name (default: function __name__).

None
parameters dict[str, Any] | None

Override the JSON-schema parameters object. If omitted, parameters are inferred from type hints.

None

Returns:

Name Type Description
A Callable[[Callable[..., Any]], ToolSpec]

class:ToolSpec instance that is also callable.

Example::

@tool("Search the web for current information")
def search(query: str) -> str:
    return requests.get(f"https://api.search.com?q={query}").text

@tool("Look up a value in the database", parameters={
    "type": "object",
    "properties": {"key": {"type": "string"}},
    "required": ["key"],
})
def db_lookup(key: str) -> str:
    return db[key]
Source code in brain/patterns/tools.py
def tool(
    description: str,
    *,
    name: str | None = None,
    parameters: dict[str, Any] | None = None,
) -> Callable[[Callable[..., Any]], ToolSpec]:
    """Decorator that wraps a function as a :class:`ToolSpec`.

    Args:
        description: Human-readable description shown to the LLM.
        name: Override the tool name (default: function ``__name__``).
        parameters: Override the JSON-schema ``parameters`` object.
            If omitted, parameters are inferred from type hints.

    Returns:
        A :class:`ToolSpec` instance that is also callable.

    Example::

        @tool("Search the web for current information")
        def search(query: str) -> str:
            return requests.get(f"https://api.search.com?q={query}").text

        @tool("Look up a value in the database", parameters={
            "type": "object",
            "properties": {"key": {"type": "string"}},
            "required": ["key"],
        })
        def db_lookup(key: str) -> str:
            return db[key]
    """

    def decorator(fn: Callable[..., Any]) -> ToolSpec:
        resolved_name = name or fn.__name__
        resolved_params = parameters if parameters is not None else _infer_parameters(fn)
        spec = ToolSpec(
            name=resolved_name,
            fn=fn,
            description=description,
            parameters=resolved_params,
        )
        # Preserve function metadata for introspection
        spec.__name__ = resolved_name  # type: ignore[attr-defined]
        spec.__doc__ = fn.__doc__
        return spec

    return decorator

brain.patterns.tools.tools_to_dict

tools_to_dict(specs: list[ToolSpec]) -> dict[str, Callable[..., Any]]

Convert a list of ToolSpecs to the {name: callable} dict that agents accept.

Parameters:

Name Type Description Default
specs list[ToolSpec]

List of :class:ToolSpec objects.

required

Returns:

Type Description
dict[str, Callable[..., Any]]

Dict mapping tool name → callable.

Source code in brain/patterns/tools.py
def tools_to_dict(specs: list[ToolSpec]) -> dict[str, Callable[..., Any]]:
    """Convert a list of ToolSpecs to the ``{name: callable}`` dict that
    agents accept.

    Args:
        specs: List of :class:`ToolSpec` objects.

    Returns:
        Dict mapping tool name → callable.
    """
    return {s.name: s for s in specs}

brain.patterns.tools.tools_to_openai_schemas

tools_to_openai_schemas(specs: list[ToolSpec]) -> list[dict[str, Any]]

Convert a list of ToolSpecs to OpenAI function-calling schemas.

Parameters:

Name Type Description Default
specs list[ToolSpec]

List of :class:ToolSpec objects.

required

Returns:

Type Description
list[dict[str, Any]]

List of OpenAI-compatible schema dicts.

Source code in brain/patterns/tools.py
def tools_to_openai_schemas(specs: list[ToolSpec]) -> list[dict[str, Any]]:
    """Convert a list of ToolSpecs to OpenAI function-calling schemas.

    Args:
        specs: List of :class:`ToolSpec` objects.

    Returns:
        List of OpenAI-compatible schema dicts.
    """
    return [s.to_openai_schema() for s in specs]