/n8n-code-tool
Write JavaScript or Python for the n8n Custom Code Tool (@n8n/n8n-nodes-langchain.toolCode) — the AI-agent-callable tool, NOT the workflow Code node. Use when building a Code Tool attached to an AI Agent, writing code that an LLM will invoke, parsing the `query` input, returning
$ npx -y skills add czlonkowski/n8n-skills --skill n8n-code-tool --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/n8n-code-tool
Context preview
The summary Claude sees to decide when to auto-load this skill.
Write JavaScript or Python for the n8n Custom Code Tool (@n8n/n8n-nodes-langchain.toolCode) — the AI-agent-callable tool, NOT the workflow Code node. Use when building a Code Tool attached to an AI Agent, writing code that an LLM will invoke, parsing the `query` input, returning
SKILL.md
n8n-code-tool.SKILL.mdname: n8n-code-tool
description: "Write JavaScript or Python for the n8n Custom Code Tool (@n8n/n8n-nodes-langchain.toolCode) — the AI-agent-callable tool, NOT the workflow Code node. Use when building a Code Tool attached to an AI Agent, writing code that an LLM will invoke, parsing the `query` input, returning a string result, defining an input schema for structured arguments (specifyInputSchema, jsonSchemaExample, DynamicStructuredTool), or troubleshooting errors like \"Wrong output type returned\", \"No execution data available\", \"The response property should be a string, but it is an object\", \"Cannot assign to read only property 'name'\", or an AI agent that refuses to call the tool. Covers the critical differences between Code node and Code Tool: return format (string vs `[{json:{...}}]`), unavailability of `$fromAI`/`$input`/`$helpers` in the Code Tool sandbox, naming rules for AI invocation, and when to use `toolWorkflow`/HTTP Request Tool instead."n8n Custom Code Tool
Expert guidance for writing code inside `@n8n/n8n-nodes-langchain.toolCode` — the tool an AI Agent can invoke, **not** the regular workflow Code node.
---
⚠️ This is NOT the Code node
The Custom Code Tool looks like a Code node in the editor — same JavaScript editor, similar layout — but it is a **completely different node** from a different package with a **different runtime contract**.
| | Code node | Custom Code Tool | |---|---|---| | **Node type** | `n8n-nodes-base.code` | `@n8n/n8n-nodes-langchain.toolCode` | | **Package** | `n8n-nodes-base` | `@n8n/n8n-nodes-langchain` | | **Invoked by** | Previous node (workflow flow) | AI Agent (LangChain) | | **Input** | `$input.all()` — item stream | `query` — string or object from LLM | | **Return** | `[{json: {...}}]` (items array) | **A string** | | **`$fromAI()`** | N/A | **Not available** (see Errors) | | **HTTP helper** | `this.helpers.httpRequest` (auth helpers blocked) | Not exposed to the tool sandbox | | **State** | Per-run execution data | No `getContext`, no `$getWorkflowStaticData` |
**If you treat it like a Code node, it fails.** The rest of this skill covers the Code Tool's actual contract.
---
Quick Start
Minimal JavaScript Code Tool
// `query` is whatever the AI sent (a string by default)
return `You asked: ${query}`;Minimal Python Code Tool
# `_query` is whatever the AI sent (a string by default)
return f"You asked: {_query}"Essential Rules
1. **Return a string.** Numbers are auto-converted. Anything else throws `"The response property should be a string, but it is an object"`. 2. **Input variable is fixed**: `query` (JS), `_query` (Python). You cannot rename it. 3. **Do NOT use `$fromAI()`** inside the Code Tool sandbox — it throws `"No execution data available"`. 4. **Do NOT use `[{json: {...}}]`** return format — that's for Code nodes. Throws `"Wrong output type returned"`. 5. **Use a descriptive tool name** (letters/numbers/underscores, v1.1+). The agent calls the tool by its name. 6. **Write a precise description** — the LLM decides whether to invoke the tool based on it.
---
The Two Input Modes
The Code Tool has two input shapes, controlled by `specifyInputSchema`:
Mode 1: Unstructured (default, `specifyInputSchema: false`)
The AI passes **a single string** as `query`. If you need multiple fields, the AI has to stuff them into that one string and you parse them out. In practice, LLMs will happily pass a JSON string if your description tells them to.
// Parse a JSON string the AI sent
let params;
try {
params = typeof query === 'string' ? JSON.parse(query) : query;
} catch (e) {
throw new Error('Expected a JSON object. Parser said: ' + e.message);
}
const price = Number(params.price);
const months = Number(params.months);
// ...
return JSON.stringify({ monthly_payment: /* ... */ });**Pros**: simplest to set up, one field to describe. **Cons**: no schema validation — if the LLM forgets a field, the tool throws at runtime.
**Best for**: quick prototypes, tools with one natural input (a question, a URL, a text blob).
Mode 2: Structured (`specifyInputSchema: true`)
The tool becomes a LangChain `DynamicStructuredTool`. The LLM sees a typed argument schema and passes a **validated object** as `query`. You access fields directly.
// query is now an object matching your schema
const price = query.price;
const months = query.months;
const residual_percent = query.residual_percent;
const monthly = computeAnnuity(price, months, residual_percent);
return JSON.stringify({ monthly_payment: monthly });Schema is defined via either:
- `schemaType: "fromJson"` + `jsonSchemaExample` (n8n v≥1.3) — paste an example JSON, n8n infers the schema
- `schemaType: "manual"` + `inputSchema` — write a full JSON Schema yourself
**Pros**: LLM gets type hints, invalid calls rejected before your code runs, cleaner code. **Cons**: a little more setup; requires n8n version with schema support.
**Best for**: production tools with multiple typed parameters (calculators, API wrappers, anything with numeric fields the LLM tends to stringify).
**See**: [INPUT_SCHEMA.md](INPUT_SCHEMA.md) for complete schema setup.
---
Return Format
**The return value must be a string.** The LLM reads it as the tool's observation.
// ✅ String
return "42";
// ✅ Number (auto-converted to string by n8n)
return 42;
// ✅ JSON-encoded structured result (recommended for rich output)
return JSON.stringify({ result: 42, currency: "SEK" });
// ❌ Raw object → "The response property should be a string, but it is an object"
return { result: 42 };
// ❌ Workflow item format → "Wrong output type returned"
return [{ json: { result: 42 } }];
// ❌ Array → "The response property should be a string, but it is an object"
return [1, 2, 3];Best practice: JSON-stringify structured results
When your tool has more than a trivial scalar output, return
Read more
name: n8n-code-tool
description: "Write JavaScript or Python for the n8n Custom Code Tool (@n8n/n8n-nodes-langchain.toolCode) — the AI-agent-callable tool, NOT the workflow Code node. Use when building a Code Tool attached to an AI Agent, writing code that an LLM will invoke, parsing the `query` input, returning a string result, defining an input schema for structured arguments (specifyInputSchema, jsonSchemaExample, DynamicStructuredTool), or troubleshooting errors like \"Wrong output type returned\", \"No execution data available\", \"The response property should be a string, but it is an object\", \"Cannot assign to read only property 'name'\", or an AI agent that refuses to call the tool. Covers the critical differences between Code node and Code Tool: return format (string vs `[{json:{...}}]`), unavailability of `$fromAI`/`$input`/`$helpers` in the Code Tool sandbox, naming rules for AI invocation, and when to use `toolWorkflow`/HTTP Request Tool instead."n8n Custom Code Tool
Expert guidance for writing code inside `@n8n/n8n-nodes-langchain.toolCode` — the tool an AI Agent can invoke, **not** the regular workflow Code node.
---
⚠️ This is NOT the Code node
The Custom Code Tool looks like a Code node in the editor — same JavaScript editor, similar layout — but it is a **completely different node** from a different package with a **different runtime contract**.
| | Code node | Custom Code Tool | |---|---|---| | **Node type** | `n8n-nodes-base.code` | `@n8n/n8n-nodes-langchain.toolCode` | | **Package** | `n8n-nodes-base` | `@n8n/n8n-nodes-langchain` | | **Invoked by** | Previous node (workflow flow) | AI Agent (LangChain) | | **Input** | `$input.all()` — item stream | `query` — string or object from LLM | | **Return** | `[{json: {...}}]` (items array) | **A string** | | **`$fromAI()`** | N/A | **Not available** (see Errors) | | **HTTP helper** | `this.helpers.httpRequest` (auth helpers blocked) | Not exposed to the tool sandbox | | **State** | Per-run execution data | No `getContext`, no `$getWorkflowStaticData` |
**If you treat it like a Code node, it fails.** The rest of this skill covers the Code Tool's actual contract.
---
Quick Start
Minimal JavaScript Code Tool
// `query` is whatever the AI sent (a string by default)
return `You asked: ${query}`;Minimal Python Code Tool
# `_query` is whatever the AI sent (a string by default)
return f"You asked: {_query}"Essential Rules
1. **Return a string.** Numbers are auto-converted. Anything else throws `"The response property should be a string, but it is an object"`. 2. **Input variable is fixed**: `query` (JS), `_query` (Python). You cannot rename it. 3. **Do NOT use `$fromAI()`** inside the Code Tool sandbox — it throws `"No execution data available"`. 4. **Do NOT use `[{json: {...}}]`** return format — that's for Code nodes. Throws `"Wrong output type returned"`. 5. **Use a descriptive tool name** (letters/numbers/underscores, v1.1+). The agent calls the tool by its name. 6. **Write a precise description** — the LLM decides whether to invoke the tool based on it.
---
The Two Input Modes
The Code Tool has two input shapes, controlled by `specifyInputSchema`:
Mode 1: Unstructured (default, `specifyInputSchema: false`)
The AI passes **a single string** as `query`. If you need multiple fields, the AI has to stuff them into that one string and you parse them out. In practice, LLMs will happily pass a JSON string if your description tells them to.
// Parse a JSON string the AI sent
let params;
try {
params = typeof query === 'string' ? JSON.parse(query) : query;
} catch (e) {
throw new Error('Expected a JSON object. Parser said: ' + e.message);
}
const price = Number(params.price);
const months = Number(params.months);
// ...
return JSON.stringify({ monthly_payment: /* ... */ });**Pros**: simplest to set up, one field to describe. **Cons**: no schema validation — if the LLM forgets a field, the tool throws at runtime.
**Best for**: quick prototypes, tools with one natural input (a question, a URL, a text blob).
Mode 2: Structured (`specifyInputSchema: true`)
The tool becomes a LangChain `DynamicStructuredTool`. The LLM sees a typed argument schema and passes a **validated object** as `query`. You access fields directly.
// query is now an object matching your schema
const price = query.price;
const months = query.months;
const residual_percent = query.residual_percent;
const monthly = computeAnnuity(price, months, residual_percent);
return JSON.stringify({ monthly_payment: monthly });Schema is defined via either:
- `schemaType: "fromJson"` + `jsonSchemaExample` (n8n v≥1.3) — paste an example JSON, n8n infers the schema
- `schemaType: "manual"` + `inputSchema` — write a full JSON Schema yourself
**Pros**: LLM gets type hints, invalid calls rejected before your code runs, cleaner code. **Cons**: a little more setup; requires n8n version with schema support.
**Best for**: production tools with multiple typed parameters (calculators, API wrappers, anything with numeric fields the LLM tends to stringify).
**See**: [INPUT_SCHEMA.md](INPUT_SCHEMA.md) for complete schema setup.
---
Return Format
**The return value must be a string.** The LLM reads it as the tool's observation.
// ✅ String
return "42";
// ✅ Number (auto-converted to string by n8n)
return 42;
// ✅ JSON-encoded structured result (recommended for rich output)
return JSON.stringify({ result: 42, currency: "SEK" });
// ❌ Raw object → "The response property should be a string, but it is an object"
return { result: 42 };
// ❌ Workflow item format → "Wrong output type returned"
return [{ json: { result: 42 } }];
// ❌ Array → "The response property should be a string, but it is an object"
return [1, 2, 3];Best practice: JSON-stringify structured results
When your tool has more than a trivial scalar output, return
Expert Claude Code skills for building flawless n8n workflows using the n8n-mcp MCP server
Repo: czlonkowski/n8n-skills
Other skills on n8n-mcp-skills.
- /n8n-agents
Design n8n AI agents the right way. Use when building or editing any @n8n/n8n-nodes-langchain.* AI node — an AI Agent, LLM chain, Text Classifier, or Information Extractor — and whenever the user mentions AI agents, LLM with tools, tool calling, $fromAI, system prompts, agent
Open skill - /n8n-binary-and-data
Handle files and binary data in n8n correctly. Use when working with files, images, PDFs, attachments, uploads or downloads, base64, vision/multimodal input, or when an AI agent needs a file as tool input or output — and whenever the user mentions $binary, binaryPropertyName,
Open skill - /n8n-code-javascript
Write JavaScript code in n8n Code nodes. Use when writing JavaScript in n8n, using $input/$json/$node syntax, making HTTP requests with this.helpers / the $helpers global, working with dates using DateTime, troubleshooting Code node errors, choosing between Code node modes, or
Open skill - /n8n-code-python
Write Python code in n8n Code nodes. Use when writing Python in n8n, using _input/_json/_node syntax, working with standard library, or need to understand Python limitations in n8n Code nodes. Use this skill when the user specifically requests Python for an n8n Code node. Note —
Open skill - /n8n-error-handling
Wire n8n error handling so failures are loud, structured, and recoverable. Use when building any webhook/API workflow, a scheduled or unattended workflow, or any path where a silent failure would drop user-visible work — and whenever the user mentions error handling, onError,
Open skill - /n8n-expression-syntax
Validate n8n expression syntax and fix common errors. Use when writing n8n expressions, using {{}} syntax, accessing $json/$node variables, troubleshooting expression errors, mapping data between nodes, or referencing webhook data in workflows. Use this skill whenever
Open skill

