Index
tools
AXM shared tool interfaces.
Re-exports
AXMTool: Abstract base class for deterministic tools. ToolResult: Immutable result of a tool execution. ToolMetadata: Resolved facade/CLI discovery metadata for a tool. tool_metadata: Read a tool's optional discovery attributes. tool_node: Adapt an AXMTool into a DAG python-node function. ToolNodeError: Raised when a tool invoked as a node fails (fail-fast).
AXMTool
Bases: Protocol
Structural protocol for AXM deterministic tools.
Implementors must provide:
- name (property): unique tool identifier
- execute(...) : deterministic execution with explicit params
Optionally provide:
- agent_hint (class attribute): optional, free-form one-liner
optimized for LLM consumption: what the tool does, key params, and
what it replaces. Like the other discovery attributes below, it is
not a protocol member and carries no guaranteed fallback — some
discovery tooling reads it best-effort via getattr /
:func:tool_metadata; absent, nothing is substituted.
- expose_directly (class attribute, default False): when
True, the MCP server registers this tool directly in
tools/list (the hot path). When False (default), the
tool is reachable only through the MCP facade
(axm_search -> axm_describe -> axm_call), keeping the
tools/list payload small.
- domain (class attribute, default None): coarse capability
group used by the facade for axm_capabilities and to scope
axm_search (e.g. "ast", "git", "ticket").
- tags (class attribute, default frozenset()): free-form
keywords feeding facade discovery (axm_search).
Uses structural typing (PEP 544) — no inheritance required.
@runtime_checkable enables isinstance() checks. The discovery
attributes (expose_directly / domain / tags) are not
protocol members on purpose: adding data attributes to a
runtime_checkable protocol would make them required for
isinstance() and break the check for the many tools that satisfy
AXMTool structurally without subclassing it. Read them through
:func:tool_metadata (or getattr(tool, name, default)) instead,
which works for subclasses and structural tools alike.
Example::
class MyTool(AXMTool):
agent_hint = "Frobnicate widgets — use width param."
expose_directly = True # hot path (read via tool_metadata)
domain = "widget"
tags = frozenset({"frobnicate"})
@property
def name(self) -> str:
return "my-tool"
def execute(self, *, value: int = 0) -> ToolResult:
return ToolResult(success=True, data={"result": value})
Source code in packages/axm/src/axm/tools/base.py
name
property
Unique tool identifier (e.g., 'esbmc', 'dafny', 'pytest').
execute(**kwargs)
Execute the tool with given arguments.
Subclasses should override with explicit, typed parameters::
def execute(self, *, title: str = "", body: str = "") -> ToolResult:
...
The **kwargs signature here is the structural minimum —
any callable accepting keyword arguments satisfies it.
Returns:
| Type | Description |
|---|---|
ToolResult
|
ToolResult with success status and structured data. |
Source code in packages/axm/src/axm/tools/base.py
ToolMetadata
dataclass
Facade/CLI discovery metadata for a tool, with safe defaults.
Built by :func:tool_metadata from the optional expose_directly /
domain / tags attributes a tool may declare. Centralising the
defaults here keeps the MCP facade and the CLI reading the same contract.
Attributes:
| Name | Type | Description |
|---|---|---|
expose_directly |
bool
|
Whether the tool is on the MCP hot path
(registered directly in |
domain |
str | None
|
Coarse capability group, or |
tags |
frozenset[str]
|
Discovery keywords (possibly empty). |
Source code in packages/axm/src/axm/tools/base.py
ToolNodeError
ToolResult
dataclass
Immutable result of a tool execution.
Attributes:
| Name | Type | Description |
|---|---|---|
success |
bool
|
Whether the tool execution succeeded. |
data |
dict[str, Any]
|
Structured output data (backend-specific). |
error |
str | None
|
Human-readable error message, if any. |
hint |
str | None
|
Optional next-step suggestion for the agent. |
text |
str | None
|
Optional pre-rendered text representation (e.g. Markdown). |
Source code in packages/axm/src/axm/tools/base.py
tool_metadata(tool)
Read a tool's optional discovery attributes into a :class:ToolMetadata.
Works for tools that subclass :class:AXMTool, tools that satisfy it
structurally, and plain callables — anything missing an attribute falls
back to the default. tags is coerced to a frozenset so callers
can rely on set semantics regardless of how the tool declared it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tool
|
object
|
The tool instance (or plain callable) to introspect. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
A |
ToolMetadata
|
class: |
Source code in packages/axm/src/axm/tools/base.py
tool_node(name, *, args=None, returns=None)
Build a DAG python-node fn(payload) -> dict around an axm.tools tool.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
The tool's |
required |
args
|
Mapping[str, str] | None
|
Optional |
None
|
returns
|
Mapping[str, str] | None
|
|
None
|
Returns:
| Type | Description |
|---|---|
Callable[[Mapping[str, object]], dict[str, object]]
|
A callable mapping the node's |
Raises:
| Type | Description |
|---|---|
ToolNodeError
|
At call time, if the tool is unknown or returns
|