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
Install / Use
npx skills add czlonkowski/n8n-skills --skill n8n-code-toolInstalls into whichever agent you are using.
SKILL.md
Installable skill definition
Quality Score
Category
AutomationSupported Platforms
Our assessment of n8n-code-tool
n8n-code-tool scores 96/100 on our quality scale, 196th of 2,988 Automation skills we index (top 7%).
Its SKILL.md is 16 KB long, well organised into 29 sections with 10 code examples: a thorough specification that gives an agent plenty to work with.
With 6,309 GitHub stars, it is one of the more widely adopted skills in the catalogue.
Maintenance, license and trust
- The repository was last updated 15 days ago, so n8n-code-tool is actively maintained.
- It is released under the MIT license, a permissive license that allows use, modification and commercial use with attribution.
- Its trust signals score 100/100, with no cautions. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.
n8n-code-tool compared with similar skills
All 4 of these similar skills score higher than n8n-code-tool; compare them before choosing.
| Skill | Score | Stars | Updated | Format |
|---|---|---|---|---|
| n8n-code-tool (this skill)by czlonkowski | 96 | 6.3k | 15d ago | SKILL.md |
| Agent-Reachby Panniantong | 100 | 87.6k | 16d ago | CLAUDE.md |
| headroomby headroomlabs-ai | 100 | 74.3k | today | CLAUDE.md |
| rufloby ruvnet | 100 | 73.7k | today | CLAUDE.md |
| Scraplingby D4Vinci | 100 | 85.0k | 1d ago | MCP Server |
Frequently asked questions
- How do I install n8n-code-tool?
- Run
npx skills add czlonkowski/n8n-skills --skill n8n-code-tool. The install tabs above show the steps for each supported agent. - Which AI agents does n8n-code-tool work with?
- It is written for Universal, as a SKILL.md file. Other agents that read the same format can often use it too.
- Is n8n-code-tool safe to use?
- It is MIT-licensed and scores 100/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
- Is n8n-code-tool still maintained?
- The repository was last updated 15 days ago, so n8n-code-tool is actively maintained.
Skill content
View source on GitHubname: 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
- Return a string. Numbers are auto-converted. Anything else throws
"The response property should be a string, but it is an object". - Input variable is fixed:
query(JS),_query(Python). You cannot rename it. - Do NOT use
$fromAI()inside the Code Tool sandbox — it throws"No execution data available". - Do NOT use
[{json: {...}}]return format — that's for Code nodes. Throws"Wrong output type returned". - Use a descriptive tool name (letters/numbers/underscores, v1.1+). The agent calls the tool by its name.
- 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 schemaschemaType: "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 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 a JSON string:
return JSON.stringify({
monthly_payment_sek: 5405,
loan_amount: 351920,
total_cost_of_credit: 63295
});
The LLM parses JSON reliably and can pick the fields it needs to present to the user.
Error handling: the agent reads your failures
Errors don't just stop the workflow — they go back to the LLM, which usually corrects its call and retries. Use that:
// Option A: throw — n8n surfaces the message to the agent
if (!isFinite(price)) throw new Error('price must be a number, e.g. 439900');
// Option B: return an error string — agent reads it like any tool result
if (!isFinite(price)) return JSON.stringify({ error: 'price must be a number, e.g. 439900' });
Either way, write error messages for the LLM: state what was wrong and what a valid call looks like. A bare throw new Error('invalid input') wastes the retry; an instructive message usually fixes the next call.
Tool Name and Description
These fields are NOT documentation — they are the tool contract the LLM sees. Treat them as prompt engineering.
Name
- Must match
[A-Za-z0-9_]+(v1.1+). No spaces, no hyphens, no emoji. - Use a verb-y descriptive name:
calculate_car_loan,get_weather,search_orders. - The agent calls the tool by this name.
Code Tool(the default) is useless — the agent won't know when to call it.
Description
- Explain when to use it and what to send.
- If unstructured mode, include an example of the JSON string the LLM should send.
- If structured mode, the schema speaks for itself — just describe purpose.
Unstructured example (JSON-in-string pattern):
Deterministiskt beräknar månadskostnad för billån. Anropa med EN JSON-sträng:
{"price":439900,"down_payment":87980,"interest_rate":6.95,"months":36,"residual_percent":50}
Fält: price (SEK), down_payment (SEK), interest_rate (% per år), months, residual_percent (0-99).
Structured example (schema-defined):
Deterministically computes the monthly car-loan payment given price, down payment,
annual interest rate, term, and residual percent. Use whenever the user asks for
monthly cost, total credit cost, or loan breakdown.
Top Errors and Fixes
Error 1: "There was an error: 'Cannot assign to read only property \"name\" of object: Error: No execution data available'"
Cause: you called $fromAI() inside the Code Tool sandbox.
Fix: $fromAI() is a helper for other tool-enabled nodes (HTTP Request Tool, SendGrid Tool, toolWorkflow, etc.) — it's not exposed inside toolCode. Read the AI's input from query directly (or use specifyInputSchema for structured fields).
Error 2: "Wrong output type returned"
Cause: you returned a workflow-style array like [{ json: { ... } }]. That's the Code node contract, not the Code Tool contract.
Fix: return a string. For structured data, return JSON.stringify(output).
Error 3: "The response property should be a string, but it is an object"
Cause: you returned a plain object or array.
Fix: JSON.stringify() the result, or coerce to a string.
Error 4: AI never calls the tool
Cause: tool name is generic (Code Tool, My Tool) or description doesn't clearly state when to use it.
Fix: rename to a verb-y name (calculate_car_loan), and rewrite the description to explicitly state the trigger conditions (e.g. "Use this whenever the user asks about monthly cost").
Error 5: AI sends garbage into query
Cause: unstructured tool with a vague description. The LLM guesses at the format.
Fix: either (a) include a concrete JSON example in the description, or (b) switch to specifyInputSchema: true so the LLM gets a typed schema.
See: ERROR_PATTERNS.md for full catalog with reproductions.
What's NOT Available in the Sandbox
The Code Tool sandbox is narrower than the Code node sandbox. Don't assume helpers carry over:
| Helper | Code node | Code Tool |
|---|---|---|
| $input.all(), $input.first(), $input.item | ✅ | ❌ |
| $node["NodeName"] | ✅ | ❌ |
| $json, $binary | ✅ | ❌ |
| $fromAI() | ❌ | ❌ (despite sitting next to an AI agent) |
| this.helpers.httpRequest() | ✅ | ❌ |
| DateTime (Luxon) | ✅ | ✅ (standard in JS sandbox) |
| $jmespath() | ✅ | ❌ |
| this.getContext(...) | ✅ | ❌ |
| $getWorkflowStaticData(...) | ✅ | ❌ |
Implication: the Code Tool is for pure computation. If you need an HTTP call, an API lookup, or cross-invocation state, use a different tool node:
- HTTP Request Tool for external API calls
toolWorkflow(Call Sub-workflow Tool) for multi-step logic with access to the full Code node sandbox- MCP / database tools for persistent state
When to Use Code Tool vs Alternatives
Use Code Tool when:
- ✅ Pure deterministic computation (math, parsing, formatting, validation)
- ✅ Lightweight transformations the LLM shouldn't do itself (precision math, regex)
- ✅ You want the code inline in the workflow, not in a separate sub-workflow
Use toolWorkflow (Call Sub-workflow Tool) when:
- ✅ You need multiple parameters with clean
$fromAI()typing - ✅ You need access to
this.helpers, credentials, or other nodes - ✅ Logic is reusable across agents
- ✅ You want structured typed inputs WITHOUT writing a JSON Schema
Use HTTP Request Tool when:
- ✅ The tool is fundamentally a single API call
- ✅ You want per-parameter
$fromAI()bindings in URL/query/body
Rule of thumb: if you find yourself wanting $fromAI(), you probably want toolWorkflow instead of toolCode.
Complete Working Example
A production calculator tool (unstructured, JSON-in-string pattern):
{
"parameters": {
"name": "calculate_car_loan",
"description": "Computes monthly car-loan payment using an annuity formula with residual/balloon. Call with a single JSON string. Example: {\"price\":439900,\"down_payment\":879
Truncated for display — read the full file on GitHub.
Related Skills
Agent-Reach
87.6kGive your AI agent eyes to see the entire internet. Read & search Twitter, Reddit, YouTube, GitHub, Bilibili, XiaoHongShu — one CLI, zero API fees.
headroom
74.3kCompress tool outputs, logs, files, and RAG chunks before they reach the LLM. 20% fewer tokens for coding agents, 60-95% fewer tokens for JSON, same answers. Library, proxy, MCP server.
ruflo
73.7k🌊 The original agent harness. Deploy intelligent multi-player swarms, coordinate autonomous workflows, and build conversational AI systems. Features adaptive memory, self-learning intelligence, federation, vector RAG integration, and native Claude Code / Codex / Hermes and many more Integrated
Scrapling
85.0k🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl! Don't be shy, join here: https://discord.gg/EMgGbDceNQ and follow here for daily tips and tricks: https://x.com/Scrapling_dev
Languages
Trust signals
From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.
