Writing custom nodes.
Declare a node's interface with comment directives, implement its run function, and use the supported runtime rather than backend internals.
A user node is Python with comment-based schema directives and a top-level
run() function. The directives declare the pins and presentation that Studio
discovers. They are not Python decorators.
Design the boundary around the computation
A node accepts declared inputs and controls, computes its result and returns declared outputs. Keep names, types and output shape consistent with the implementation. Validate the definition before execution and handle missing or invalid input explicitly. A visual preview and a saved schema are different evidence from a tested computation.
The same value, factor and result names are highlighted in declarations, function arguments and the return map. The example inputs 3 and 2 multiply to the result 6 at the node’s interface.
A small deterministic node
# @node title="Scale a Number" category="Math" icon="calculator" preview=true
# @task category="Analyze" subcategory="Data" filter="Math"
# @help Multiply a number by a factor.
# @style minHeight=260 widgetSpacing=60
# @input float value label="Value" default=1.0
# @input float factor label="Factor" default=2.0
# @output float result label="Result"
def run(value, factor):
return {"result": value * factor}
Save it through the supported user-node editor or authoring tool, place it in a graph, and run a known input. A value of 3 and a factor of 2 should return 6. This example needs no provider key or additional package.
Preserve the contract
Use the declared pin names in the function and output dictionary. Every normal
return path must map output names to values. For an actionable failure, raise
an appropriate exception or return a useful @error; do not fabricate a
successful result when required work failed.
Node discovery requires valid metadata. Keep matching category/filter metadata, help, style, and correct pin declarations. Test after changing the interface; existing graphs can still refer to its earlier pin names.
Use the public runtime
User nodes have a restricted runtime. Built-in source can use privileges and
imports that user code does not have, so copying a built-in implementation may
need adaptation. Use supported injectables such as files, output, package,
log, and progress for the operations they provide. Do not depend on backend
modules, direct filesystem access, or internal library paths.
Request helpers by their exact supported parameter names. An undeclared helper name is not injected automatically and can make the function fail to run.
Declare external packages with supported @require metadata. Package approval
and runtime policy still apply. Web previews and custom widgets also need the
documented node-preview integration rather than unrestricted access to Studio.
Test before publishing
Test valid, empty, and invalid inputs and any failure path. For media, inspect a real produced file; an empty path or placeholder file is not a usable output. Keep keys and private data out of source, diagnostics, and bundled assets.
Separate schema, runtime, and behavior checks
Declare: use single-line comment directives, top-level run(), and output names matching the return mapping. Runtime: use exact supported helper names and managed file representations; dependencies still face policy. Test: verify real values/files and error paths, not just a successful save.
Checks and next steps
Keep each metadata directive on its own line and preserve exact pin names
when editing an existing interface. @require declares a dependency; it does
not bypass the installation's package allowlist or guarantee that a package
can run in the selected environment.
The outer return dictionary maps output names to values. A file-backed
object output needs the managed JSON-file representation; returning an
arbitrary Python dictionary as that pin's value is not the same contract.
Use the supported files helper and current data-type reference for the value.
Likewise, allocating an output path does not create valid media by itself.
Review generated-source diagnostics and implementation warnings, then test a small known example and failure cases. Static acceptance does not prove provider availability, media quality, complete implementation, or safe reuse with different inputs. See Node and data reference and Creating nodes with Penny.
Last reviewed against Kaitoi on 24 October 2026. View this page as Markdown