Sandbox — LSD is in active testing. All systems operational·payments are test-mode (no real charges).
Overview Use Cases News Docs Tool Store Support Community Get LSD
All docs
Docs/ Building Tools/ The Tool object
Building Tools

The Tool object

Every LSD package has one Tool at module scope — it names your nodes, collects each decorated step, and mounts them into the host at load.

TL;DRA Tool is the single registry object for a package. Create one at module scope, decorate functions with @tool.load / shape / deliver, and each becomes a node whose identity is "toolname.funcname". The loader calls tool.mount() to surface commands, views and actions into the host.

A Tool is the one registry object a package hands to LSD. You create it once, at module scope, and every @tool.* decorator you write attaches a node, command, or view to it.

Usage#

Python
from lsdtools import Tool, Table

tool = Tool("hello", label="Hello")

@tool.load
def numbers(n: int = 5) -> Table:
    return Table({"i": list(range(n))})
# → a node "hello.numbers" appears in the add-node palette

Tool("hello") sets the package's name; label="Hello" is the human title shown in the UI (it defaults to the name).

One Tool per package#

Create exactly one Tool per package, at module scope — never inside a function. Its name is the namespace for everything the package contributes, so a second Tool would split your nodes across two namespaces. Decorators register their target the moment the module is imported, so all a package needs to expose a node is the decorated function sitting under tool.

Node identity#

Every step's identity is f"{tool_name}.{fn_name}" — the tool name joined to the decorated function's name:

Python
tool = Tool("hello")

@tool.shape
def top(t: Table, limit: int = 3) -> Table: # identity → "hello.top"
    return t.sort("i", descending=True).head(limit)

This string is stable: it keys the node in saved projects, in the palette, and in cross-package references. Node variants use it as the target of overrides=, and renaming a function changes the identity — so treat function names as part of your public surface.

Mounting#

Steps and entity types register at decoration time. The interactive surfaces — commands, views (sidebar, overlay, and editor placements), actions, and templates — are collected on the Tool and flushed into the host by tool.mount():

Python
tool.mount(host, package="hello") # the loader calls this for you

The package loader calls mount() when your package loads, attributing every contribution to the package id so it can be cleanly removed later. You only call mount() by hand in a standalone script or a test harness. tool.dispatch(route, ...) and tool.contribute(...) round out the object — see Commands & actions.

LearnParameters vs ports · Load steps · Node variants

APITool · Context · Table

ExamplesYour first tool · A complete package

Frequently asked questions

How many Tool objects should a package have?

Exactly one, created at module scope. Its name namespaces every node in the package, so a second Tool would fragment your node identities and confuse the loader.

Do I have to call tool.mount() myself?

No. The package loader calls mount() when the package is loaded, attributing every contribution to the package so it can be cleanly unmounted. You only call it by hand in a standalone script or a test.

By LSD Team · Last updated Jul 18, 2026 Ask on Discord View as Markdown