Skip to content

aimu.tools

In-process @tool decorator and cross-process MCPClient.

Decorator

aimu.tools.tool

tool(func: Callable) -> Callable

Mark a Python function as an AIMU tool.

Inspects the signature and docstring at decoration time and attaches an OpenAI-format tool spec to func.__tool_spec__. The function itself is unchanged and remains directly callable.

Each parameter must either have a type hint or a default value. Variadic parameters (*args / **kwargs) are not supported; declare each argument explicitly.

Supported parameter types: str, int, float, bool, list, dict, plus Optional[T] and T | None (which unwrap to the inner type).

A tool may be plain (def fn() -> T), async (async def fn() -> T), a generator (def fn(): yield ...; return T), or an async generator (async def fn(): yield ...). Generator and async-generator tools stream :class:~aimu.models.StreamChunk objects during execution; the agent forwards each yielded chunk through its own stream and treats the final yielded TOOL_CALLING chunk's content["response"] as the canonical tool result. The decorator sets discriminator attributes:

  • func.__tool_is_async__: True for async def or async def + yield.
  • func.__tool_is_streaming__: True for generator functions (sync or async).

Usage::

import aimu

@aimu.tool
def letter_counter(word: str, letter: str) -> int:
    """Count occurrences of a letter in a word."""
    return word.lower().count(letter.lower())

agent = Agent(client, tools=[letter_counter])

aimu.tools.ToolSignatureError

Bases: TypeError

Raised by @tool when a function signature can't be converted to a tool spec.

Argument validation

aimu.tools.coerce_tool_arguments

coerce_tool_arguments(fn: Callable, arguments: dict) -> dict

Validate and lax-coerce model-supplied tool arguments against fn's type hints.

Returns a new dict of model-facing arguments coerced to their declared types ("5" -> 5, "true" -> True). Raises :class:ToolArgumentError with a single self-contained message when arguments are unknown, a required one is missing, or a value can't be coerced.

Callables not built by @tool (e.g. MCP as_tools() wrappers) carry no __tool_param_adapters__; their arguments pass through unchanged (the MCP server validates them).

aimu.tools.ToolArgumentError

Bases: ValueError

Raised when model-supplied tool-call arguments fail validation/coercion.

Caught at dispatch and surfaced to the model as a tool result so it can self-correct.

MCP client

aimu.tools.MCPClient

MCPClient(config: Optional[dict] = None, server: Optional[FastMCP] = None, file: Optional[str] = None)

Synchronous wrapper around an async FastMCP Client.

Uses anyio's start_blocking_portal() to run the FastMCP Client in a background thread with a properly initialized anyio event loop.

Pass exactly one of config, server, or file. Connection errors are re-raised as :class:MCPConnectionError with the original exception chained.

ping

ping() -> list

Verify the connection is alive by listing tools. Returns the tool list.

Raises :class:MCPConnectionError if the connection has been closed or the server is unreachable.

as_tools

as_tools() -> list

Return this server's tools as @tool-style callables.

Each callable closes over this client, invokes :meth:call_tool cross-process, and returns the result's text content as a string. The callables carry __tool_spec__ (OpenAI format), __tool_is_async__ = False, and __tool_is_streaming__ = False, so they drop straight into client.tools or Agent(tools=...) and dispatch through the same path as @tool functions, no model_client.mcp_client reference needed::

mcp = MCPClient(server=my_server)
client.tools = builtin.web + mcp.as_tools()

The tool list is a snapshot taken now (one list_tools() round-trip); call as_tools() again to pick up server-side changes. Keep a reference to this MCPClient (or to the returned callables, which hold one) for the lifetime of the connection.

aimu.tools.MCPConnectionError

Bases: RuntimeError

Raised when an :class:MCPClient fails to establish or use a connection.

Built-in tools

The aimu.tools.builtin module ships ready-made @tool functions grouped by domain:

Group Tools
builtin.web get_weather, get_webpage, web_search, wikipedia
builtin.fs list_directory, read_file
builtin.compute calculate
builtin.misc echo, get_current_date_and_time
builtin.ALL_TOOLS All of the above

aimu.tools.builtin.echo

echo(echo_string: str) -> str

Returns echo_string.

aimu.tools.builtin.get_current_date_and_time

get_current_date_and_time() -> str

Returns the current date and time.

aimu.tools.builtin.get_weather

get_weather(location: str) -> str

Returns the current weather for a given location.

Parameters:

Name Type Description Default
location str

City name or coordinates (e.g. "London", "48.8566,2.3522").

required

aimu.tools.builtin.calculate

calculate(expression: str) -> str

Evaluates a simple arithmetic expression and returns the result.

aimu.tools.builtin.get_webpage

get_webpage(url: str) -> str

Fetches a web page and returns its visible text content with HTML stripped.

When the page exposes a publication timestamp (in tags, JSON-LD, or a

Parameters:

Name Type Description Default
url str

The URL of the page to retrieve.

required
web_search(query: str, num_results: int = 5, time_range: str = '', categories: str = '') -> str

Search the web using a SearXNG instance and return the top results.

Each result includes its publication date when the search engine reports one (shown as a "Published:" line), useful for judging how recent an article is.

Parameters:

Name Type Description Default
query str

The search query string.

required
num_results int

Number of results to return (default 5).

5
time_range str

Optional recency filter, one of "day", "week", "month", or "year". Use "day" to restrict results to roughly the last 24 hours (best for fresh news).

''
categories str

Optional SearXNG category filter, e.g. "news" to restrict to news engines (which report publication dates far more reliably than general web engines). Comma-separated for multiple, e.g. "news,science".

''

Set SEARXNG_BASE_URL env var to point to your SearXNG instance (or a .env file) (default: http://localhost:8080).

aimu.tools.builtin.wikipedia

wikipedia(query: str) -> str

Fetches a Wikipedia article summary for the given query.

Parameters:

Name Type Description Default
query str

Article title or search phrase (e.g. "Albert Einstein", "general relativity").

required

aimu.tools.builtin.list_directory

list_directory(path: str) -> str

Lists files and subdirectories at the given path.

Parameters:

Name Type Description Default
path str

Directory path to list.

required

aimu.tools.builtin.read_file

read_file(path: str, max_lines: int = 200) -> str

Reads a local file and returns its contents, capped at max_lines lines.

Parameters:

Name Type Description Default
path str

Path to the file to read.

required
max_lines int

Maximum number of lines to return (default 200).

200

Tool factories

Bind a tool to a specific resource (a memory store, a knowledge base) instead of a process-wide singleton.

aimu.tools.builtin.make_memory_tools

make_memory_tools(store)

Build store_memory, search_memories, and list_memories tools bound to store.

store may be any :class:aimu.memory.MemoryStore implementation: :class:~aimu.memory.SemanticMemoryStore (ChromaDB vector search), :class:~aimu.memory.DocumentStore (path-keyed), or a custom subclass.

Unlike the image/audio/speech tools, there is no env-var singleton for memory because the choice of store (ephemeral vs. persistent, which persist_path) is meaningful and should be explicit. Construct the store, pass it here, and add the returned list to the agent::

store = SemanticMemoryStore(persist_path="./.memory")
agent = Agent(client, tools=make_memory_tools(store) + builtin.web)

For cross-process or multi-agent memory, use the FastMCP servers in aimu.memory.mcp / aimu.memory.document_mcp instead.

aimu.tools.builtin.make_retrieval_tool

make_retrieval_tool(store, *, n_results: int = 5)

Build a retrieve_context tool bound to store for retrieval-augmented agents.

store may be any :class:aimu.memory.MemoryStore (typically a :class:~aimu.memory.SemanticMemoryStore populated via :func:aimu.rag.ingest). The tool runs :func:aimu.rag.retrieve and returns the joined context, letting an agent fetch relevant background on demand::

from aimu.rag import ingest
store = SemanticMemoryStore()
ingest(store, my_documents)
agent = Agent(client, tools=[make_retrieval_tool(store)])

Like :func:make_memory_tools, there is no env-var singleton; the store (and what was ingested into it) is a meaningful, explicit choice.