mirror of
https://github.com/n8n-io/n8n.git
synced 2026-10-18 14:56:50 +02:00
1640 lines
83 KiB
Markdown
1640 lines
83 KiB
Markdown
# Tool Reference
|
||
|
||
All tools the Instance AI agent has access to. Tools are organized into
|
||
orchestration tools (used by the orchestrator for loop control) and domain tools
|
||
(used by the orchestrator directly). Each tool defines its input schema with
|
||
Zod. Tools with one stable output shape can also define an output schema.
|
||
|
||
Most domain tools are **action-based**: one tool per domain, with an `action`
|
||
field selecting the operation. The input is a Zod discriminated union keyed on
|
||
`action`, so the model receives a precise schema per action and the handler
|
||
receives a narrowed type. Capability-gated actions are generally absent from the
|
||
union when the host has not wired them. Some tools instead keep an unavailable
|
||
action or fallback tool surface and return an error or empty result. Tool ids
|
||
live in `src/tools/tool-ids.ts`.
|
||
|
||
### Approval copy
|
||
|
||
An approval card has a title and a description. The title names the asset without
|
||
its ID, for example `Assistant wants to edit CRM Lead enrichment`. The text below
|
||
it is a plain-language description of the change. Tools send the asset name as
|
||
`resourceName` on the suspend payload and structured `approvalDetails`. The
|
||
frontend builds the title from `resourceName` and the
|
||
`instanceAi.tools.{tool}.{action}.imperativeWithResource` i18n key.
|
||
It renders the details with `instanceAi.approval.*` keys in the current UI locale.
|
||
This includes row and column previews, filters, workflow actions, and publish
|
||
verification notices. Counts use locale plural rules. Names and data values stay
|
||
unchanged. Add locale translations for these keys; missing translations fall back
|
||
to English. The backend retains `message` for older clients and saved approvals
|
||
that have no structured details.
|
||
|
||
`build-workflow`, `workflows(action="publish")`,
|
||
and `executions(action="run")` accept `approvalSummary`. The agent supplies one
|
||
line in the user’s language that describes the concrete change or effect of the
|
||
call, for example `Add a Slack notification after the payment check`. Live execution summaries
|
||
describe the external actions that the workflow will perform. The field is
|
||
optional so older saved tool calls can still resume. Calls without the field
|
||
show a generic description such as `Save the changes to this workflow`.
|
||
|
||
Data-table approvals build the description from the tool input: columns, row
|
||
counts, and filter conditions. Insert previews show up to three rows, five columns
|
||
per row, and 100 characters per JSON-formatted value, including quotes. The card states how many rows or columns
|
||
the preview omits. Pass `dataTableName` and `currentColumnName` when known
|
||
so the card shows names instead of IDs. No tool looks up names only for the
|
||
card. These messages do not change approval permissions or group separate tool
|
||
calls. Bare `like` and `ilike` values use contains matching: the data-table
|
||
service adds `%` before and after a value when it has no `%`. Values with `%`
|
||
keep their explicit pattern. `like` matches case; `ilike` ignores case.
|
||
|
||
| Tool | Actions |
|
||
|------|---------|
|
||
| `workflows` | 12 |
|
||
| `data-tables` | 11 |
|
||
| `workspace` | 8 |
|
||
| `executions` | 8 |
|
||
| `credentials` | 6 |
|
||
| `nodes` | 7 |
|
||
| `mcp-servers` | 4 |
|
||
| `conversation-history` | 2 |
|
||
| `task-control` | 3 |
|
||
| `research` | 2 |
|
||
| `eval-config` | 6 |
|
||
| `n8n-docs` | 3 |
|
||
| `agent-context` | 13 lookup types |
|
||
| `build-workflow`, `ask-user`, `parse-file`, `searchModels` | single-purpose |
|
||
|
||
## Orchestration Tools
|
||
|
||
These tools are exclusive to the orchestrator agent. Sub-agents do not receive
|
||
them. Some are conditional on context availability.
|
||
|
||
### `create-tasks`
|
||
|
||
Persist a dependency-aware task plan for detached multi-step execution. For
|
||
initial plan-worthy work, the orchestrator loads the `planning` skill, performs
|
||
discovery with normal domain tools, loads `create-tasks` via `load_tool`, then
|
||
calls `create-tasks` with
|
||
`planningContext.source: "planning-skill"`. For
|
||
`<planned-task-follow-up type="replan">` turns, use
|
||
`planningContext.source: "replan"` when multiple dependent tasks still need
|
||
scheduling. Clear single-workflow builds, including new and one-off workflows,
|
||
use `workflow-builder`, workspace file tools, and `build-workflow` directly.
|
||
The plan is shown to the user for approval before execution starts.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `tasks` | array | yes | Dependency-aware execution plan (see schema below) |
|
||
| `planningContext` | object | yes | `{ source: "planning-skill" \| "replan", summary: string, assumptions?: string[] }` |
|
||
|
||
**Task schema**:
|
||
|
||
```typescript
|
||
{
|
||
id: string; // Stable identifier used by dependency edges
|
||
title: string; // Short user-facing task title
|
||
kind: 'build-workflow' | 'checkpoint';
|
||
spec: string; // Detailed executor briefing for this task
|
||
deps: string[]; // Task IDs that must succeed before this task can start
|
||
workflowId?: string; // Existing workflow ID for the builder to hydrate before saving
|
||
isSupportingWorkflow?: boolean; // Build task completes after saving a supporting sub-workflow
|
||
}
|
||
```
|
||
|
||
**Returns**: `{ result: string, taskCount: number }`
|
||
|
||
**Behavior**:
|
||
- First call persists the plan, publishes `tasks-update` event, and **suspends**
|
||
for user approval
|
||
- On approval: calls `schedulePlannedTasks()` to start detached execution
|
||
- On rejection: returns feedback for the LLM to revise the plan
|
||
- On denial: cancels the graph and blocks same-turn resubmission
|
||
|
||
**Task kinds** map to executors:
|
||
- `build-workflow` → orchestrator follow-up run using the workflow-builder skill
|
||
- `checkpoint` → exceptional orchestrator-executed semantic or cross-workflow check
|
||
|
||
Standalone data-table work is handled directly by the orchestrator with the
|
||
`data-table-manager` skill and the `data-tables` / `parse-file` tools. Single
|
||
workflow-local table requirements belong in the builder task spec; plan only
|
||
when the table schema is shared, independently durable, or creates real
|
||
dependency coordination.
|
||
|
||
### `task-control`
|
||
|
||
Progress tracking and background-task control. One tool, three actions.
|
||
|
||
#### `task-control(action="update-checklist")`
|
||
|
||
Update a visible task checklist for the user. Lightweight progress tracking
|
||
during synchronous work.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `tasks` | array | yes | List of `{id, description, status, detail?}` items |
|
||
|
||
**Returns**: `{ saved: true }`
|
||
|
||
**Behavior**: Saves to storage, publishes `tasks-update` event for live UI refresh.
|
||
|
||
#### `task-control(action="cancel-task")`
|
||
|
||
Cancel a running background task by its ID.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `taskId` | string | yes | Background task ID (from `<running-tasks>` context) |
|
||
|
||
**Returns**: `{ result: "Background task {taskId} cancelled." }`
|
||
|
||
**Cancellation flow** (three surfaces converge):
|
||
```
|
||
User clicks stop button -> POST /chat/:threadId/tasks/:taskId/cancel ---+
|
||
User says "stop that" -> orchestrator calls task-control -------------+
|
||
cancelRun (global stop) -> cancelBackgroundTasks(threadId) -------------+
|
||
v
|
||
service.cancelBackgroundTask()
|
||
```
|
||
|
||
#### `task-control(action="correct-task")`
|
||
|
||
Send a course correction to a running background task.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `taskId` | string | yes | Background task ID |
|
||
| `correction` | string | yes | Correction message |
|
||
|
||
**Returns**: `{ result: string }`. The string says whether the correction was
|
||
sent, the task already completed, the task was not found, or delivery is not
|
||
available.
|
||
|
||
### `complete-checkpoint`
|
||
|
||
Close out a `checkpoint` planned task with its verdict. The tool is registered
|
||
for the orchestrator and is intended only for checkpoint follow-up turns. The
|
||
task must exist, have kind `checkpoint`, and be in the `running` state.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `taskId` | string | yes | Checkpoint task ID from the planned-task follow-up |
|
||
| `status` | `"succeeded" \| "failed"` | yes | Checkpoint verdict |
|
||
| `result` | string | no | Short user-visible outcome note |
|
||
| `error` | string | no | Failure message when `status` is `failed` |
|
||
| `outcome` | object | no | Structured evidence such as an execution ID, failed node, or data excerpt |
|
||
|
||
**Returns**: `{ result: string, ok: boolean }`
|
||
|
||
### `get-session` *(conditional)*
|
||
|
||
Read a resolved Agent preview session — title, session number and transcript.
|
||
Registered only when the host provides both `agentPreviewSession` and
|
||
`resolvePreviewSession`.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `executionId` | string | no | Limit the transcript to one execution; omit for the whole session |
|
||
|
||
**Returns**: `{ ok, title?, sessionNumber?, transcript?, error? }`
|
||
|
||
### `verify-built-workflow` *(conditional)*
|
||
|
||
Run a built workflow with per-execution pin data for verification (never
|
||
persisted to the workflow). Destructive and user-action nodes — write
|
||
operations, nodes with mocked credentials, mid-workflow Form pages, Wait
|
||
nodes — are **simulated**: the build outcome carries a per-node
|
||
execute-vs-simulate plan (`nodeSimulationPlan`, produced by a deterministic
|
||
classifier plus an LLM pass at submit time) and LLM-generated mock output
|
||
(`simulationFixtures`). Simulated nodes are pinned with their fixture, so
|
||
verification never sends messages, writes rows, deletes data, or parks in
|
||
`waiting`. The tool output marks simulated nodes (`simulatedNodes`,
|
||
`nodePreviews[].simulated`, `simulationNote`), and the saved execution
|
||
carries `resultData.simulation` so the editor can label simulated outputs.
|
||
For build outcomes that carry a plan, a `waiting` result is a failure (an
|
||
unsimulated user-action node); only legacy plan-less outcomes keep the
|
||
waiting-with-output-as-success fallback.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `workItemId` | string | yes | Work item ID from build outcome |
|
||
| `workflowId` | string | yes | Workflow ID to execute |
|
||
| `inputData` | object | no | Trigger payload — **shape depends on trigger type**, see below |
|
||
| `timeout` | number | no | Max wait in ms (default 300000) |
|
||
|
||
**`inputData` shape by trigger type** (the adapter's `getPinDataForTrigger` spreads or wraps based on type — passing the wrong shape produces null downstream values that look like an expression bug):
|
||
|
||
| Trigger | Pass | Adapter emits on `$json` |
|
||
|---|---|---|
|
||
| Form Trigger | flat field map, e.g. `{name: "Alice", email: "a@b.c"}` | `{ submittedAt, formMode: "instanceAi", name, email, ... }` — matches production. Do NOT wrap in `formFields`. |
|
||
| Webhook | body payload, e.g. `{event: "signup", userId: "..."}`, **or** the request envelope `{ body: {...}, query: {...}, headers: {...}, params: {...} }` when any expression reads `$json.query.*`, `$json.headers.*` or `$json.params.*` | flat payload → `{ headers: {}, query: {}, params: {}, body: { event, userId, ... } }`; envelope → passed through as-is |
|
||
| Chat Trigger | `{chatInput: "..."}` | `{ sessionId, action, chatInput }` |
|
||
| Schedule | omit | synthetic timestamp fields |
|
||
|
||
For reusable workflows with multiple enabled triggers, pass `triggerNodeName`
|
||
and run verification once for each trigger. Successful runs accumulate node
|
||
coverage per trigger. Each retry reserves an attempt and clears that trigger's
|
||
old pass before execution. A successful result restores the combined coverage.
|
||
If either write fails, the tool reports an error. The attempt limit still applies.
|
||
|
||
The returned and saved `claim` use the same cumulative evidence. Pending triggers
|
||
and nodes without real coverage prevent a `verified` claim. Publishing during a
|
||
retry requires explicit acknowledgement through `acknowledgeUnverified: true`.
|
||
|
||
**Writes on success/failure**: the tool persists a structured `verification`
|
||
record (`{ attempted, success, executionId, status, claim, evidence, verifiedAt }`) onto
|
||
the build outcome so workflow-verification follow-ups and exceptional checkpoint
|
||
turns can reuse it without re-running verify.
|
||
|
||
**Returns**: `{ executionId?, success, status?, data?, error?, simulationNote?, resolvedParameterWarnings?, skippedParameterChecks?, skippedParameterCheckCount? }`
|
||
|
||
**Simulated-node parameter check**: a simulated node's preview is fixture data, so
|
||
an expression that resolved to empty leaves no trace in the run. After the run the
|
||
tool replays parameter resolution (`getResolvedNodeParameters`) for every reached
|
||
simulated node and returns `resolvedParameterWarnings`, one entry per parameter
|
||
that resolved to `null`/`undefined`/`""` or threw (`{ nodeName, executionId, path, raw, issue:
|
||
'empty' | 'failed', detail? }`), with a summary appended to `simulationNote`.
|
||
For scripted gates, it checks each node against every pass that reached it.
|
||
Each warning identifies the execution used for that check.
|
||
Expressions that need live-only context (`$secrets`, `$response`, …) are excluded.
|
||
The check is advisory and does not change execution success. Suppressed parameter
|
||
values, replay failures, and missing executions produce `skippedParameterChecks`
|
||
entries (`{ nodeName, executionId?, reason }`) and a note in `simulationNote`.
|
||
The list contains at most 20 entries across all passes. `skippedParameterCheckCount`
|
||
reports the total. When entries are omitted, the note states how many are shown.
|
||
Omitted checks also leave dynamic fields unverified.
|
||
The reasons are `parameter-values-disabled`, `replay-failed`, and
|
||
`execution-unavailable`. Skipped checks expose no parameter values or replay
|
||
error details. Their dynamic fields remain unverified.
|
||
|
||
**Empty-output check**: when a real node returns only `{}` items,
|
||
`emptyOutputNote` names that node. The run still counts as a success. The note
|
||
tells the agent to compare the node's parameters with its typeVersion before it
|
||
replies. If they do not match, the agent repairs the node, also when the agent
|
||
did not change it. If they match, the agent does not change a node that it did
|
||
not build, and tells the user that the node outputs no fields. Only the first
|
||
empty node of a chain is named, because the nodes after it only pass the
|
||
emptiness on. Triggers are skipped because a Manual Trigger emits `{}`.
|
||
Simulated nodes are skipped because their output is a fixture. Nodes that
|
||
output a file are skipped because their data is in the binary, which the
|
||
preview omits. Truncated outputs are skipped because the hidden items can hold
|
||
data.
|
||
|
||
### `report-verification-verdict` *(conditional)*
|
||
|
||
Feed verification results into the deterministic workflow loop state machine.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `workItemId` | string | yes | Work item ID |
|
||
| `verdict` | enum | yes | `verified`, `needs_patch`, `needs_rebuild`, `trigger_only`, `needs_user_input`, `failed_terminal` |
|
||
| `failureSignature` | string | no | For repeated failure detection |
|
||
| `failedNodeName` | string | no | Node that failed |
|
||
| `patch` | string | no | For `needs_patch` verdict |
|
||
| `diagnosis` | string | no | Failure analysis |
|
||
|
||
**Returns**: `{ guidance: string }` — next action based on loop state machine.
|
||
|
||
### `apply-workflow-credentials` *(conditional)*
|
||
|
||
Atomically apply real credentials to previously-mocked workflow nodes.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `workItemId` | string | yes | Work item ID from build outcome |
|
||
| `credentials` | object | yes | Real credential mapping |
|
||
|
||
**Returns**: `{ updatedNodes: string[] }`
|
||
|
||
## `workflows` (12 actions)
|
||
|
||
The domain surface has up to twelve actions. Version actions are registered only
|
||
when their backend methods are available. Use `get` to inspect a workflow. Use
|
||
`get-as-code`, workspace edits, and `build-workflow` to change a workflow.
|
||
The internal `getAsWorkflowJSON` and `updateFromWorkflowJSON` service methods
|
||
remain available to compiler, setup, validation, credential, and verification
|
||
flows. They are not model-facing actions.
|
||
|
||
### `workflows(action="list")`
|
||
|
||
List workflows accessible to the current user.
|
||
|
||
| Field | Type | Required | Default | Description |
|
||
|-------|------|----------|---------|-------------|
|
||
| `query` | string | no | — | Substring filter on the workflow name only — omit for inventory questions |
|
||
| `limit` | number | no | 50 | Max results (1–100) |
|
||
| `status` | `"active" \| "archived" \| "all"` | no | `"active"` | Which workflows to list |
|
||
| `scope` | `"project" \| "instance"` | no | `"project"` | Which project(s) to search |
|
||
| `projectId` | string | no | — | Read one specific project, overriding `scope` |
|
||
| `folderPath` | string | no | — | Restrict to one folder, named as the user named it (`Clients/Acme`, `Acme`). Strict, staged match; never fuzzy. Advertised only while folder exploration is on |
|
||
| `folderId` | string | no | — | Restrict to one folder by id (from a prior row's `folder.id`). Same gate |
|
||
| `recursive` | boolean | no | `true` | Include nested subfolders. Same gate |
|
||
|
||
**Returns**: `{ workflows: [{ id, name, activeVersionId, isArchived, createdAt, updatedAt, project?, folder? }], total, totalInScope, note?, folderResolution? }`
|
||
|
||
`activeVersionId` is `null` when the workflow is unpublished.
|
||
|
||
`total` is how many workflows match every filter; `totalInScope` is how many the
|
||
same status and scope hold with `query` dropped. When a name filter or `limit`
|
||
left workflows out, `note` says so — a filtered page must never be read as the
|
||
project's full inventory.
|
||
|
||
`project` (`{ id, name }`) is the owning project, present only when the listing
|
||
can span more than one — i.e. neither `projectId` nor a bound project narrowed it
|
||
to one. It is what makes membership readable in a cross-project listing instead
|
||
of guessable by comparing per-scope counts.
|
||
|
||
`folder` (`{ id, name, path }`) is the workflow's folder with its root-relative
|
||
path (`Clients/Acme`). Folder names cannot contain `/`, so `path` is
|
||
unambiguous. Absent for root-level workflows, and absent on every row
|
||
while folder exploration is off for the run (PostHog flag
|
||
`110_instance_ai_folder_exploration`, force-on via
|
||
`N8N_INSTANCE_AI_FOLDER_EXPLORATION_ENABLED`).
|
||
|
||
`folderResolution` (`{ requested, reason, candidates }`) is present only when a
|
||
requested folder did not resolve. `workflows` is then empty on purpose, and
|
||
`note` says so first: the rows must never be read as the folder, and a `query`
|
||
name filter is not a substitute. `reason` is `not-found`, `ambiguous` (more
|
||
than one folder matched; `candidates` lists them), `unsupported` (folders are
|
||
not licensed on the instance) or `scope-too-wide` (the listing spans more
|
||
projects than the folder scan covers, so the caller must pass `projectId`).
|
||
|
||
`projectId` is a read-only narrowing: the adapter passes it as a filter on a query
|
||
that still resolves readability from the caller's own project and workflow roles,
|
||
so it cannot reach a project the user can't read (`scope: "instance"` already
|
||
returns that whole readable set). Writes ignore it and stay locked to the thread's
|
||
bound project.
|
||
|
||
### `workflows(action="get")`
|
||
|
||
Inspect workflow metadata and structure. Small workflows return their full node
|
||
data. Large workflows return a structural summary unless `full` is true.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `workflowId` | string | yes | Workflow ID |
|
||
| `versionId` | string | no | Read this historical version |
|
||
| `full` | boolean | no | Include complete node data for a large workflow |
|
||
|
||
**Returns**: the workflow detail, a structure-only summary, a version response,
|
||
or a structured not-found response.
|
||
|
||
`activeVersionId` is `null` when the workflow is unpublished.
|
||
|
||
### `workflows(action="get-as-code")`
|
||
|
||
Get a workflow as TypeScript SDK code. Used by the builder agent to inspect an
|
||
existing workflow when no workspace source file is already available. Existing
|
||
workflow modifications should write the returned code to a workspace source file
|
||
and call `build-workflow` with both `filePath` and the real n8n `workflowId`
|
||
once; subsequent repairs can reuse only `filePath`.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `workflowId` | string | yes | Workflow ID |
|
||
| `versionId` | string | no | Convert this historical version |
|
||
|
||
**Returns**: `{ workflowId, name, code, error? }`.
|
||
|
||
### `build-workflow`
|
||
|
||
Compile, validate, and save a workspace workflow source file. Inline source and
|
||
string patches are not accepted; edit the workspace file first and then call
|
||
this tool with `filePath`.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `filePath` | string | yes | Workspace path to the `.workflow.ts` or WorkflowJSON source file |
|
||
| `workflowId` | string | no | Existing n8n workflow ID to bind to this file on the first update |
|
||
| `name` | string | no | Workflow name override for new workflows |
|
||
| `workItemId` | string | no | Work item hint for workflow-loop reporting |
|
||
| `isSupportingWorkflow` | boolean | no | Marks a saved sub-workflow as supporting |
|
||
| `folderPath` | string | no | Folder to create the new workflow in, named as the user named it (`Clients/Acme`, `Acme`). Same strict resolution as `list`; an unresolved folder fails the build before anything is saved, with the real folders listed. New workflows only: to move an existing one use `workspace(action="move-workflow-to-folder")`. Advertised only while folder exploration is on |
|
||
|
||
There is deliberately **no `projectId`**: a build writes to the project the
|
||
conversation is bound to, and nothing can redirect it. The field used to exist and
|
||
the adapter ignored it, so a build could report a project it had not written to.
|
||
|
||
**Returns**: `{ success, workflowId?, workflowName?, workItemId?, filePath, sourceHash?, folder?, remediation?, errors?, warnings? }`
|
||
— `folder` is `{ id, name, path }` when the workflow was created inside a folder.
|
||
|
||
**Behavior**: Reads the source file from the runtime workspace, compiles
|
||
TypeScript sources through the sandbox `tsx` runner or parses WorkflowJSON
|
||
directly, validates the resulting workflow JSON server-side, resolves
|
||
credentials, saves by the workflow ID bound to the source file, and persists the
|
||
latest source hash and workflow version in thread metadata. If the file has no
|
||
saved workflow ID, the build creates a new workflow unless `workflowId` is
|
||
provided to bind the file to an existing workflow. If the bound workflow no
|
||
longer exists, the tool returns blocked remediation rather than creating a
|
||
replacement.
|
||
|
||
For edits, only `INVALID_PARAMETER`, `chat_model_validation`,
|
||
`HARDCODED_CREDENTIALS`, and `SWITCH_NO_OUTPUT_CONNECTIONS` can become
|
||
informational. Missing saved state or a finding without a node name keeps the
|
||
finding blocking. Other codes keep their original severity.
|
||
|
||
For `HARDCODED_CREDENTIALS`, compare the saved authentication values, credential
|
||
selection, and destination settings. The URL must be fixed and unchanged.
|
||
Wiring, timeout, response formatting, and non-auth headers or query fields do not
|
||
introduce a new hardcoded value. Changed auth, destination settings, or enabled
|
||
state still block. Expression URLs stay blocking because their destination
|
||
depends on execution data.
|
||
|
||
For `SWITCH_NO_OUTPUT_CONNECTIONS`, check whether the same enabled Switch already
|
||
had no main outputs. Changes to its inputs or rules leave that finding
|
||
informational, including connecting an existing parked Switch. New or re-enabled
|
||
Switches and removal of existing output branches remain blocking. These checks
|
||
do not prove runtime correctness. The sandbox CLI has no saved-workflow baseline,
|
||
so `build-workflow` makes the final decision. Preserve unrelated nodes and report
|
||
any remaining blocker instead of expanding the edit.
|
||
|
||
### `workflows(action="delete")`
|
||
|
||
Archive a workflow (soft delete, deactivates if needed). Reverse it with
|
||
`workflows(action="unarchive")`.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `workflowId` | string | yes | Workflow to archive |
|
||
|
||
**Returns**: `{ success: boolean }`
|
||
|
||
### `workflows(action="unarchive")`
|
||
|
||
Restore an archived workflow without publishing it.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `workflowId` | string | yes | Archived workflow to restore |
|
||
|
||
**Returns**: `{ success: boolean }`
|
||
|
||
### `workflows(action="setup")`
|
||
|
||
Open the inline UI for per-node credential and parameter setup. The tool uses a
|
||
suspend/resume state machine and can present several node setup requests in one
|
||
confirmation card.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `workflowId` | string | yes | Workflow to set up |
|
||
| `projectId` | string | no | Project scope for credential creation |
|
||
| `credentialHints` | array | no | Researched templates for Simplified Custom Auth credentials |
|
||
| `allowPlainGenericAuth` | boolean | no | Allow a user-selected plain generic auth type |
|
||
| `preferNewCredentials` | string[] | no | Types for which the user explicitly requested a new credential |
|
||
| `reopenSkipped` | string[] | no | Previously skipped types or nodes to reopen at the user's request |
|
||
| `includeAllNodes` | boolean | no | Include every node instead of only nodes changed by the last build |
|
||
|
||
**Returns**: `{ completedNodes, nodesStillNeedingSetup, skippedByUser, failedNodes }` —
|
||
`nodesStillNeedingSetup` is what nobody has configured yet, `skippedByUser` what the user
|
||
actively dismissed and the agent must not re-open (see `reopenSkipped`).
|
||
|
||
**Setup panel** (`118_instance_ai_setup_overhaul: variant`): the normal setup call
|
||
analyzes the whole workflow, including bound slots. It publishes the `setup-items`
|
||
snapshot and confirms that it reached storage. It then saves the build's setup
|
||
routing marker. Only after both steps succeed does it return
|
||
`{ success: true, announced: true, workflowId, open, configured, validationWarnings, message }`.
|
||
The agent summarizes the result and ends its turn. `open` lists pending items.
|
||
`configured` lists stored bindings. Configuration does not prove that a connection
|
||
test or workflow execution passed. Failed connection checks appear in `validationWarnings`.
|
||
|
||
Validation and destination approval run before the announcement. The agent follows
|
||
the returned guidance for errors, denials, or approvals. Explicit
|
||
`preferNewCredentials` requests use the selection card. Existing cards keep their
|
||
`apply`, `test-trigger`, and decline paths.
|
||
|
||
Each new user turn carries a `<workflow-setup-state>` block with current saved
|
||
state and items that settled since the previous look. This observation does not
|
||
publish snapshots or change the current workflow target. It preserves announced
|
||
recipes and does not count temporary credential replacement requests as user progress.
|
||
This observation reads saved bindings and checks required values and placeholders.
|
||
It does not test credentials or fetch provider resource lists. It does not produce
|
||
fresh connection-test warnings. Live checks remain part of setup and verification.
|
||
|
||
When setup items settle between turns, none remain open, and there are no
|
||
validation warnings, the agent verifies the current configuration on the next
|
||
user turn. Setup changes do not start an agent run by themselves.
|
||
The panel's Execute action sends a normal chat message with
|
||
`context: { source: 'setup-panel-execute', workflowId }`. With the flag on,
|
||
the host adds a private `workflow-test-request` block that identifies the target.
|
||
If required setup remains open, the agent reports those items and ends the turn
|
||
without a run. Otherwise, it runs the saved workflow through `executions(action="run")`, inspects
|
||
the output, and reports the test result in chat. Execution approval policy still
|
||
applies. The new panel does not use the wizard's trigger-test resume loop.
|
||
|
||
### `workflows(action="publish")`
|
||
|
||
Publish a workflow version to production. Makes it active — it will run on triggers.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `workflowId` | string | yes | Workflow ID |
|
||
| `versionId` | string | no | Specific version (omit for latest draft) |
|
||
|
||
**Returns**: `{ success: boolean, activeVersionId?: string }`
|
||
|
||
### `workflows(action="unpublish")`
|
||
|
||
Stop a workflow from running in production. The draft is preserved.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `workflowId` | string | yes | Workflow ID |
|
||
|
||
**Returns**: `{ success: boolean }`
|
||
|
||
### `workflows(action="list-versions")` *(conditional — requires license)*
|
||
|
||
List version history for a workflow (metadata only).
|
||
|
||
| Field | Type | Required | Default | Description |
|
||
|-------|------|----------|---------|-------------|
|
||
| `workflowId` | string | yes | — | Workflow ID |
|
||
| `limit` | number | no | 20 | Max results (1–100) |
|
||
| `skip` | number | no | 0 | Results to skip |
|
||
|
||
**Returns**: `{ versions: [{ versionId, name, description, authors, createdAt, autosaved, isActive, isCurrentDraft }] }`
|
||
|
||
### `workflows(action="validate")`
|
||
|
||
Return the per-node configuration issues a human would see as red warning
|
||
indicators on the canvas — missing credentials, parameter validation errors and
|
||
similar. A static check; it does not execute the workflow. Use it to confirm a
|
||
workflow is configured correctly before suggesting the user run or publish it.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `workflowId` | string | yes | Workflow ID |
|
||
| `ignoreIssues` | array | no | Issue categories to skip: `parameters`, `credentials`, `input`, `execution`, `typeUnknown`, `aiGateway`, `chatModel` |
|
||
|
||
### `workflows(action="restore-version")` *(conditional — requires license)*
|
||
|
||
Restore a workflow to a previous version (overwrites current draft). HITL
|
||
approval required. This does not publish the restored draft. A production rollback
|
||
must also use the publish action and its normal approval flow.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `workflowId` | string | yes | Workflow ID |
|
||
| `versionId` | string | yes | Version to restore |
|
||
|
||
**Returns on success**: `{ success: true, workflowId, publishState, publishStateNote }`.
|
||
`publishState` contains `savedVersionId`, `activeVersionId`, and `live`
|
||
(`unpublished`, `current`, or `stale`). The note states whether publication is still
|
||
required. Denied and failed restores retain their existing error responses.
|
||
|
||
### `workflows(action="update-version")` *(conditional — requires `feat:namedVersions` license)*
|
||
|
||
Update a version's name or description.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `workflowId` | string | yes | Workflow ID |
|
||
| `versionId` | string | yes | Version ID |
|
||
| `name` | string \| null | no | New name |
|
||
| `description` | string \| null | no | New description |
|
||
|
||
**Returns**: `{ success: boolean }`
|
||
|
||
---
|
||
|
||
## `executions` (8 actions)
|
||
|
||
### `executions(action="list")`
|
||
|
||
List recent workflow executions.
|
||
|
||
| Field | Type | Required | Default | Description |
|
||
|-------|------|----------|---------|-------------|
|
||
| `workflowId` | string | no | — | Filter by workflow |
|
||
| `status` | string | no | — | `success`, `error`, `running`, `waiting` |
|
||
| `limit` | number | no | 20 | Max results (1–100) |
|
||
|
||
**Returns**: `{ executions: [{ id, workflowId, workflowName, status, startedAt, finishedAt, mode }] }`
|
||
|
||
### `executions(action="run")`
|
||
|
||
Execute a workflow, wait for completion (with timeout), and return the result.
|
||
Default timeout: 5 minutes; max: 10 minutes. On timeout, execution is cancelled.
|
||
|
||
| Field | Type | Required | Default | Description |
|
||
|-------|------|----------|---------|-------------|
|
||
| `workflowId` | string | yes | — | Workflow to run |
|
||
| `inputData` | object | no | — | Data passed to the trigger node |
|
||
| `timeout` | number | no | 300000 | Max wait time in ms (max 600000) |
|
||
| `triggerNodeName` | string | no | — | Trigger node to use when a workflow has more than one trigger |
|
||
|
||
**Returns**: `{ executionId, status, data?, error?, startedAt?, finishedAt?, verificationClaim? }`
|
||
|
||
**Live test evidence**: `verify-built-workflow` always simulates destructive
|
||
nodes, so a live test runs through this action. When a successful run reaches
|
||
every planned node of the latest build, with no saved pins and no injected
|
||
trigger input, the run is recorded as a `verified` claim on the build outcome.
|
||
The publish gate then reads that claim. The result carries it as
|
||
`verificationClaim`. Other runs leave the stored claim unchanged: a live run
|
||
can raise the verdict but never lower it.
|
||
|
||
**Type-aware pin data**: Constructs proper pin data per trigger type:
|
||
- **Chat trigger**: `{ chatInput, sessionId, action }`
|
||
- **Form trigger**: `{ submittedAt, formMode: 'instanceAi', ...inputData }`
|
||
- **Webhook trigger**: flat `inputData` → `{ headers: {}, query: {}, params: {}, body: inputData }`; an envelope whose keys are only `body`/`query`/`headers`/`params` is passed through, so query- and header-driven expressions can be exercised
|
||
- **Schedule trigger**: current datetime information
|
||
- **Unknown trigger**: `{ json: inputData }` (generic fallback)
|
||
|
||
### `executions(action="run-step")`
|
||
|
||
Run ONE node of a saved workflow and return its real output — the canvas
|
||
"Execute step". The node runs inside the real workflow, so expressions that
|
||
reference other nodes resolve, sub-nodes (model, memory, tools) come along, and
|
||
the run lands in the workflow's execution history. The execution is always
|
||
manual: `WorkflowRunner.resolvePinData` returns pin data only for manual and
|
||
evaluation mode, so any other mode would drop the workflow's pins.
|
||
|
||
| Field | Type | Required | Default | Description |
|
||
|-------|------|----------|---------|-------------|
|
||
| `workflowId` | string | yes | — | Workflow that owns the node |
|
||
| `nodeName` | string | yes | — | Node to run |
|
||
| `reuseExecutionId` | string | no | — | Replay this past execution's data for the nodes above the target |
|
||
| `mockInput` | object[] | no | — | Items to feed the target, skipping every node above it |
|
||
| `toolArguments` | object \| string | no | — | Arguments for a tool target — what an agent would fill from `$fromAI` |
|
||
| `versionId` | string | no | current draft | Run a past version's graph |
|
||
| `timeout` | number | no | 300000 | Max wait time in ms (max 600000) |
|
||
|
||
**Returns**: `{ executionId, status, nodeName, inputMode, mockedNodeNames, replayedNodeNames?, reusedFromExecutionId?, executedNodeNames?, data?, error?, ... }`
|
||
|
||
**Input modes**, in descending order of what the result proves:
|
||
|
||
| `inputMode` | Set by | What it proves |
|
||
|-------------|--------|----------------|
|
||
| `reused-execution` | `reuseExecutionId` | The node ran on data the workflow really produced |
|
||
| `chain` | neither option | The node ran on data its ancestors really produced in this run |
|
||
| `mocked` | `mockInput` | Only that the node accepts *this* input — the upstream output is invented |
|
||
|
||
`executedNodeNames` counts only what ran in *this* execution. Mocked and
|
||
replayed nodes carry run data without having run, so they are excluded —
|
||
otherwise a step run on a ten-node workflow would report ten nodes as executed
|
||
when one was. `data` still shows their output, listed under
|
||
`mockedNodeNames` and `replayedNodeNames`. `replayedNodeNames` names only
|
||
what this run carried: a node of the reused execution that sits outside the
|
||
trigger-to-target subgraph never enters the run and is not listed.
|
||
|
||
`mocked` also invents a placeholder item for every node between the trigger
|
||
and the target, because `findStartNodes` walks down from the trigger and stops
|
||
at the first node with no run data. `mockedNodeNames` lists them. A
|
||
placeholder on an upstream IF or Switch picks a branch that real data may pick
|
||
differently, which is why a mocked step is never evidence that the workflow
|
||
works.
|
||
|
||
The action refuses a run whose input would not keep the nodes above the target
|
||
out of it. It applies the rules of `findStartNodes`, so the engine re-runs:
|
||
|
||
- A node with neither run data nor pin data.
|
||
- A node whose saved run failed, even a pinned one. The engine retries it.
|
||
- A Loop Over Items node whose last run left the `done` output empty. The
|
||
engine restarts the loop. A loop edge that runs through the target does not
|
||
count, because `findSubgraph` drops it, and then the second output decides.
|
||
|
||
`mockInput` can hit only the last rule: the placeholder on a loop leaves
|
||
`done` empty when the target hangs off the loop body. Use `reuseExecutionId`
|
||
with an execution where the loop finished.
|
||
|
||
**Sub-node targets**: a tool has no main input, and the engine never runs one on
|
||
its own. It replaces the node that owns the tool (the Agent) with a virtual Tool
|
||
Executor that inherits that node's main parents, then runs the tool from there.
|
||
So a step on a tool is planned against the Agent: `mockInput` feeds the Agent's
|
||
input, and `reuseExecutionId` replays the Agent's ancestors. A chain run on a
|
||
tool runs every node above the Agent, so supply `reuseExecutionId` or
|
||
`mockInput` when one of those nodes writes.
|
||
|
||
The engine runs the tool through a virtual node the workflow does not contain,
|
||
`PartialExecutionToolExecutor`. The result never carries that name: the run is
|
||
reported under the node the caller named, in the output data, the executed
|
||
names, the last node, and a node error. The tool's own record is the one kept,
|
||
because the executor re-serializes the result as a single string.
|
||
|
||
`ranThroughNodeNames` names the nodes that can run the tool, not the one that
|
||
ran it. A tool that hangs off several agents lists them all: the engine picks
|
||
one and reports no choice, so any single name here would be a guess. The plan
|
||
covers every candidate's ancestry, so the run is right whichever one the engine
|
||
takes.
|
||
|
||
The action refuses a tool when one of its agents runs above another. If the
|
||
engine picks the lower agent, it drops the data the plan gave the upper agent
|
||
and the nodes between them, and runs those nodes for real. The error tells the
|
||
caller to run the upper agent instead and read the tool with
|
||
`get-node-output`. Agents on parallel branches are not affected.
|
||
|
||
`toolArguments` supplies what the agent would normally decide — the values
|
||
behind the tool's `$fromAI` calls, keyed by argument name, or a bare string for
|
||
a tool that takes one free-text input (Wikipedia, Code Tool, a vector store used
|
||
as a tool). It is **required** when the node declares `$fromAI` arguments: the
|
||
action refuses the run rather than execute the tool on empty arguments and
|
||
report a failure that says nothing about the user's problem.
|
||
|
||
The agent request names no tool. The Tool Executor looks the arguments up by the
|
||
tool's *runtime* name, which is `nodeNameToToolName(node)` on current versions
|
||
but comes from a parameter on older ones (`name` on Code Tool <= 1.1, Vector
|
||
Store Tool <= 1, Workflow Tool <= 2.1; `toolName` on a retrieve-as-tool vector
|
||
store < 1.3) and is hardcoded on Think 1. An empty name makes the Tool Executor
|
||
run the only tool connected to it, so a name this cannot know never stops the
|
||
run; the arguments are keyed under every name the tool can have so the lookup
|
||
finds them. Think 1's hardcoded `thinking_tool` is the one name this cannot key,
|
||
and there it costs the arguments, not the run.
|
||
|
||
A node that holds several tools is refused: the Tool Executor runs the member
|
||
whose name matches the request, that name is `buildMcpToolName` of the node
|
||
name and the server's tool name, and a miss reports success with no result at
|
||
all. Run the owning Agent instead and read the node's output from that
|
||
execution. The check is on the node type, because a node name is the user's to
|
||
change:
|
||
|
||
- `@n8n/n8n-nodes-langchain.mcpClientTool` — "MCP Client Tool" on the canvas.
|
||
- `@n8n/mcp-registry.<slug>` — a server the MCP registry added, one node type
|
||
for each server. All of them run on one hidden class
|
||
(`mcpRegistryClientTool`), and that class name never appears as a node type,
|
||
so the match is on the `@n8n/mcp-registry` package — the same test
|
||
`agents-tools.service.ts` makes.
|
||
|
||
Every **other** sub-node kind — a model, memory, embeddings — is refused: n8n
|
||
runs those only as part of the node that owns them, so the action points the
|
||
caller at that node instead of starting a run that cannot work. Their output is
|
||
still readable afterwards: a sub-node records each call under its own connection
|
||
type, and `action="get-node-output"` reads that when a node has no `main`
|
||
output.
|
||
|
||
**Refusals**: the action fails, and starts nothing, rather than run something
|
||
whose result would mislead:
|
||
|
||
- a `reuseExecutionId` whose execution holds no data for any node above the
|
||
target — falling back to a chain run would execute those nodes for real, which
|
||
is what asking for replayed input rules out;
|
||
- a tool that declares `$fromAI` arguments with no `toolArguments`;
|
||
- a node that holds several tools;
|
||
- a sub-node that is not a tool;
|
||
- a tool no node is connected to run — the engine has no node to stand in for,
|
||
so the run would start and then die;
|
||
- `toolArguments` on a node in the main graph.
|
||
|
||
**Pin data**: the target's own pin, and any pin on a node whose output the
|
||
mocked mode replaced, come off this run's copy — a pinned node never
|
||
executes, so leaving them on would make the step replay stale output. The saved
|
||
workflow keeps its pins. `workflowPinnedNodeNames` lists only the pins that fed
|
||
the run.
|
||
|
||
**Safety**: a step run is a real run, with the user's credentials against their
|
||
systems. It suits reads and transforms. A node that writes
|
||
(`create`/`update`/`delete`/`send`/`append`, non-GET HTTP Request) performs its
|
||
effect again, so debug that from `debug` and `get-resolved-node-parameters`
|
||
instead. `mockInput` does not change this: only the input is invented, the node
|
||
still runs. See the `debugging-executions` skill.
|
||
|
||
**Approval**: the same gate as `action="run"` — the admin `runWorkflow` policy,
|
||
the pre-authorized workflow list, and session grants. The session grant is per
|
||
node (`executions:run-step:<workflowId>:<nodeName>`), so a debug loop on one
|
||
node stops prompting while the rest of the workflow still asks. A whole-workflow
|
||
run grant covers a step of that workflow too.
|
||
|
||
### `executions(action="get")`
|
||
|
||
Get execution status without blocking.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `executionId` | string | yes | Execution ID |
|
||
|
||
**Returns**: `{ executionId, status, data?, error?, startedAt?, finishedAt? }`
|
||
|
||
### `executions(action="debug")`
|
||
|
||
Analyze a failed execution with structured diagnostics.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `executionId` | string | yes | Failed execution to debug |
|
||
|
||
**Returns**: `{ executionId, status, failedNode?: { name, type, error, inputData? }, nodeTrace: [{ name, type, status }] }`
|
||
|
||
### `executions(action="get-node-output")`
|
||
|
||
Get the output data of a specific node from an execution.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `executionId` | string | yes | Execution ID |
|
||
| `nodeName` | string | yes | Node name to get output for |
|
||
| `startIndex` | number | no | First item index to return. Defaults to `0` |
|
||
| `maxItems` | number | no | Maximum items to return. Defaults to `10`; maximum `50` |
|
||
|
||
**Returns**: `{ nodeName, outputs: [{ index, name?, totalItems, items }], totalItems, returned: { from, to }, totalRuns? }`.
|
||
One `outputs` entry per node output, in output order; a Filter reports `Kept` and
|
||
`Discarded` separately. `name` follows the node's output pane labels, including
|
||
renamed Switch outputs and `Success` / `Error` for nodes that route errors to an
|
||
extra output. `totalItems` and `returned` count across all outputs.
|
||
|
||
A node records one run for each time it ran, and `totalRuns` reports how many
|
||
when there was more than one. Which runs are read depends on the node:
|
||
|
||
- A node in the **main graph** reports its **last** run. A node inside a loop
|
||
has one run for each iteration, so this is the run the caller usually means.
|
||
- A **sub-node** — a model, a memory, a tool — reports **every** run, in call
|
||
order, because one run is one call its owner made. That is what makes this
|
||
action the answer to a step run the tool refuses. An item's label names the
|
||
call it came from, `node:Search Tickets[call 2][0][0]`, and `startIndex` and
|
||
`maxItems` page across the calls as one sequence.
|
||
|
||
A run that failed records no items, so `totalRuns` can count more runs than the
|
||
items account for. Read the error of that run from `action="get"`, which
|
||
reports it under `nodeErrors`.
|
||
|
||
### `executions(action="get-resolved-node-parameters")`
|
||
|
||
Replay expression resolution for a node's parameters against a past execution.
|
||
Returns raw `parameters`, the `resolved` tree, `failedExpressions`, and
|
||
`emptyResolutions` (values that resolved to `null`, `undefined` or `""` — the
|
||
common silent cause of empty downstream fields). Use it when debugging why a
|
||
node received an unexpected value; more precise than reading raw expressions.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `executionId` | string | yes | Execution ID |
|
||
| `nodeName` | string | yes | Node whose parameters to resolve |
|
||
| `itemIndex` | number | no | Input item index to resolve against. Defaults to `0` |
|
||
| `runIndex` | number | no | Node run to use when it ran more than once. Defaults to the last run |
|
||
|
||
### `executions(action="stop")`
|
||
|
||
Cancel a running execution.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `executionId` | string | yes | Execution to cancel |
|
||
|
||
**Returns**: `{ success: boolean, message: string }`
|
||
|
||
---
|
||
|
||
## `credentials` (6 actions)
|
||
|
||
The instance PostHog flag `120_credential_descriptions` controls description
|
||
fields and selection guidance. Only boolean `true` enables them. When the flag
|
||
is false or missing, `list` and `get` omit `description`, including managed entries.
|
||
|
||
> **Security note**: The agent never handles raw credential secrets. Credential
|
||
> creation and secret configuration is done through the n8n frontend UI (via
|
||
> `credentials(action="setup")`) or Computer Use browser credential capture.
|
||
|
||
### `credentials(action="list")`
|
||
|
||
List credentials accessible to the current user. Never exposes secrets.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `type` | string | no | Filter by credential type (e.g., `notionApi`) |
|
||
| `name` | string | no | Case-insensitive substring filter on the credential name |
|
||
| `limit` | number | no | Page size. Default 50 and maximum 200 |
|
||
| `offset` | number | no | Number of credentials to skip. Default 0 |
|
||
|
||
**Returns**: `{ credentials: [{ id, name, type, description }], total, hasMore, hint? }`.
|
||
Descriptions have a 256-character preview limit, including the truncation marker.
|
||
An unset description returns `null`. Read the descriptions when several credentials
|
||
share one type. Use `get` to read the full text if the preview does not resolve the choice.
|
||
A Gateway credits managed entry has `id: "__AI_GATEWAY_MANAGED__"`,
|
||
`__aiGatewayManaged: true`, and `description: null`.
|
||
|
||
### `credentials(action="get")`
|
||
|
||
Get credential metadata. Never returns decrypted secrets.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `credentialId` | string | yes | Credential ID |
|
||
|
||
**Returns**: `{ id, name, type, description, nodesWithAccess? }`.
|
||
The description contains the full stored text, or `null` when unset.
|
||
The response never contains credential secret data.
|
||
|
||
### `credentials(action="delete")`
|
||
|
||
Permanently delete a credential. **Irreversible** — HITL confirmation required.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `credentialId` | string | yes | Credential to delete |
|
||
| `credentialName` | string | no | Display name for the confirmation message |
|
||
|
||
**Returns**: `{ success: boolean }`
|
||
|
||
### `credentials(action="search-types")`
|
||
|
||
Search available credential types by name or description.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `query` | string | no | Search query. Required unless `gatewayCreditsOnly` is true |
|
||
| `gatewayCreditsOnly` | boolean | no | Return credential types supported by Gateway credits |
|
||
|
||
**Returns**: `{ results: [...] }`. Gateway-credits-only results have
|
||
`{ type, gatewayCredits: true }`.
|
||
|
||
### `credentials(action="setup")`
|
||
|
||
Open the credential picker UI for the user to configure credentials securely.
|
||
The LLM never sees secrets — the user interacts with the n8n frontend directly.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `credentials` | array | yes | Requests with `{ credentialType, reason?, suggestedName?, preferNew?, setupHint? }` |
|
||
| `workflowId` | string | no | The workflow the credentials are for, when one exists |
|
||
| `requireUserSelection` | boolean | no | Keep the card open for an explicit choice |
|
||
| `credentialFlow` | object | no | `{ stage: "generic" | "finalize" }` |
|
||
|
||
**Returns**: one of the completed, deferred, browser-setup, or validation-error
|
||
shapes. A completed result contains `{ success: true, credentials, message }`.
|
||
A browser handoff contains `{ success: false, needsBrowserSetup: true,
|
||
credentialType, docsUrl?, requiredFields? }`.
|
||
|
||
**HITL**: Suspends execution and renders the credential setup UI. When a single
|
||
matching *service-scoped* credential already exists, the card auto-selects it
|
||
and resolves without user input — a `success` result with a credentials map
|
||
means setup is already complete, and the card is never open once a result is
|
||
returned. Generic auth types (bearer/header/query/basic/etc.) stay preselected
|
||
but always require an explicit Continue, since the type alone does not identify
|
||
a service. When `needsBrowserSetup=true`, the orchestrator should load the
|
||
`credential-setup-with-computer-use` skill, use Computer Use `browser_*` tools
|
||
directly, then call `credentials(action="setup")` again to select the created
|
||
credential.
|
||
|
||
**Setup panel** (`118_instance_ai_setup_overhaul: variant`): when the call belongs
|
||
to a workflow (`workflowId`, or the workflow this run last saved) and the stage
|
||
is not `finalize`, the tool does not suspend. It merges the credential types
|
||
into the workflow's durable `setup-items` snapshot and returns
|
||
`{ success: true, announced: true, workflowId, credentials: [{ credentialType,
|
||
existingCredentials }], message }` so the build continues while the user
|
||
connects credentials from the panel. The announcement is built from the saved
|
||
workflow's analysis, so generic auth types land on their per-node rows; a type
|
||
no saved node uses yet gets a node-less row (generic types wait for the next
|
||
build snapshot). Standalone setup, `requireUserSelection`, and an entry with
|
||
`preferNew` keep the card: the panel cannot express "replace the bound
|
||
credential".
|
||
|
||
### `credentials(action="test")`
|
||
|
||
Test whether a credential is valid and can connect to its service.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `credentialId` | string | yes | Credential to test |
|
||
|
||
**Returns**: `{ success: boolean, message?: string }`
|
||
|
||
---
|
||
|
||
## `nodes` (7 actions)
|
||
|
||
The full domain surface has seven actions. The orchestrator receives all seven
|
||
actions in the current registry. The tool also defines a restricted
|
||
`type-definition` and `explore-resources` surface, but the orchestrator registry
|
||
does not currently select it. Specialized agents that resolve the full domain
|
||
tool can also receive all seven actions.
|
||
|
||
### `nodes(action="list")`
|
||
|
||
List available node types in the n8n instance.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `query` | string | no | Filter by name or description |
|
||
| `gatewayCreditsOnly` | boolean | no | Return only nodes supported by Gateway credits |
|
||
|
||
**Returns**: `{ nodes: [{ name, displayName, description, group, version }] }`
|
||
|
||
### `nodes(action="describe")`
|
||
|
||
Get detailed node description including properties, credentials, inputs, and outputs.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `nodeType` | string | yes | Node type (e.g., `n8n-nodes-base.httpRequest`) |
|
||
|
||
**Returns**: `{ name, displayName, description, properties, credentials, inputs, outputs }`
|
||
|
||
### `nodes(action="type-definition")`
|
||
|
||
Get TypeScript definitions for one to five node types, including exact
|
||
parameters, credentials, display conditions, and builder annotations.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `nodeTypes` | array | yes | One to five node requests. Each entry is a node type string or `{ nodeType, version?, resource?, operation?, mode? }` |
|
||
|
||
**Returns**: `{ definitions, error? }`.
|
||
|
||
### `nodes(action="search")`
|
||
|
||
Search nodes ranked by relevance with `@builderHint` annotations. Includes
|
||
subnode requirements and discriminator values.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `query` | string | no | Short search query |
|
||
| `connectionType` | string | no | AI sub-node connection type |
|
||
| `limit` | number | no | Maximum results. Default 10 |
|
||
|
||
**Returns**: `{ results, totalResults }`
|
||
|
||
### `nodes(action="suggested")`
|
||
|
||
Get curated node suggestions for common use cases.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `categories` | string[] | yes | One to three supported technique categories |
|
||
|
||
**Returns**: `{ results, unknownCategories }`.
|
||
|
||
### `nodes(action="explore-resources")`
|
||
|
||
Explore a node's dynamic resources (listSearch / loadOptions). Used to discover
|
||
discriminator values like spreadsheet IDs, calendar names, etc.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `nodeType` | string | yes | Node type |
|
||
| `version` | number | yes | Node version |
|
||
| `methodName` | string | yes | Exact annotated list-search or load-options method |
|
||
| `methodType` | `"listSearch" | "loadOptions"` | yes | Dynamic method type |
|
||
| `credentialType` | string | yes | Credential type key |
|
||
| `credentialId` | string | yes | Credential to use |
|
||
| `filter` | string | no | Search text |
|
||
| `paginationToken` | string | no | Token from a previous result |
|
||
| `currentNodeParameters` | object | no | Parameters needed by dependent lookups |
|
||
|
||
**Returns**: `{ results, paginationToken?, builderHint?, error? }`.
|
||
|
||
### `nodes(action="execute")`
|
||
|
||
Execute a single node standalone — real credentials, caller-supplied parameters
|
||
and input items — and return its real output items. The node runs through the
|
||
regular execution engine (an archived temporary workflow is created for the run
|
||
and deleted afterwards), so queue-mode worker dispatch applies. Intended for
|
||
learning a node's exact output shape before wiring downstream expressions, or
|
||
testing one node in isolation.
|
||
|
||
Before the approval prompt, `config` is validated against the generated
|
||
workflow-sdk node schema (`validateNodeConfig`) - a malformed config returns
|
||
field-level errors immediately. One exception: a missing discriminator (e.g.
|
||
`resource`/`operation`) does not block — n8n falls back to the node's defaults
|
||
at runtime, so the node can still run and cause side effects.
|
||
|
||
**Approval mirrors `executions(action="run")`** — executing one node is
|
||
equivalent to running a one-node workflow, so the same `runWorkflow` admin
|
||
policy applies (`blocked` denies; `always_allow` skips the prompt — a
|
||
standalone node request is always agent-authored, the analog of an AI-created
|
||
workflow). A *scoped* `always_allow` — the checkpoint follow-up override, which
|
||
names the workflow IDs it covers — does not skip the prompt: a standalone node
|
||
run has no workflow ID to match. Under the default `require_approval`, the tool suspends with
|
||
severity `warning`; "Always allow" persists a session grant scoped by node
|
||
type + resource + operation (`nodes:execute:<type>:<resource>:<operation>`) —
|
||
the same split the generated node TS types use, so a future per-operation
|
||
destructiveness policy plugs in without changing the key format. Later
|
||
executions of the same operation skip the prompt for the session.
|
||
|
||
A node that declares neither discriminator (HTTP Request, Set, Merge, Filter, …)
|
||
is scoped by the first of `mode`, `url`, `query`, `command` or `action` it
|
||
declares, and by node type alone when it declares none. "Always allow" is not
|
||
offered when that value is long enough to push the key past the grant column
|
||
width — one approved URL must not stand for every URL sharing its prefix.
|
||
|
||
The request envelope mirrors a workflow-sdk node (`{ type, version, config }`),
|
||
so the agent can pass a node it is building verbatim:
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `type` | string | yes | Full node type name, e.g. `n8n-nodes-base.slack` |
|
||
| `version` | number | yes | Node type version |
|
||
| `config.parameters` | object | yes | Same shape as workflow-sdk `NodeConfig.parameters` |
|
||
| `config.credentials` | object | no | Resolved credential references `{ id, name }` by credential type; n8n Connect managed credentials use `{ id: null, name, __aiGatewayManaged: true }` |
|
||
| `input` | array | no | Input items `{ json }` (defaults to one empty item) |
|
||
| `timeoutMs` | number | no | Max execution time, capped at 60s |
|
||
|
||
**Returns**: `{ status: 'success', output }` or
|
||
`{ status: 'error', error: { message, description?, nodeErrorType? } }`. The
|
||
output is the serialized items inside an `<untrusted_data
|
||
source="execution-output">` boundary, the same envelope the workflow-execution
|
||
path puts on node output — the items come from whatever service the node
|
||
called. Binary output is reduced to metadata (`fileName`, `mimeType`,
|
||
`fileSize`). Output is size-capped (a `truncated` field reports shown vs total
|
||
items).
|
||
When `N8N_AI_ALLOW_SENDING_PARAMETER_VALUES` is disabled, output items and
|
||
upstream error details are suppressed, mirroring `executions(action="run")`.
|
||
Wait states are not supported — a node that starts waiting (e.g. Wait,
|
||
send-and-wait operations) returns an error.
|
||
|
||
Limitations: the node really runs (side effects happen); expressions
|
||
referencing other nodes cannot resolve; trigger/webhook-only nodes are
|
||
rejected; credentials must be resolved references — the SDK's
|
||
placeholder/new-credential forms have no stored row and cannot execute.
|
||
|
||
---
|
||
|
||
## `searchModels`
|
||
|
||
Preliminary models.dev catalog search when choosing a model without a relevant
|
||
credential or a suitable named builder-hint recommendation. The `model-selection`
|
||
skill activates this deferred tool when model-bearing node definitions are
|
||
inspected. It can also be discovered with `search_tools` and loaded with
|
||
`load_tool`. Activation does not call the catalog. If a provider credential or Gateway credits is
|
||
available, use `nodes(action="explore-resources")` with that credential instead.
|
||
Do not use catalog search to validate an unfamiliar model or to recover from a
|
||
failed credential lookup.
|
||
|
||
| Field | Type | Required | Description |
|
||
| --- | --- | --- | --- |
|
||
| `provider` | string | yes | Canonical catalog provider ID, such as `openai`, `google`, `anthropic`, `openrouter`, `aws-bedrock`, or `azure-openai`. Trimmed and case-insensitive. Model-family names are not provider IDs. |
|
||
| `query` | string | no | Case-insensitive substring match on model IDs or names, applied before sorting and limiting. Trimmed; blank means no filter. Maximum 100 characters. |
|
||
| `limit` | integer | no | Default 10, minimum 1, maximum 10. |
|
||
|
||
For Claude through OpenRouter, use `provider: "openrouter", query: "claude"`.
|
||
For OpenAI through OpenRouter, use `query: "openai"`. `hasMore` counts only
|
||
matching eligible models.
|
||
|
||
Returns recent non-deprecated models whose catalog input and output modalities
|
||
both include text. Preview models remain eligible. Results are ordered by a valid ISO release
|
||
date, newest first, then by model ID. Missing or invalid dates sort last and are
|
||
returned as `null`. Existing catalog alias normalization removes equivalent
|
||
dated snapshots where the catalog identifies a latest alias.
|
||
|
||
The result includes exact IDs, model metadata, catalog pricing, `hasMore`,
|
||
`source`, `fetchedAt`, `freshness`, and `credentialAccess: "not_checked"`.
|
||
Missing metadata is `null`. Status is `ok`, `unknown_provider`,
|
||
`no_matching_models`, or `catalog_unavailable`. Absence from this limited
|
||
catalog result does not establish that a model is invalid.
|
||
|
||
The public catalog cache is shared across requests for one hour. Refreshes have
|
||
a five-second deadline. On failure, a snapshot younger than 24 hours can be
|
||
returned with `freshness: "stale"`. Older snapshots are not returned. Cancelling
|
||
one caller stops its wait without cancelling a refresh shared with other callers.
|
||
|
||
---
|
||
|
||
## `data-tables` (11 actions)
|
||
|
||
Full CRUD suite for n8n data tables. System columns (`id`, `createdAt`,
|
||
`updatedAt`) are reserved and auto-managed.
|
||
|
||
### Table operations
|
||
|
||
| Action | Description |
|
||
|--------|-------------|
|
||
| `list` | List data tables |
|
||
| `create` | Create a data table with columns |
|
||
| `delete` | Delete a data table after confirmation |
|
||
| `schema` | Get the table columns |
|
||
|
||
### Column operations
|
||
|
||
| Action | Description |
|
||
|--------|-------------|
|
||
| `add-column` | Add a column to a table |
|
||
| `delete-column` | Remove a column from a table |
|
||
| `rename-column` | Rename a column |
|
||
|
||
### Row operations
|
||
|
||
| Action | Description |
|
||
|--------|-------------|
|
||
| `query` | Query rows with optional filters |
|
||
| `insert-rows` | Insert one or more rows |
|
||
| `update-rows` | Update rows matching a filter |
|
||
| `delete-rows` | Delete rows matching a non-empty filter after confirmation |
|
||
|
||
---
|
||
|
||
## `workspace` (4 or 8 actions)
|
||
|
||
The registry always contains this tool. Without `workspaceService`, every call
|
||
returns an unavailable error. Folder actions are present only when
|
||
`workspaceService.listFolders` is available.
|
||
|
||
| Tool | Description |
|
||
|------|-------------|
|
||
| `list-projects` | List projects accessible to the user; on a project-scoped thread the conversation's own project carries `isCurrentProject: true` |
|
||
| `tag-workflow` | Apply tags to a workflow |
|
||
| `list-tags` | List available tags |
|
||
| `cleanup-test-executions` | Remove test execution data |
|
||
| `list-folders` | List folders (conditional) |
|
||
| `create-folder` | Create a new folder (conditional) |
|
||
| `delete-folder` | Delete a folder (conditional) |
|
||
| `move-workflow-to-folder` | Move a workflow to a folder (conditional) |
|
||
|
||
---
|
||
|
||
## `research` (2 actions)
|
||
|
||
### `research(action="web-search")`
|
||
|
||
Search the web and return ranked results. Provider priority: Brave > SearXNG > disabled.
|
||
The action remains in the schema without a provider and returns an empty result
|
||
list in that case.
|
||
|
||
| Field | Type | Required | Default | Description |
|
||
|-------|------|----------|---------|-------------|
|
||
| `query` | string | yes | — | Search query |
|
||
| `maxResults` | number | no | 5 | Max results (1–20) |
|
||
| `includeDomains` | string[] | no | — | Restrict to these domains |
|
||
|
||
**Returns**: `{ query, results: [{ title, url, snippet, publishedDate? }] }`
|
||
|
||
Results cached for 15 minutes (LRU, 100 entries).
|
||
|
||
### `research(action="fetch-url")`
|
||
|
||
Fetch a web page and extract content as markdown. Local pipeline (Readability +
|
||
Turndown). SSRF protection and result caching.
|
||
|
||
| Field | Type | Required | Default | Description |
|
||
|-------|------|----------|---------|-------------|
|
||
| `url` | string | yes | — | URL to fetch |
|
||
| `maxContentLength` | number | no | 30000 | Max content chars (max 100000) |
|
||
|
||
**Returns**: `{ url, finalUrl, title, content, truncated, contentLength, safetyFlags? }`
|
||
|
||
**Content routing**: HTML → Readability + Turndown + GFM, PDF → pdf-parse,
|
||
plain text / markdown → passthrough.
|
||
|
||
---
|
||
|
||
## Evaluation Tools
|
||
|
||
### `eval-config` (6 actions, conditional)
|
||
|
||
Manage config-based evaluations without adding evaluation nodes to the canvas.
|
||
The tool is registered only when `evaluationConfigService` is available. A
|
||
config links a workflow start node, end node, Data Table dataset, and one or
|
||
more judged metrics.
|
||
|
||
| Action | Required fields | Result and behavior |
|
||
|--------|-----------------|---------------------|
|
||
| `list` | `workflowId` | Returns `{ configs }`. |
|
||
| `get` | `workflowId`, `configId` | Returns a summary as `{ config }`, or an error. |
|
||
| `describe` | `workflowId`, `configId` | Returns full metric expressions, model details, and prompts as `{ config }`, or an error. |
|
||
| `create` | `workflowId` and all config fields | Suspends for approval, then returns `{ config }`, a denial, or an error. |
|
||
| `update` | `workflowId`, `configId`, and all config fields | Replaces the full config after approval. Read it with `describe` first. |
|
||
| `delete` | `workflowId`, `configId` | Suspends for destructive approval, then returns `{ success }` or a denial. |
|
||
|
||
The config fields are `name`, `startNodeName`, `endNodeName`, `dataTableId`, and
|
||
`metrics`. Each metric requires `name`, `preset`, `credentialId`, `model`, and
|
||
`actualAnswer`; it can also set `provider`, `outputType`, `userQuery`,
|
||
`expectedAnswer`, and `prompt`.
|
||
|
||
---
|
||
|
||
## `n8n-docs` (3 actions)
|
||
|
||
Search the current n8n documentation registry and read registered Markdown
|
||
pages. This tool is always loaded when registered.
|
||
|
||
| Action | Fields | Result |
|
||
|--------|--------|--------|
|
||
| `lookup` | Shared lookup fields plus `oauthRedirectUrl?`, `maxPages?` (default 3, max 5), and `maxContentLength?` (default 30000, max 100000) | Ranked matches and the best matching documents. |
|
||
| `search` | Shared lookup fields plus `maxResults?` (default 8, max 20) | Ranked registry matches without page content. |
|
||
| `read` | `url`, `maxContentLength?` | One document when the URL is a registry entry; otherwise an empty document list and error. |
|
||
|
||
Shared lookup fields are `query`, `intent`, `credentialType`,
|
||
`credentialDisplayName`, `documentationUrl`, and `nodeType`. `query` is optional
|
||
when the supplied credential or node context is enough. `intent` is one of
|
||
`credential-setup`, `node-help`, `hosting`, `api`, or `general`.
|
||
|
||
Results include registry metadata and can include a hint or error. Answers based
|
||
on returned documents must cite the returned page titles and public URLs.
|
||
|
||
## `parse-file` *(conditional)*
|
||
|
||
Parse an attachment from the current user message. The registry adds this tool
|
||
only when the current turn contains a parseable attachment.
|
||
|
||
| Field | Type | Required | Default | Description |
|
||
|-------|------|----------|---------|-------------|
|
||
| `attachmentIndex` | number | no | 0 | Zero-based attachment index |
|
||
| `format` | enum | no | detected | `csv`, `tsv`, `json`, `xlsx`, `text`, `markdown`, `html`, `pdf`, or `docx` |
|
||
| `hasHeader` | boolean | no | true | Treat the first CSV or TSV row as headers |
|
||
| `delimiter` | string | no | — | One-character CSV delimiter override |
|
||
| `startRow` | number | no | 0 | Pagination offset for tabular data |
|
||
| `maxRows` | number | no | 20 | Tabular rows to return, from 1 to 100 |
|
||
|
||
Tabular results contain normalized column metadata, rows, row counts,
|
||
pagination, truncation state, and warnings. Text-like results contain extracted
|
||
content and can include a title or page count. All results identify the source
|
||
attachment and can contain an error.
|
||
|
||
## `ask-user`
|
||
|
||
Suspend the run for one or more human decisions.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `questions` | array | yes | Items with `id`, `question`, `type`, and optional `options` |
|
||
| `introMessage` | string | no | Text shown above the first question |
|
||
|
||
Question type is `single`, `multi`, or `text`. The UI adds its own free-text
|
||
choice to select questions. The result is `{ answered: false }` when the user
|
||
dismisses the request. Otherwise it is `{ answered: true, answers }`, with the
|
||
question text added to every answer.
|
||
|
||
A skipped question grants no additional permission. Defaults apply only to
|
||
unspecified details within the requested task. A skipped request to expand scope
|
||
leaves the existing state intact. Report any remaining blocker without asking
|
||
the same question again.
|
||
|
||
---
|
||
|
||
## `save_user_preference` *(conditional)*
|
||
|
||
Save a durable preference for the current user. Present only when saved AI
|
||
preferences are enabled for the user (the adapter wires `aiPreferenceService`).
|
||
Always loaded, because a user can state a preference at any point in a
|
||
conversation.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `content` | string | yes | The preference in the user's own terms, at most `AI_PREFERENCE_CONTENT_MAX_LENGTH` characters |
|
||
| `scope` | `'user'` | yes | Only `user` exists in this version |
|
||
|
||
The tool does not suspend. It writes the row at once and returns
|
||
`{ ok: true, preference: { id, content, scope } }`, which the chat renders as a
|
||
card the user can edit or undo. It returns
|
||
`{ ok: false, reason, message }` for `blocked_by_admin`, `too_long`,
|
||
`scope_full`, `duplicate`, `not_permitted` or `failed`, and writes nothing in
|
||
those cases. The model relays a rejection in its own words and never says
|
||
"saved" without an `ok: true` result.
|
||
|
||
The system prompt carries the judgment of *when* to call it (see
|
||
`getPreferenceSavingSection` in `agent/system-prompt.ts`); the description
|
||
carries *what* it does.
|
||
|
||
---
|
||
|
||
## Filesystem Tools (dynamic, conditional)
|
||
|
||
Only registered when a `localMcpServer` (computer-use gateway) is connected.
|
||
Tools are dynamically created from the MCP server's advertised capabilities.
|
||
See `docs/filesystem-access.md`.
|
||
|
||
---
|
||
|
||
## Knowledge Base (sandbox workspace)
|
||
|
||
Best-practices guides and curated workflow templates are materialized under
|
||
`<workspace_root>/knowledge-base/` when a builder sandbox is available. Agents
|
||
read them with workspace tools — there is no dedicated `get-best-practices` or
|
||
template-search tool.
|
||
|
||
| Path | Description |
|
||
|------|-------------|
|
||
| `knowledge-base/index.json` | Root catalog advertising all three sections |
|
||
| `knowledge-base/best-practices/index.json` | Catalog of workflow technique guides |
|
||
| `knowledge-base/best-practices/*.md` | Best-practices documentation per technique |
|
||
| `knowledge-base/templates/index.json` | Catalog of curated SDK workflow examples |
|
||
| `knowledge-base/templates/*.ts` | Template workflow source files |
|
||
| `knowledge-base/reference/index.json` | Catalog of SDK reference material |
|
||
| `knowledge-base/reference/*.md` | SDK language and output-shape reference |
|
||
|
||
The tree is written by `src/knowledge-base/materialize-knowledge-base.ts`, which
|
||
sources best practices and some reference material from
|
||
`@n8n/workflow-sdk/prompts/*`, additional reference documents from the local
|
||
`knowledge-base/reference/` directory, and templates from the host's
|
||
`BuilderTemplatesService`. It also writes a workspace manifest alongside the
|
||
root index.
|
||
|
||
Use `workspace_read_file` and `workspace_grep` (or shell equivalents in the
|
||
sandbox) to consult these before planning or building non-trivial workflows.
|
||
|
||
---
|
||
|
||
## Agent Builder Tool
|
||
|
||
### `build-agent` *(orchestration tool — requires the `agents` backend module)*
|
||
|
||
Delegates agent building to the agents-module builder chat
|
||
(`AgentsBuilderService`) running as an embedded sub-agent: one conversational
|
||
turn per call. Registered in `createOrchestrationTools` only when the host
|
||
provides `builderDelegate` (agents module active). The builder's own prompt
|
||
and tools drive the build, including its interactive tools (`ask_questions`,
|
||
`ask_credential`, `ask_embedding_credential`, `configure_channel`, and
|
||
`call_agent` target-tool approvals) and
|
||
lifecycle tools (`publish_agent`, `unpublish_agent`) on the bound target agent —
|
||
the sub-agent session no longer excludes them. Forward publish/unpublish/
|
||
activate/make-live intents to `build-agent`; never tell the user to open the
|
||
agent editor and click Publish. The builder also inherits the orchestrator's
|
||
validated, approval-wrapped MCP connector tools so it can use the same external
|
||
context while designing the agent; connector tools that conflict with a native
|
||
builder tool name are skipped. Builder session state is keyed to
|
||
instance-AI-scoped threads (`ia-builder:<threadId>:<agentId>`) and never
|
||
appears in the agents-module builder UI.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `message` | string | yes | Instruction or user message to forward to the builder — the builder cannot see this chat, so include every requirement, decision, and answer already gathered, not just the latest message |
|
||
| `name` | string | no | Agent name — switches back to the agent with that name built earlier in this conversation, or creates a new agent and makes it the active target; omit on follow-up calls for the current agent |
|
||
| `agentId` | string | no | Existing agent id to edit — use the `agentId` returned by earlier build-agent results; pass to start editing that agent or to switch the active build target; omit on follow-up calls |
|
||
| `workflowContext` | array | no | `{ id, name, description? }` refs to session-built workflows the builder may attach as tools |
|
||
|
||
**Returns**: `{ ok: true, builderReply, configUpdated, agentId,
|
||
agentName?, requiredArtifacts? }` on success, or `{ ok: false, error,
|
||
configUpdated?, agentId?, agentName?, requiredArtifacts? }` on failure
|
||
(`agentId`/`agentName` identify the targeted agent
|
||
once a builder turn was dispatched; precondition failures before any turn
|
||
omit them). `configUpdated` is optional: it's included (reporting mutations
|
||
from passes that already ran) once a builder turn has actually been
|
||
dispatched — mid-turn failures and resume failures that still carry a prior
|
||
checkpoint ref — but omitted for precondition failures before any turn
|
||
starts (agents module not configured, missing `name`/`agentId`, no project
|
||
context to bind `agentId`, or a resume whose suspend payload has no
|
||
checkpoint ref to carry).
|
||
|
||
`requiredArtifacts` contains structured workflows or data tables that the
|
||
embedded builder cannot create. Build an `agent-tool` workflow and pass it back
|
||
through `workflowContext`. Build an `agent-entrypoint` workflow around the
|
||
returned Agent ID and never attach it to the Agent; this is used for unsupported
|
||
chat channels whose trigger and reply nodes live in a workflow. Requirements
|
||
reported before an interactive suspension are carried across its checkpoint.
|
||
|
||
**Interactive requests:** when the builder suspends on one of its interactive
|
||
tools (batched questions, a credential picker, channel setup, or a standard SDK
|
||
approval requested by a target-agent test run), this tool
|
||
cascades the suspension through its own suspend/resume so it renders as a
|
||
chat card directly in the assistant conversation — no manual relaying, and the
|
||
suspension survives a process restart. On resume, the tool takes the target
|
||
agent from the checkpoint ref carried in the suspend payload (falling back
|
||
to the persisted active binding for older checkpoints), re-derives the
|
||
builder's open suspension from persistence, and verifies they match the
|
||
suspension it originally cascaded before routing the answer back; a stale
|
||
or superseded suspension fails the call instead of silently resuming the
|
||
wrong one.
|
||
|
||
**Targeting:** the first call must pass `name` (new agent) or `agentId`
|
||
(existing agent); the active target is persisted to thread metadata so
|
||
follow-up calls keep editing the same agent without repeating them. The
|
||
target is rebindable: a `name` matching an agent already targeted this
|
||
conversation switches back to it (tracked in a per-thread registry), while
|
||
an unmatched name creates another agent and switches to it (the same name
|
||
as the active target just continues it), a different `agentId` switches to
|
||
that agent (persisted only once the builder turn settles, so a bad id
|
||
cannot clobber the existing binding), and `agentId` wins when both are
|
||
given. Prefer switching by the `agentId` returned from earlier calls; the
|
||
name lookup is the fallback when the id is unknown.
|
||
|
||
### `agent-context` *(domain tool — requires the `agents` backend module)*
|
||
|
||
Read-only access to Agent context in the conversation's bound project. The host
|
||
registers the tool only when the user has `agent:read` scope. Both the Assistant
|
||
and Agent Builder use this tool.
|
||
|
||
The `type` field selects one lookup. Supported values are `agents`, `config-schema`, `config`,
|
||
`skills`, `skill`, `tasks`, `custom-tools`, `custom-tool`, `sessions`, `session`,
|
||
`capabilities`, `integrations`, and `attachable-workflows`. Detailed lookups
|
||
return one body at a time. Session lookup supports status, origin, date, and
|
||
cursor filters.
|
||
|
||
The Agent id is optional when the conversation has a bound Agent target. Use
|
||
`type: "agents"` to resolve the id in other conversations. The tool labels the
|
||
returned config as the current draft. It wraps all returned context as untrusted
|
||
data before it returns it to the model.
|
||
|
||
## MCP Registry Tool
|
||
|
||
### `mcp-servers` *(domain tool — conditional)*
|
||
|
||
Tool to interact with connected and available MCP servers, and to let the user connect one from the chat.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `action` | `'connected' \| 'details' \| 'search' \| 'connect'` | yes | Discriminator |
|
||
| `slug` | string | `details` | Server slug, as returned by `connected` |
|
||
| `queries` | string[] | `search` | Free-text queries matched against server name, title, description |
|
||
| `serverSlugs` | string[] | `connect` | Slugs returned by `search`, best match first, max 3 |
|
||
| `reason` | string | `connect` | One sentence for the confirmation record |
|
||
|
||
**`connected`** → `{ servers: [{ slug, toolCount }], hint? }`. Every connected MCP
|
||
server, counts only — names are `details`' job.
|
||
|
||
**`details`** → `{ slug, tools, hint? }`. One server's tool names. `hint` tells an
|
||
unconnected slug apart from a connected server that loaded no tools.
|
||
|
||
**`search`** → `{ results: [{ slug, title, description, tools }], hint? }`, capped
|
||
at 5, most relevant first. Only servers the user has *not* connected come back.
|
||
|
||
**`connect`** → `{ connectedSlugs, message }`. Suspends to render the inline
|
||
**Available tools** card, resuming when the user connects or skips. `connectedSlugs`
|
||
are the ones the server confirms on resume, not the ones the client claimed.
|
||
|
||
## Conversation History Tool
|
||
|
||
### `conversation-history` *(domain tool — conditional, orchestrator only)*
|
||
|
||
Read-only recall over the user's past conversations in the current project.
|
||
Scoped to the current user and project, with the current thread excluded from
|
||
search. Registered only when the host wires `conversationHistoryService` — the
|
||
user is in the `109_instance_ai_conversation_history` experiment and the run
|
||
has a bound project — and only onto the orchestrator: sub-agents get
|
||
their context from briefings, not by reading across threads. Always loaded:
|
||
recall only works proactively, and deferred it was only reached when the user
|
||
explicitly asked about past conversations. The system prompt's "Past
|
||
Conversations" section describes the situations where recall helps (an
|
||
example-based list, not hard rules — mandates proved both repetitive and
|
||
over-aggressive), and the host appends
|
||
a `<past-conversations>` block (recent titles + count) to the first user
|
||
message of a thread whose project has history — the ambient cue that makes the
|
||
tool's relevance self-evident.
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `action` | `'search' \| 'get-messages'` | yes | Discriminator |
|
||
| `query` | string | no | Case-insensitive text matched against titles, user messages, and ask-user answers (2–200 chars) as one exact phrase — the description steers the model toward fewer, short, distinctive terms. Omitted → `search` lists the most recent conversations instead |
|
||
| `limit` | number | no | Max conversations to return (default 10 when searching, 5 when listing recent; max 10) |
|
||
| `threadId` | string | `get-messages` | Conversation id from a search result |
|
||
| `aroundMessageId` | string | no | Center the read on this message id (from a search excerpt) |
|
||
| `before` | number | no | Messages before the anchor; without `aroundMessageId`, the last N messages (max 5) |
|
||
| `after` | number | no | Messages after the anchor; without `aroundMessageId`, the first N messages (max 5) |
|
||
|
||
`before` and `after` can only be combined with `aroundMessageId` — passing both
|
||
without an anchor is a schema-level rejection.
|
||
|
||
**`search`** → `{ hits: [{ threadId, title, updatedAt, matchedIn, firstMessageExcerpt?, excerpts: [{ messageId, text, createdAt }] }], error? }`,
|
||
recency-ordered. `matchedIn` is an array containing zero or more of `'title' | 'messages' | 'user-answers'`.
|
||
The SQL prefilter is a LIKE over serialized JSON, so candidates are re-checked
|
||
against the text a reader would see, one page at a time; a thread with neither
|
||
a title match nor a re-checked excerpt is dropped. There are no counts. Threads
|
||
with no messages are never returned. Without a `query` the same shape carries a
|
||
recency listing: empty `matchedIn`/`excerpts` — pair it with a `get-messages`
|
||
tail read to continue recent work.
|
||
|
||
**`get-messages`** → `{ threadId, title, messages: [{ messageId, role, createdAt, text, userAnswers?: [{ question, answer }] }], hasMoreBefore, hasMoreAfter, error? }`,
|
||
oldest-first. Defaults for the read window (tail/head/around sizing) are
|
||
applied by the service, not the tool. The read is the conversation as the
|
||
user experienced it: their messages, ask-user Q&A, and each turn's final
|
||
text-only reply. Mid-turn assistant rows — the agent loop only continues on
|
||
tool calls, so a row carrying them is working narration rather than the reply
|
||
that ended the turn — are filtered out in SQL via structural markers
|
||
(unescaped `"type":"tool-call"` can only be block structure — quotes inside
|
||
text are escaped); ask-user rows stay visible for their Q&A. Rows only
|
||
recognizable after parsing — internal auto-follow-up user rows, rows with no
|
||
visible text, ask-user rows still awaiting an answer, unreadable content — are
|
||
dropped by the same visibility predicate the window fetch uses, so
|
||
`before`/`after` count returned messages. The fetch over-reads to fill its
|
||
slots; `hasMoreBefore`/`hasMoreAfter` may over-report after a long run of
|
||
invisible rows, never under-report.
|
||
|
||
Both actions return `{ ..., error: '...' }` with empty/default fields — never a
|
||
thrown tool error — when the service is unavailable or a lookup fails.
|
||
|
||
## Tool Distribution
|
||
|
||
The orchestrator receives the safe native domain tools and orchestration tools
|
||
from `src/tools/index.ts`. Its workflow tool omits raw workflow JSON reads and
|
||
full-definition replacements. It receives the full six-action `nodes` tool.
|
||
External and local MCP tools are added after their names are checked against the
|
||
native tools active for the current request.
|
||
|
||
The embedded Agent Builder uses the agents-module builder's own tool surface
|
||
through `build-agent`. It does not receive the Instance AI domain registry. It
|
||
inherits the orchestrator's safe MCP connector tools.
|
||
|
||
---
|
||
|
||
## Adding New Tools
|
||
|
||
Most additions are a new **action on an existing domain tool** rather than a new
|
||
tool. Add a top-level tool only when the operation does not belong to an
|
||
existing domain.
|
||
|
||
**Adding an action:**
|
||
|
||
1. Add an action schema to the domain's `src/tools/<domain>.tool.ts` and include
|
||
it in that tool's discriminated union
|
||
2. Give every field a `.describe()` — these are the LLM's parameter docs
|
||
3. If it needs a new service method, add it to the interface in `src/types.ts`
|
||
and implement it in the backend adapter
|
||
4. Gate it on host capability if applicable, so the action is absent from the
|
||
union when unsupported
|
||
|
||
**Adding a tool:**
|
||
|
||
1. Create `src/tools/<name>.tool.ts` (domain) or
|
||
`src/tools/orchestration/<name>.tool.ts` (orchestration)
|
||
2. Add its id to `DOMAIN_TOOL_IDS` or `ORCHESTRATION_TOOL_IDS` in
|
||
`src/tools/tool-ids.ts`
|
||
3. Export a factory that takes the service context and returns an `@n8n/agents` tool
|
||
4. Register it in `src/tools/index.ts` with `createOrchestratorDomainTools` or
|
||
`createOrchestrationTools`
|
||
5. Decide whether it belongs in `ALWAYS_LOADED_TOOL_NAMES`. Everything not in
|
||
that set is normally reached through `search_tools` + `load_tool`. Tools in
|
||
`CHECKPOINT_FOLLOW_UP_TOOL_NAMES` are also loaded directly during checkpoint
|
||
follow-ups. Deferral is the right default, but a tool whose job is to reveal
|
||
an absence, or to redirect the model's attention, cannot be found by
|
||
searching for it
|
||
6. For HITL tools, define `suspendSchema` and `resumeSchema` — `@n8n/agents` handles
|
||
the suspension/resume lifecycle automatically
|
||
7. Tool handlers are wrapped at registry registration time so Stop races
|
||
`ctx.abortSignal`. For network/sandbox I/O, also forward `ctx.abortSignal`
|
||
into the underlying request so work stops cooperatively (see `research` and
|
||
`n8n-docs`)
|