windlass.interfaces.tool¶
tool
¶
The tool interface.
A tool is a capability an agent can invoke: a Python function, an HTTP call, a database query, a remote MCP endpoint. Windlass represents all of them with this one class, so an agent binding a local function and a remote MCP tool cannot tell the difference.
Most users never subclass this — the :func:~windlass.tools.tool decorator wraps
a plain function, derives the JSON schema from its type hints and docstring, and
registers it. Subclass :class:Tool when the capability is not a function:
stateful clients, dynamically generated tools, or remote proxies.
Implementers override one coroutine, :meth:Tool.acall.
Example
from windlass.tools import tool @tool ... def add(a: int, b: int) -> int: ... '''Add two numbers.''' ... return a + b add.schema()["function"]["name"] 'add' add.run(a=1, b=2).content '3'
Tool
¶
Tool(
*,
name: str,
description: str = "",
parameters: dict[str, Any] | None = None,
timeout: float | None = None,
requires_approval: bool = False,
**config: Any
)
Bases: Component
Abstract agent-callable capability.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Tool name the model sees. Must match |
required |
description
|
str
|
What the tool does. This is prompt text — the model chooses tools based on it, so vague descriptions cause bad calls. |
''
|
parameters
|
dict[str, Any] | None
|
JSON Schema for the arguments. Derived automatically by the decorator; supply it manually when subclassing. |
None
|
timeout
|
float | None
|
Seconds before the call is abandoned. |
None
|
requires_approval
|
bool
|
When True the agent pauses for human approval before invoking it. Use this for anything that spends money or mutates state. |
False
|
**config
|
Any
|
Tool-specific options. |
{}
|
Attributes:
| Name | Type | Description |
|---|---|---|
description |
The model-facing description. |
|
parameters |
dict[str, Any]
|
The JSON Schema for arguments. |
requires_approval |
Whether human-in-the-loop approval is required. |
Example
Implementing a stateful tool::
class SearchTool(Tool):
def __init__(self, client):
super().__init__(
name="search",
description="Search the product catalogue.",
parameters={
"type": "object",
"properties": {"q": {"type": "string"}},
"required": ["q"],
},
)
self.client = client
async def acall(self, **kwargs):
return await self.client.search(kwargs["q"])
Source code in src\windlass\interfaces\tool.py
acall
abstractmethod
async
¶
Execute the tool.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**kwargs
|
Any
|
Arguments matching :attr: |
{}
|
Returns:
| Type | Description |
|---|---|
Any
|
Any JSON-serialisable value. It is rendered to a string for the |
Any
|
model and kept intact on :attr: |
Raises:
| Type | Description |
|---|---|
Exception
|
Anything. The agent catches it and reports the failure back to the model rather than crashing the run. |
Source code in src\windlass\interfaces\tool.py
schema
¶
Return the tool definition in a provider's format.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
style
|
str
|
|
'openai'
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
A dict ready to drop into that provider's request. |
Raises:
| Type | Description |
|---|---|
ValueError
|
For an unknown style. |
Example
from windlass.tools import tool @tool ... def ping() -> str: ... '''Ping.''' ... return "pong" ping.schema(style="anthropic")["name"] 'ping'
Source code in src\windlass\interfaces\tool.py
ainvoke
async
¶
Execute a model-issued tool call and wrap the outcome.
Failures are captured rather than raised: the agent needs to hand the error text back to the model so it can recover, which is impossible if the exception unwinds the loop.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
call
|
ToolCall
|
The call requested by the model. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
A |
ToolResult
|
class: |
ToolResult
|
when the tool raised or timed out. |
Performance
Enforces :attr:timeout. A hung tool fails the call, not the run.
Example
import asyncio from windlass.core.types import ToolCall from windlass.tools import tool @tool ... def echo(text: str) -> str: ... '''Echo text.''' ... return text asyncio.run(echo.ainvoke(ToolCall(name="echo", arguments={"text": "hi"}))).content 'hi'
Source code in src\windlass\interfaces\tool.py
invoke
¶
arun
async
¶
Execute the tool directly, outside an agent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**kwargs
|
Any
|
Tool arguments. |
{}
|
Returns:
| Type | Description |
|---|---|
ToolResult
|
The result. |
Raises:
| Type | Description |
|---|---|
ToolExecutionError
|
When the tool fails. Unlike :meth: |
Source code in src\windlass\interfaces\tool.py
run
¶
describe
¶
Return a JSON-safe summary including the schema.
Source code in src\windlass\interfaces\tool.py
render_result
¶
Render a tool's return value as text for the model.
Strings pass through; everything else is JSON-encoded when possible and
repr-ed otherwise. Keeping this in one place means every tool's output
looks the same to the model regardless of who wrote it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
Any
|
The tool's return value. |
required |
Returns:
| Type | Description |
|---|---|
str
|
A string safe to place in a |
Example
render_result({"a": 1}) '{"a": 1}' render_result("plain") 'plain'