> For the complete documentation index, see [llms.txt](https://vibemx.gitbook.io/neuronai-studio/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://vibemx.gitbook.io/neuronai-studio/extending/custom-node-types.md).

# Custom Node Types

Extend NeuronAI Studio with custom workflow node types by registering executors programmatically.

## Overview

Node types consist of:

1. **Metadata** — label, icon, category (canvas palette)
2. **Executor** — PHP class that runs the node at workflow runtime

```mermaid
flowchart LR
    Register[NeuronAIStudio::registerNode] --> Registry[NodeTypeRegistry]
    Registry --> Canvas[Canvas palette]
    Executor[NodeExecutor class] --> ExecRegistry[NodeExecutorRegistry]
    ExecRegistry --> Runtime[GraphExecutionLoop]
```

## Register a node type

In your `AppServiceProvider::boot()`:

```php
use DigitalElvis\NeuronAIStudio\Facades\NeuronAIStudio;
use App\Neuron\Nodes\SendEmailExecutor;

NeuronAIStudio::registerNode('send_email', SendEmailExecutor::class, [
    'label' => 'Send Email',
    'icon' => 'mail',
    'category' => 'integration',
]);
```

### Toolable meta (Tool Mode)

To allow authors to flip a custom node between Step and Tool Mode (same contract as Agent), set `toolable` and optional `tool_exposure` defaults in meta or `config/neuronai-studio.php` → `node_types`:

```php
NeuronAIStudio::registerNode('my_specialist', MySpecialistExecutor::class, [
    'label' => 'My Specialist',
    'icon' => 'bot',
    'category' => 'ai',
    'toolable' => true,
    'tool_exposure' => [
        'slug_prefix' => 'call_specialist',
        'default_description' => 'Delegate a task to this specialist.',
    ],
]);
```

`NodeTypeRegistry::forCanvas()` surfaces these fields to the editor. v1 runtime Tool Mode execution is implemented for **Agent** nodes; other toolable types can opt in later.

Also register the executor in `NodeExecutorRegistry` if not auto-wired:

```php
use DigitalElvis\NeuronAIStudio\Runtime\NodeExecutors\NodeExecutorRegistry;

$this->app->make(NodeExecutorRegistry::class)
    ->register('send_email', $this->app->make(SendEmailExecutor::class));
```

## Implement an executor

Extend the base executor pattern used by built-in nodes:

```php
namespace App\Neuron\Nodes;

use DigitalElvis\NeuronAIStudio\Runtime\NodeExecutors\NodeExecutor;

class SendEmailExecutor extends NodeExecutor
{
    public function execute(array $nodeData, $state, $context): array
    {
        $to = $nodeData['to'] ?? '';
        $subject = $nodeData['subject'] ?? '';

        // Your logic here — send email, call API, etc.

        $state->set($nodeData['output_key'] ?? 'email_sent', true);

        return ['handle' => 'default'];
    }
}
```

### Structured output in custom executors

To validate LLM responses against a typed output class in a custom AI node, inject `StructuredOutputResolver` and `AgentRunner`, then branch on the `structured` flag in node config:

```php
use DigitalElvis\NeuronAIStudio\Runtime\AgentRunner;
use DigitalElvis\NeuronAIStudio\Runtime\StructuredOutput\StructuredOutputResolver;

if ($nodeData['structured'] ?? false) {
    $class = $this->outputResolver->resolve((string) ($nodeData['output_class'] ?? ''));
    $result = $this->agentRunner->structuredInline($config, $userMessage, $class);
    $state->set($outputKey, $result->structured);
} else {
    // plain text path
}
```

Register output classes under `structured_output_scan_paths` so they appear in the canvas picker, or accept a fully qualified class name in node JSON. See [AI Nodes — Structured output](/neuronai-studio/workflows/ai-nodes.md#structured-output).

## Canvas configuration

Custom node fields appear in the inspector when you extend the React inspector components. For server-only nodes, users can edit raw JSON in the graph data until UI support is added.

## Built-in node types

See the registered types in `NeuronAIStudioServiceProvider::registerNodeTypes()`:

* start, stop, agent, llm, condition, set\_state, invoke, loop, tool, rag, delay, mcp, human, fork, join

Use these as reference implementations in `src/Runtime/NodeExecutors/`.

### Invoke vs custom node

For a one-off host callable, prefer the built-in **invoke** node: list the FQCN in `config('neuronai-studio.invoke_hooks')` and place an Invoke node on the canvas (`hook_class` + `output_key`). Register a full custom node type when you need a named palette entry, multiple output handles, checkpointing, or a dedicated inspector UX.

See [Logic Nodes — Invoke](/neuronai-studio/workflows/logic-nodes.md#invoke).

### The fork/join pattern

Parallel execution is implemented as a **pair** of cooperating nodes plus a helper runner — a useful template if you build fan-out/fan-in nodes:

* `ForkNodeExecutor` reads its branch edges (each non-`default` source handle is a branch), runs each branch subgraph through `ParallelBranchRunner` in an isolated `BuilderWorkflowState` up to the paired join node, and stores the collected `{ branchId: result }` map on the state.
* `JoinNodeExecutor` writes that map to its `output_key`.
* `GraphValidator::validateParallel()` enforces the pairing (a fork must reach a join on its `default` handle and declare at least one branch; a join must have a paired fork).
* A branch that pauses raises `ParallelBranchInterruptException`, which `WorkflowRunner` turns into a `kind: parallel` checkpoint so only the interrupted branch resumes.

Key idea: keep branch state **isolated** and merge only at the join, so branches remain independent and order-insensitive.

### Checkpointable nodes (decorator)

`CheckpointingExecutor` wraps Agent/LLM/RAG/Tool executors so a node with `data.checkpoint: true` caches its state change in `neuronai_studio_workflow_checkpoints` and is skipped on resume. It is a plain decorator over `NodeExecutorInterface` — you can wrap your own executor the same way when registering it, injecting `CheckpointService`. The checkpoint key is `sha256(trace_id | node_id | iteration | input_hash)` and the input hash excludes volatile internal keys, so changing relevant upstream state invalidates the cache.

### Nodes with guardrails

The **Loop** node is the canonical example of a guardrailed node type:

* `max_steps` per node prevents unbounded cycles
* `loop.global_max_steps` caps total executions
* `GraphValidator` requires authorized back-edges to pass through a Loop node

When adding custom nodes that can repeat or recurse, follow the same pattern: explicit limits, validation at save time, and a dedicated exception at runtime.

## Configuration

Add metadata to `config/neuronai-studio.php` under `node_types` for config-driven registration, or use the facade for dynamic registration.

## See also

* [Node Types](/neuronai-studio/workflows/flow-nodes.md)
* [Contributing to Studio UI](/neuronai-studio/extending/contributing-to-studio-ui.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://vibemx.gitbook.io/neuronai-studio/extending/custom-node-types.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
