Back to N8n Mcp

Agent tools

data/skills/n8n-agents/TOOLS.md

2.63.111.1 KB
Original Source

Agent tools

The agent picks tools by reading their name and description — nothing else. Both are part of the prompt. Treat tool design like API design: what it does, when to use it, what each parameter means, and how it fails.


The four tool types

1. Native tool node

Pre-built tool versions of regular nodes: slackTool, gmailTool, googleSheetsTool, toolCalculator, notionTool, httpRequestTool, and so on. Identical to their non-tool counterparts except parameters can be agent-filled via $fromAI().

  • Pros: minimal config, well-tested, native feel.
  • Cons: one node = one operation. Multi-step logic doesn't fit.
  • Use when: the capability maps cleanly to one node and one operation.

When a native node is missing an operation or needs a non-standard param shape, point an HTTP Request Tool at the service's API with the service's predefined credential type — you reuse the existing OAuth/API-key credential and get the full API.

2. Sub-workflow as tool (@n8n/n8n-nodes-langchain.toolWorkflow)

The default for anything beyond one node. Any workflow becomes a tool with typed $fromAI() inputs.

  • Pros: full power of n8n inside the tool — branching, error handling, sub-sub-workflows, native nodes, custom logic. Reusable across agents. Independently testable.
  • Cons: one extra workflow boundary, slight latency.
  • Use when: more than one node, logic that might be reused, or you want testability.

The canonical n8n way to build agent capabilities. → SUBWORKFLOW_AS_TOOL.md

3. HTTP Request Tool (@n8n/n8n-nodes-langchain.toolHttpRequest)

A wrapper around the HTTP Request node exposing its parameters to the agent.

  • Pros: any HTTP API becomes a tool with one node.
  • Cons: HTTP only. Auth/retry/error handling are yours to wire.
  • Use when: calling a single external API the agent should orchestrate directly.

One thing to know: HTTP Request has its own HTTP-level timeout (default 5 minutes) — bump options.timeout for slow endpoints. The agent tool itself has no timeout; the agent waits as long as the tool takes. Pointing it at, say, the Notion API (with the Notion predefined credential) lets the agent compose path, method, and body itself — covering operations the native node doesn't expose. Trade-off: the agent is now writing API requests, which is more error-prone and needs a capable model plus clear endpoint guidance in the description. That widens the blast radius — make sure the user understands.

4. MCP Client Tool (@n8n/n8n-nodes-langchain.mcpClientTool)

Connects the agent to any MCP server. Two flavors:

  • External MCP servers — any third-party or self-hosted MCP (GitHub, Linear, Notion, custom internal). One node exposes every tool that server offers.

  • n8n-hosted MCP — a workflow on the same instance published with MCP access enabled. Same client node, pointed at an n8n MCP trigger URL. Lets one workflow serve many agents.

  • Cons: tool descriptions and shapes come from the server, so quality varies and you can't easily tune them. Auth and reachability are yours.

  • Use when: a maintained MCP server already covers the capability, or you want one published workflow to serve many agents.

Plus: Custom Code Tool (@n8n/n8n-nodes-langchain.toolCode)

Pure inline computation (math, parsing, formatting). Its runtime contract is string in / string out, no $fromAI, no $helpers and is owned by the n8n-code-tool skill — read it before writing one. Rule of thumb: if you want $fromAI() in the code, you want .toolWorkflow instead.


Decision: which tool type?

Capability the agent needs?
├── One native node + one operation does it
│     → native tool node
├── Native node missing an op / needs custom params for ONE API
│     → HTTP Request Tool (with the service's predefined credential)
├── More than one node, or logic that might be reused
│     → Sub-workflow as tool (.toolWorkflow)   ← default when in doubt
├── Pure deterministic computation, one-off, inline
│     → Custom Code Tool (.toolCode)            ← see n8n-code-tool
└── A maintained MCP server covers it / publish n8n logic to many agents
      → MCP Client Tool

$fromAI(): how the agent fills tool parameters

$fromAI() is a real n8n expression helper, written inside a tool node's parameter expressions. Parameters the agent should decide get wrapped in it:

sendTo:  ={{ $fromAI('recipient', 'Email address of the recipient', 'string') }}
subject: ={{ $fromAI('subject', 'Email subject line, concise and informative', 'string') }}
body:    ={{ $fromAI('body', 'Email body in plain text, professional tone', 'string') }}

Shape: $fromAI(paramName, description, type?, defaultValue?)

  • paramName — the name the model uses internally. snake_case or camelCase, be consistent.
  • description — what value to produce. Part of the prompt. Be specific: format, range, example.
  • type'string' (default), 'number', 'boolean', 'json'. Enforced — a wrong-typed value fails the call.
  • defaultValue — used when the model omits the parameter.

It carries JSON only — it cannot carry binary (no base64, no file bytes), even through a non-AI binding. For binary, pass a storage key as a string and have the tool re-fetch (→ n8n-binary-and-data).

A good description vs a useless one:

✅ ={{ $fromAI('imageName', 'Storage key for an existing image to edit, or empty for a new generation. Use the exact key shown in the system prompt; do not reconstruct or guess.', 'string') }}

❌ ={{ $fromAI('imageName', 'image name', 'string') }}   // useless to the model

Treat $fromAI descriptions like JSDoc — the model reads them to figure out what to pass.


Plumbed params: hide what the agent shouldn't decide

Not every parameter has to be $fromAI. Any parameter can be filled deterministically from workflow context, and plumbed values are invisible to the agent — not in the tool schema, not influenceable by anything the model produces:

reason:        ={{ $fromAI('reason', 'Why the user is requesting a refund', 'string') }}   // agent-filled
customerId:    ={{ $('Chat Trigger').first().json.user.id }}                                 // hidden
maxRefund:     ={{ $('Get user tier').first().json.refundLimit }}                            // hidden
idempotencyKey:={{ $('Chat Trigger').first().json.sessionId }}                               // hidden

Plumb anything the agent shouldn't get wrong or see:

  • IdentityuserId, customerId, authenticated actor, tenant scope.
  • Authority limits — refund caps, tier flags, allowed regions.
  • Correlation IDssessionId, idempotency keys, trace IDs.

Give the agent a button to push, not a steering wheel. The strongest version is a sensitive tool with zero $fromAI parameters: a "Refund order" tool takes orderId from the trigger, amount from the fetched order record, actor from the session — all plumbed. The agent literally cannot refund the wrong order; it only chooses whether to fire. Pair with HUMAN_REVIEW.md for actions needing both deterministic params and sign-off.


Tool name and description as prompt

Selection process the model runs every turn:

  1. It gets the system prompt, conversation, and the list of tools.
  2. For each tool it reads name + description + parameter schema (with $fromAI descriptions).
  3. It picks the tool whose description best matches what it needs to do.

Bad names and descriptions cause bad selection — usually silently. The model just doesn't call your tool, or calls a different one with garbage parameters. No error.

Names: verb-first and specific

GoodBadWhy
Search customer databasequery / tool1Generic names say nothing
Generate image with VeoimageGenWhich generator?
Edit existing imageeditEdit what?
Send Slack message to channelslackName the action, not just the surface
Lookup user by emailgetUserLookup how?

Descriptions: three parts

  1. What it does (one sentence).
  2. When to use it (one or two sentences, with boundaries / examples).
  3. Parameter notes (only if not already covered in $fromAI descriptions).
Edit existing image: Modifies an image the user already uploaded, based on a prompt.
Use when the user uploaded an image and asks for changes (color, style, composition, content).
Do NOT use for generating new images from scratch — use Generate Image for that.
The imageName parameter must be the storage key of the existing image as listed in your
available files; do not pass the original filename or a URL.

That description does work that would otherwise bloat the system prompt — which is exactly the point.


Tool descriptions as modular prompts

Anything specific to how to call this tool belongs in the tool's description, not the system prompt:

In the system prompt (move out)Better in the tool description
"When generating images, prefer realistic photography over 8k cinematic"Generate Image: "Default to realistic photography aesthetics…"
"If the search tool returns nothing, summarize politely"Search: "Returns up to 10 results; if empty, report 'no matches' rather than retrying broader"
"Use 9:16 for video tools"Generate Video: "Defaults to 9:16; pass aspectRatio: '16:9' for landscape"

Three reasons: reusability (the tool teaches each new agent how to use it), token efficiency (per-tool guidance only loads when the model considers that tool, not every turn), maintainability (one description, not a buried paragraph).


Granularity: one tool with branching, not two near-identical tools

The model gets confused choosing between near-identical tools. If two are ~80% the same internally:

  • One tool with a branching parameter. Generate Image vs Edit Image share most logic → collapse to one with an imageName parameter (empty = generate, populated = edit).
  • Two tools only when genuinely distinct AND the descriptions clearly differentiate. Send DM vs Send Channel Message are distinct.

Operational notes

  • maxIterations. Agents have a configurable tool-call cap (options.maxIterations), and the default is low. A multi-tool agent that chains calls hits it and surfaces "max iterations reached" or empty output. Raise it. Build a fallback — don't trust graceful recovery.
  • Tool-call cost. Each call is at minimum one extra model round-trip. Frequently-called tools should return concise results — bloated returns burn input tokens fast.
  • Tool failure handling. Set onError: 'continueErrorOutput' on tool sub-workflows where you want the agent to receive an error string instead of halting; the agent can retry, switch tools, or report. → n8n-error-handling.

Cross-references

  • The sub-workflow tool pattern in detail → SUBWORKFLOW_AS_TOOL.md
  • System-prompt-vs-tool-description split → SYSTEM_PROMPT.md
  • Passing binary into tools → n8n-binary-and-data
  • The Custom Code Tool contract → n8n-code-tool