n8n, MCP and AI agents: when to use a workflow, when to use an agent, and how to build your own MCP proxy
There are two ways to make software talk to other software in 2026, and most teams pick the wrong one for the job. The first is a deterministic...
The Sellarix team · 29 Mar 2026 · 19 min read

There are two ways to make software talk to other software in 2026, and most teams pick the wrong one for the job. The first is a deterministic workflow: you draw the steps, the tool runs them the same way every time. The second is an AI agent: you describe the goal, and a model decides the steps at runtime. n8n is the most popular tool for the first. Agents calling tools over the Model Context Protocol (MCP) are the standard for the second. They are not rivals so much as different instruments, and underneath both sits the same new wiring, MCP, which is quietly becoming the universal way to plug an AI into anything.
This piece is a practical, sourced walk through all of it: what n8n actually is and how to set it up, what MCP and agents and Claude Skills really are, when a workflow beats an agent and when it does not, and, for the case that bites everyone eventually, how to build your own MCP server when the service you need does not have one yet, including what a reusable API-to-MCP proxy looks like from a developer's point of view. Almost every factual claim links to a numbered source at the end.
Part one: what n8n is and how it works
n8n is a node-based workflow automation platform that bills itself as giving technical teams "the flexibility of code with the speed of no-code," with 400-plus integrations and native AI features [1]. It is the most developer-leaning of the big three automation tools: Zapier is the easiest and most linear, Make is a visual canvas for complex scenarios, and n8n wins on control and cost because you can self-host it and drop into code whenever you need to [1].
One thing to get straight before you build a business on it: n8n is fair-code, not open source. It is released under the Sustainable Use License, which lets you self-host and use it freely for internal business or personal purposes, but forbids you from reselling n8n itself as a hosted product that your own customers log into and build workflows on, without a separate commercial licence [2]. For most teams automating their own operations, that restriction never bites. If your plan is to wrap n8n and sell it, read the licence first [2].
The data model is the thing to understand
Almost every n8n confusion traces back to one design choice: data passing between nodes is always an array of items, where each item is an object wrapped under a json key (with an optional binary key for files) [3]. A node runs its operation once per item in that array [3]. So the data flowing down a workflow looks like this:
[
{ "json": { "id": 1, "email": "a@example.com", "status": "active" } },
{ "json": { "id": 2, "email": "b@example.com", "status": "churned" } }
]
Once that clicks, the rest of n8n follows. A workflow is a set of connected nodes [1]. Trigger nodes start it (a webhook, a schedule, an app event, a chat message, or a manual run); regular nodes do the work afterwards [5]. You move and reshape data with expressions, written in {{ }} and able to reference the current item or any earlier node [4]:
{{ $json.email }} // a field on the current item
{{ $('HTTP Request').json.id }} // a field from a named earlier node
{{ $json.price * 1.1 }} // inline JavaScript
{{ $input.first().json.name }} // the first input item
Flow control is a small set of nodes: IF (a true/false branch), Switch (multi-way routing), Merge (combine branches back together), Loop Over Items (batch iteration), and Filter (keep or drop items) [5]. You modularise with sub-workflows (call one workflow from another) and you catch failures with an Error Trigger node that routes any workflow's errors to recovery or alerting logic [5]. Credentials are stored separately from workflows and encrypted at rest with an N8N_ENCRYPTION_KEY [6].
Part two: setting up n8n and building your first workflow
The fastest way to see it is one command, which opens the editor at http://localhost:5678 [1]:
npx n8n
For anything you intend to keep, run it in Docker with a persistent volume [1]:
docker volume create n8n_data
docker run -it --rm --name n8n -p 5678:5678 \
-v n8n_data:/home/node/.n8n docker.n8n.io/n8nio/n8n
That single-container setup is fine for a small team, but it does not scale, it uses SQLite and runs every execution in one process. The production-grade setup is queue mode: a main instance that handles triggers and webhooks and hands executions to a queue, Redis as the message broker, PostgreSQL 13+ as the database (SQLite is not supported in queue mode), and one or more worker processes that actually run the executions [7]. You turn it on with an environment variable and start workers separately, and every process must share the same encryption key [7]:
export EXECUTIONS_MODE=queue
export QUEUE_BULL_REDIS_HOST=localhost
# main instance:
n8n
# one or more workers (default concurrency 10):
n8n worker --concurrency=5
# optional dedicated webhook process:
n8n webhook
Secure it properly: put it behind a reverse proxy with HTTPS, use the built-in user management and 2FA (the old N8N_BASIC_AUTH approach is legacy), keep secrets in environment variables, and in multi-worker setups use S3 for binary storage rather than the local filesystem [6][7].
A first real workflow is just the data model in action. Add a Schedule or Webhook trigger, then an HTTP Request node to call an API; its response becomes your array of items [3]. Add a Set / Edit Fields or Code node to reshape the data, referencing the previous step with {{ $('HTTP Request').json.someField }} [4]. Branch on a value with an IF node, then finish with a destination node (a database, Slack, Google Sheets) that runs once per item [3][5]. While building, use data pinning to freeze a node's output so you can iterate on later steps without hammering the live API on every test run [3].
A note on cost and limits. n8n Cloud bills per workflow execution, one run of the whole workflow regardless of how many steps or items it touches, which is much cheaper than the per-step billing of Zapier or Make [1][8]. The honest limitations: it is the most technical of the tools and has a real learning curve; scaling needs genuine ops work (Postgres, Redis, workers); versioned publishing and autosave only arrived with the n8n 2.0 era in late 2025 and early 2026; and very large workflows get harder to debug on a canvas than they would be in code [1][9].
Part three: MCP, agents and Claude Skills
Now the other side of the house. The Model Context Protocol is an open standard Anthropic introduced in November 2024 to connect AI assistants to the systems where data and tools live, replacing a mess of one-off integrations with a single protocol [13]. The problem it solves is combinatorial: instead of building a bespoke connector for every model-times-tool pairing, each model implements MCP once and each tool implements MCP once [13]. It caught on fast, OpenAI adopted it in 2025 and it spread across the industry, and in December 2025 Anthropic donated MCP to a new Agentic AI Foundation under the Linux Foundation, co-founded with Block and OpenAI, making it genuinely vendor-neutral [16].
Architecturally, MCP is client-server over JSON-RPC 2.0 [15]. A host (Claude Code, Claude Desktop, an IDE) runs one client per server, each client holding a dedicated connection to one server, and a host can talk to many servers at once [14]. Servers expose three primitives [14]:
| Primitive | What it is | Example |
|---|---|---|
| Tools | Executable functions the model can call | Create an order, run a query, send an email |
| Resources | Read-only data the model can load as context | A file's contents, a database record, an API response |
| Prompts | Reusable templates that structure an interaction | A pre-built "review this PR" prompt |
There are also client-side primitives the server can invoke, sampling (the server asks the host's model for a completion), elicitation (the server asks the user for more input), and roots (filesystem or URI boundaries) [14]. Two transports cover the cases that matter: stdio for a local server running as a child process on the same machine, and streamable HTTP (with optional Server-Sent Events) for remote servers, which supports bearer tokens, API keys and OAuth [14].
The agent loop on top is simple to state. The host fetches every connected server's tools with a tools/list call and gives the combined set to the model; each tool advertises a name, a description (what it does and when to use it) and an inputSchema (a JSON Schema for its parameters) [14]. When the model decides to use one, the host routes the tools/call, runs it, and feeds the structured result back into the conversation so the model can continue [14]. That same pattern is now cross-vendor; the OpenAI Agents SDK, for instance, lists an MCP server's tools on every run [21].
Skills are not MCP
People conflate Claude Skills with MCP, and they are different things that complement each other. A Skill is "an organised folder of instructions, scripts and resources that agents can discover and load dynamically," introduced in October 2025, defined by a SKILL.md file whose frontmatter carries a name and description [18]. It uses progressive disclosure: only the name and description sit in the system prompt; the full instructions load when the model judges them relevant; linked files and scripts load only when needed [18]. The clean distinction is this: MCP connects Claude to external tools and data; a Skill teaches Claude what to do with them [19]. MCP extends reach; a Skill carries procedural know-how [19]. In Claude Code they compose with subagents too, each running in its own context window with its own tools and permissions, returning only a summary to keep the main context clean [20].
The deepest framing comes from Anthropic's own "Building Effective Agents," which defines the two worlds precisely: workflows are systems where LLMs and tools are orchestrated through predefined code paths, while agents are systems where the LLM dynamically directs its own process and tool use [17]. Their headline advice is to reach for the simplest thing that works and "add multi-step agentic systems only when simpler solutions fall short" [17]. That sentence is the whole decision, which is the next part.
Part four: n8n workflows vs agents and MCP, and when to use which
Here is the real difference, stripped of hype. n8n is an explicit, deterministic graph you design step by step; it does the same thing every time and is built for moving data reliably at scale [25]. An agent with MCP tools decides at runtime which tools to call and in what order; it handles ambiguity and open-ended tasks but is non-deterministic by nature [17][25]. You design a workflow around the predictable and reach for an agent to handle the unpredictable.
The trade-offs are stark and worth a table.
| Dimension | n8n workflow (deterministic) | Agent + MCP (model-driven) |
|---|---|---|
| Predictability | Same input, same path, every time | Non-deterministic; the path varies |
| Cost per run | Near-zero; thousands of runs an hour with no token bill [25] | Burns tokens; an unconstrained agent can cost dollars per task [24] |
| Best at | Well-defined, repeatable pipelines you can pre-map | Ambiguous, judgement tasks with no fixed flowchart [22] |
| Failure mode | Fail-stop: halts with a clear, local error [23] | Fail-open: confident, plausible, subtly wrong output [23] |
| Debugging | Visual run log, each node's input/output inspectable | Hard to trace; a prompt tweak can reroute the whole run [23] |
| Errors over steps | A rule executes at 100% consistency | Errors compound across steps; reliability multiplies down [23] |
| Security | Standard app surface | Prompt injection and over-privileged tools are real risks [27][28] |
A useful rule of thumb, distilled from Anthropic's framing: ask whether you can pre-map the decision tree [22]. If you can map it, build it as a workflow, you get more accuracy, more control and lower cost than any agent [22]. Reach for an agent only when the task is genuinely ambiguous, when it is valuable enough to justify the token spend, and when the model can reliably recover from its own mistakes, because each loop iteration multiplies the failure rate of the weakest step [22]. It is worth knowing the wider context too: an MIT study widely cited in 2025 found that the great majority of enterprise generative-AI pilots delivered no measurable financial return, a finding about ROI in general rather than agents specifically, but a useful caution against reaching for the most complex tool by default [26].
The good news is that this is not actually an either/or, because both consume MCP. n8n now supports MCP in both directions [10]: its MCP Client Tool node lets a workflow's AI agent call external MCP servers, and its MCP Server Trigger node exposes your workflows as MCP tools that any external agent can call [11][12][25]. n8n also has a built-in AI Agent node, a LangChain-powered node that reasons and loops inside an otherwise deterministic graph [9]. So the mature pattern in 2026 is a hybrid: a deterministic workflow that calls a model for the one fuzzy step, or an agent that triggers a workflow to do the reliable heavy lifting, with MCP as the shared tool layer underneath either approach [25]. The orchestration style (a fixed graph versus an LLM-driven loop) is the real choice; the tools you can reach are increasingly the same.
Part five: no MCP server for your service? Build a proxy
Sooner or later you want your agent to use a service that has no official MCP server. The answer is to write a small MCP server yourself that wraps that service's existing REST API, an API-to-MCP proxy. It is less work than it sounds, because the SDKs do the protocol plumbing.

In the official TypeScript SDK, a tool is a name, a description, an input schema and a handler. One important gotcha that half the blog posts get wrong: in the current v1 SDK, inputSchema takes a raw Zod shape, not a wrapped z.object() [29]:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({ name: "orders-proxy", version: "1.0.0" });
server.registerTool(
"get_order",
{
title: "Get order",
description: "Fetch a single order by its ID from the Orders API.",
inputSchema: { orderId: z.string() }, // raw shape, not z.object({...})
},
async ({ orderId }) => {
const res = await fetch(`https://api.example.com/orders/${orderId}`, {
headers: { Authorization: `Bearer ${process.env.ORDERS_API_KEY}` },
});
if (!res.ok) {
return { content: [{ type: "text", text: `Error ${res.status}` }], isError: true };
}
const data = await res.json();
return { content: [{ type: "text", text: JSON.stringify(data) }] };
}
);
await server.connect(new StdioServerTransport());
Python is even terser with FastMCP, where the function's type hints become the schema and the docstring becomes the description [30]:
from fastmcp import FastMCP
import httpx, os
mcp = FastMCP("orders-proxy")
@mcp.tool()
async def get_order(order_id: str) -> dict:
"""Fetch a single order by its ID from the Orders API."""
async with httpx.AsyncClient() as client:
r = await client.get(
f"https://api.example.com/orders/{order_id}",
headers={"Authorization": f"Bearer {os.environ['ORDERS_API_KEY']}"},
)
r.raise_for_status()
return r.json()
if __name__ == "__main__":
mcp.run(transport="stdio") # or "streamable-http" for a shared server
You can also auto-generate the whole thing from an OpenAPI spec. FastMCP's from_openapi() turns a spec plus an HTTP client into a server in one call, and there are dedicated generators and proxies such as Speakeasy, fastapi-mcp, and community projects like mcp-openapi-proxy and openapi-mcp-generator [31][32][33][34]. But heed the warning that even the auto-generators' own docs give: LLMs perform significantly better with a small, well-curated set of tools than with a server that auto-converts every endpoint [31]. Auto-generation is great for a prototype; for production, curation is the work.
Part six: designing a reusable, multi-service MCP proxy
The version worth building is not a one-off wrapper but a single local proxy that can front several services, with the design centred on curation and safety rather than coverage. From a developer's point of view it has a handful of parts.
A config registry, keyed by service. One proxy, a config that maps each service to a base URL, an auth method and a hand-picked list of endpoints to expose as tools:
const services = {
orders: {
baseUrl: "https://api.example.com",
auth: { type: "bearer", env: "ORDERS_API_KEY" },
tools: [
{ name: "get_order", method: "GET", path: "/orders/:id",
description: "Fetch one order by ID.", input: { id: "string" } },
{ name: "list_orders", method: "GET", path: "/orders",
description: "List recent orders. Supports a 'status' filter.",
input: { status: "string?" }, readOnly: true },
],
},
// add the next service here; the proxy loops over this registry
};
Aggressive curation. This is the whole value-add. Reliability degrades badly as tool count climbs; practitioners see diminishing returns past roughly fifteen tools per server and real trouble (the model picking the wrong tool, or hallucinating one) beyond thirty to forty [33]. Tool definitions also cost tokens before any work happens, so a default-deny posture, allow-list the handful of operations you actually need, mirrors the RouteMap and scope-filtering patterns the mature generators provide [31][33][38].
Descriptions are the product. A 2025 ecosystem study found that the overwhelming majority of MCP tool descriptions had at least one quality issue, and tuning names and descriptions often matters more than the underlying model [33]. Write each description as if onboarding a new colleague: what the tool does, when to use it, and what it returns.
Hold the credentials at the proxy, never pass them through. The proxy reads each service's API key from the environment and injects it into its own outbound HTTP calls; the model never sees it. The MCP security guidance is explicit that you must not forward a client's token to the downstream API ("token passthrough" is forbidden) and must guard against the confused-deputy problem in OAuth scenarios [35].
Shape inputs and outputs. Hand-write tight input schemas rather than flattening an entire OpenAPI body, and trim responses to the fields that matter, paginating and capping size so a single call cannot dump megabytes (and a fortune in tokens) into the context [31][38].
Gate the dangerous stuff. Use MCP's tool annotations, readOnlyHint, destructiveHint, idempotentHint, so clients can auto-approve reads and prompt before writes and deletes, but treat them as hints, not security: real enforcement lives in your proxy's allow-list [36]. Choose stdio for a single local user and streamable HTTP for a shared proxy, and test everything with the MCP Inspector (npx @modelcontextprotocol/inspector), which lists your tools, builds a form from each schema and shows the raw responses [37].
Build versus use. If the target has a clean OpenAPI spec and you mainly need reads, a generic generator with route filtering will get you there fast [31][34]. Build your own curated proxy when the API is large (auto-generation yields too many tools), the descriptions are poor, you need to wrap several services behind one server, or you must shape responses and gate destructive calls. The consistent message, even from the auto-generators, is that a thin proxy exposing five to fifteen well-described tools per service beats a sprawling auto-converted one [31][33].
How it all connects
Step back and the picture is tidy. n8n and agents are two orchestration styles: a deterministic graph you can see and trust, and an LLM loop that trades predictability for flexibility. MCP is not a third option, it is the wiring beneath both, the standard way an AI reaches a tool or a dataset, now vendor-neutral and consumed by n8n and agent frameworks alike [16][25]. Skills sit alongside, teaching an agent the procedure once it has the reach [19]. And when a service you need is not yet on that wiring, you add it yourself with a small, curated MCP proxy.
The practical takeaway is the unglamorous one Anthropic keeps repeating: start simple [17]. If you can draw the steps, build a workflow, it will be cheaper, faster and more reliable than any agent. Reach for an agent only where the task genuinely cannot be pre-mapped, give it a small set of well-described tools over MCP, hold the credentials yourself, and gate anything destructive. Get that discipline right and the choice between n8n and an agent stops being a religious war and becomes what it should be: picking the right tool for the shape of the job.
Sources
- n8n, GitHub repository (overview, integrations, positioning) - https://github.com/n8n-io/n8n
- n8n, Sustainable Use License - https://docs.n8n.io/sustainable-use-license/
- n8n docs, data structure (item array model) - https://docs.n8n.io/data/data-structure/
- n8n docs, expressions - https://docs.n8n.io/code/expressions/
- n8n docs, flow logic (triggers, IF/Switch/Merge/Loop, sub-workflows, error trigger) - https://docs.n8n.io/flow-logic/
- n8n docs, securing n8n - https://docs.n8n.io/hosting/securing/overview/
- n8n docs, scaling / queue mode - https://docs.n8n.io/hosting/scaling/queue-mode/
- n8n, pricing (per-execution billing) - https://n8n.io/pricing/
- n8n, introducing n8n 2.0 - https://blog.n8n.io/introducing-n8n-2-0/
- n8n docs, advanced AI / MCP overview - https://docs.n8n.io/advanced-ai/mcp/
- n8n docs, MCP Server Trigger node - https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-langchain.mcptrigger/
- n8n docs, MCP Client Tool node - https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.toolmcp/
- Anthropic, Introducing the Model Context Protocol (2024) - https://www.anthropic.com/news/model-context-protocol
- Model Context Protocol, architecture overview - https://modelcontextprotocol.io/docs/learn/architecture
- Model Context Protocol, specification - https://modelcontextprotocol.io/specification/latest
- Anthropic, donating MCP and the Agentic AI Foundation (2025) - https://www.anthropic.com/news/donating-the-model-context-protocol-and-establishing-of-the-agentic-ai-foundation
- Anthropic, Building Effective AI Agents - https://www.anthropic.com/research/building-effective-agents
- Anthropic, Equipping agents for the real world with Agent Skills (2025) - https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills
- Claude, Skills explained (Skills vs MCP) - https://claude.com/blog/skills-explained
- Claude Code docs, subagents - https://code.claude.com/docs/en/sub-agents
- OpenAI Agents SDK, MCP support - https://openai.github.io/openai-agents-python/mcp/
- Shelly Palmer, how Anthropic thinks about agents, workflows and tasks (2026) - https://shellypalmer.com/2026/04/how-anthropic-thinks-about-agents-workflows-and-tasks/
- Elementum AI, deterministic vs probabilistic AI / are AI agents deterministic - https://www.elementum.ai/blog/deterministic-vs-probabilistic-ai
- Stevens Online, the hidden economics of AI agents (token costs, latency) - https://online.stevens.edu/blog/hidden-economics-ai-agents-token-costs-latency/
- Infralovers, n8n as an agentic MCP hub (both directions) - https://www.infralovers.com/blog/2026-03-09-n8n-agentic-mcp-hub/
- Fortune, MIT report on enterprise GenAI pilots (2025) - https://fortune.com/2025/08/18/mit-report-95-percent-generative-ai-pilots-at-companies-failing-cfo/
- TrueFoundry, MCP security risks and best practices - https://www.truefoundry.com/blog/mcp-security-risks-best-practices
- Prompt Security, top 10 MCP security risks - https://prompt.security/blog/top-10-mcp-security-risks
- Model Context Protocol, TypeScript SDK server guide - https://github.com/modelcontextprotocol/typescript-sdk
- FastMCP / Model Context Protocol Python SDK - https://github.com/modelcontextprotocol/python-sdk
- FastMCP, OpenAPI integration (auto-generation and its limits) - https://gofastmcp.com/integrations/openapi
- fastapi-mcp (FastAPI to MCP) - https://github.com/tadata-org/fastapi_mcp
- Speakeasy, MCP tool design (curation, descriptions, scopes) - https://www.speakeasy.com/mcp/tool-design
- mcp-openapi-proxy (generic REST to MCP) - https://github.com/matthewhand/mcp-openapi-proxy
- Model Context Protocol, security best practices (token passthrough, confused deputy) - https://modelcontextprotocol.io/specification/draft/basic/security_best_practices
- Model Context Protocol, tool annotations - https://blog.modelcontextprotocol.io/posts/2026-03-16-tool-annotations/
- Model Context Protocol, the MCP Inspector - https://modelcontextprotocol.io/docs/tools/inspector
- Anthropic, code execution with MCP (tool/token overhead) - https://www.anthropic.com/engineering/code-execution-with-mcp
Replace six tools with one
Join the waitlist to be first on the platform, or book a demo.