Tools
In shortToolNode, ToolResult, and utilities for tool registration and execution.
- 6 min read
- 9 sections
- Updated
- v0.10.0
- Markdown
ToolNode is a unified registry and executor for Python functions and MCP tools. It automatically generates JSON schemas from type annotations, executes tool calls in parallel, handles errors, and publishes events. Pair it with Agent to give LLMs access to callable tools, or use the utilities and decorators to define, inspect, and manage tool metadata.
Import paths
from tenxgraph.core.graph import ToolNode
from tenxgraph.core.state import ToolResult
from tenxgraph.core.graph.tool_node import UnsupportedToolParameterError, HAS_MCP, HAS_FASTMCP
from tenxgraph.utils import tool, get_tool_metadata, has_tool_decoratorToolNode
A unified registry and executor for callable tools from multiple sources (local functions, MCP servers).
Constructor
ToolNode(tools, client=None, pass_user_info_to_mcp=False)| Parameter | Type | Default | Description |
|---|---|---|---|
tools |
Iterable[Callable] |
required | Local Python functions to register. Each is registered under its __name__. Pass an empty list when only serving MCP tools. |
client |
fastmcp.Client | None |
None |
MCP client for remote tool access. Requires pip install "10xgraph[mcp]". |
pass_user_info_to_mcp |
bool |
False |
Forward the run config’s user dict to MCP tool calls as request metadata, readable on the server. |
Raises: TypeError when an item in tools is not callable; ImportError when a client is given but the MCP packages are not installed.
Methods
| Method | Signature | Returns | Description |
|---|---|---|---|
invoke |
await invoke(name: str, args: dict, tool_call_id: str, config: dict, state: AgentState) |
Message |
Execute a single tool call by name. Returns a Message with the result or error information (the signature also allows a dict). |
all_tools |
await all_tools(tags: set[str] | None = None, config: dict | None = None) |
list[dict] |
Async method returning JSON schemas for all registered tools, optionally filtered by tags. |
all_tools_sync |
all_tools_sync(tags: set[str] | None = None, config: dict | None = None) |
list[dict] |
Synchronous version of all_tools. |
add_tool |
add_tool(tool: Callable) |
None |
Register one more function after construction. Raises TypeError if it is not callable. |
Example
from tenxgraph.core.graph import Agent, ToolNode
def lookup_order(order_id: str) -> dict:
"""Look up an order by ID."""
return {"order_id": order_id, "status": "shipped"}
def refund_order(order_id: str, amount: float) -> dict:
"""Refund an order."""
return {"refund_id": "R-1", "amount": amount}
# Register local functions; each is exposed under its __name__
tool_node = ToolNode([lookup_order, refund_order])
# Inspect the JSON schemas the model will see
schemas = tool_node.all_tools_sync()
print(len(schemas)) # 2
# Add another tool after construction
def cancel_order(order_id: str) -> dict:
"""Cancel an order."""
return {"order_id": order_id, "status": "cancelled"}
tool_node.add_tool(cancel_order)
# Give the tools to an agent; also add tool_node to your StateGraph as a node
agent = Agent(model="gpt-4o", provider="openai", tool_node=tool_node)To wire the agent, the tool node and the routing edges into a graph, see Define custom tools with @tool.
ToolResult
Return type for tool functions that need to update graph state and return a message to the AI.
Constructor
ToolResult(message=None, state=None, is_error=False, *, content=None)| Parameter | Type | Default | Description |
|---|---|---|---|
message |
Any |
None |
The text response to return to the AI. content is accepted as an alias. |
state |
dict[str, Any] | None |
None |
Dict mapping state field names to new values. Only fields present are updated. |
is_error |
bool |
False |
If True, marks the result as a failed tool call (status=“failed”). |
content |
Any |
None |
Alias for message; ignored if message is provided. |
Example
from tenxgraph.core.state import ToolResult, AgentState
class MyState(AgentState):
jd_name: str = ""
def update_context(state: MyState, jd_name: str) -> ToolResult:
return ToolResult(
message=f"JD name updated to '{jd_name}'",
state={"jd_name": jd_name},
)tool decorator
Mark a function as a tool with metadata for schema generation and filtering.
Signature
@tool(
_func=None,
*,
name: str | None = None,
description: str | None = None,
tags: list[str] | set[str] | None = None,
provider: str | None = None,
capabilities: list[str] | None = None,
metadata: dict[str, Any] | None = None,
parameters: dict[str, Any] | None = None,
)| Parameter | Type | Default | Description |
|---|---|---|---|
name |
str | None |
None |
Tool name. If None, uses the function’s __name__. |
description |
str | None |
None |
Tool description. If None, uses the function’s docstring, or “No description provided.” when there is none. |
tags |
list[str] | set[str] | None |
None |
Tags for categorizing and filtering tools. Examples: ["search", "web"]. |
provider |
str | None |
None |
Provider or source of the tool (e.g., “local”, “mcp”, “composio”). |
capabilities |
list[str] | None |
None |
Capabilities or permissions required by the tool. Examples: ["read_files", "network_access"]. |
metadata |
dict[str, Any] | None |
None |
Additional arbitrary metadata for the tool. |
parameters |
dict[str, Any] | None |
None |
Explicit JSON Schema dict for parameters. Bypasses automatic schema generation. |
Returns: The decorated function with metadata attached as private attributes (_py_tool_*). The function behaves identically to the original.
Raises: ValueError if the decorated object is not callable or if parameters is provided but not a dict.
Example
from tenxgraph.core.graph import ToolNode
from tenxgraph.utils import tool
@tool(name="lookup_order", tags=["search", "orders"])
def find_order(order_id: str) -> dict:
"""Look up an order by ID."""
return {"order_id": order_id, "status": "shipped"}
@tool(tags=["write", "payments"])
async def refund_order(order_id: str, amount: float) -> dict:
"""Refund an order."""
return {"refund_id": "R-1", "amount": amount}
tool_node = ToolNode([find_order, refund_order])get_tool_metadata
Extract all tool metadata from a decorated function.
Signature
get_tool_metadata(func: Callable) -> dict[str, Any]| Parameter | Type | Description |
|---|---|---|
func |
Callable |
A function that may have been decorated with @tool. |
Returns: Dict with keys: name, description, tags (set), provider, capabilities, metadata, parameters. Unset values are None; tags is an empty set when unset.
Example
from tenxgraph.utils import tool, get_tool_metadata
@tool(name="my_tool", tags=["test"])
def example():
"""Example function."""
pass
metadata = get_tool_metadata(example)
print(metadata["name"]) # "my_tool"
print(metadata["tags"]) # {"test"}has_tool_decorator
Check if a function has been decorated with @tool.
Signature
has_tool_decorator(func: Callable) -> bool| Parameter | Type | Description |
|---|---|---|
func |
Callable |
The function to check. |
Returns: True if the function has been decorated with @tool, False otherwise.
Example
from tenxgraph.utils import tool, has_tool_decorator
@tool
def decorated():
pass
def not_decorated():
pass
print(has_tool_decorator(decorated)) # True
print(has_tool_decorator(not_decorated)) # FalseUnsupportedToolParameterError
Raised when a tool parameter annotation cannot be expressed as a portable JSON Schema.
Supported annotations are str, int, float, bool, common stdlib scalars (datetime, date, time, UUID, Path, Decimal, bytes), Optional, list, dict, Literal, Enum subclasses, pydantic models and dataclasses, and nestings of those. Anything else, including TypedDict, raises this error when schemas are built. Pass @tool(parameters=...) to supply a hand-written schema instead.
Subclasses TypeError so existing except TypeError handlers around tool registration continue to work.
Example
from tenxgraph.core.graph.tool_node import UnsupportedToolParameterError
from typing import TypedDict
from tenxgraph.core.graph import ToolNode
class Options(TypedDict):
verbose: bool
def bad_tool(options: Options) -> str:
"""A tool whose parameter (a TypedDict) is not supported."""
return "ok"
try:
# Schemas are built from annotations; unsupported ones raise this error
schemas = ToolNode([bad_tool]).all_tools_sync()
except UnsupportedToolParameterError as e:
print(f"Cannot register tool: {e}")HAS_MCP and HAS_FASTMCP
Boolean flags indicating whether the Model Context Protocol and FastMCP packages are installed.
Use these to conditionally enable MCP features without importing optional dependencies:
from tenxgraph.core.graph import ToolNode
from tenxgraph.core.graph.tool_node import HAS_MCP, HAS_FASTMCP
def my_function(text: str) -> str:
"""Echo text."""
return text
if HAS_FASTMCP and HAS_MCP:
from fastmcp import Client
client = Client("http://localhost:8000/mcp") # replace with your MCP server
tools = ToolNode([my_function], client=client)
else:
print('MCP support requires: pip install "10xgraph[mcp]"')
tools = ToolNode([my_function])Related guides
For task-focused guidance on using tools, see: