n8n MCP Agent Workflow in Production: Where the Agent Stops

In an n8n MCP agent workflow, the agent drafts and n8n holds publishing. Postiz's schedulePostTool takes a type of draft, schedule or now, so the limit is a parameter n8n must pin.
One field decides whether an agent drafts a post or publishes it. Postiz's MCP server has a single tool for creating posts, schedulePostTool, and each post it takes has a type of draft, schedule or now. In an n8n MCP agent workflow in production, where the agent stops is therefore a parameter on one tool, not a choice between tools, and a line in a prompt is the weakest place to hold it.
I build content-creation and automated-posting workflows for 10+ clients and 4+ of my own projects: agents draft, n8n routes, Postiz schedules and posts, and both n8n and Postiz are self-hosted and exposed over MCP. My n8n is a single instance behind nginx and Cloudflare, and nothing has broken in production. So this is the boundary set up step by step, plus what a demo showed when I broke the parts that can fail.
Every figure comes from that demo, run on 12 October 2026 against n8n 2.42.7 and the MCP TypeScript SDK 1.32.1. No AI Agent node or LLM was involved: an SDK client called the tools directly and stood in for the agent. The Postiz and human-review parts come from documentation, and I did not run them.
TL;DR: Give the agent the judgement calls and keep publishing in n8n. Select only the tools the agent needs, gate schedulePostTool with n8n's tool-level human review (since 2.6.0), and pin type: draft in a fixed step. In the demo, Retry On Fail was capped at 5 tries and 5 s, and a tool's own rejection text came back as a success.
Where Does the Agent Stop in an n8n MCP Agent Workflow in Production?
The workflow ends wherever a correct output can be written down in advance. Triggers, schedules, branching, retries, length checks and the final call to Postiz are n8n nodes. The agent gets two decisions that need judgement: drafting copy for one channel, and choosing which brand rules apply to it. The line I would draw is that the agent proposes a post and only n8n lets one leave.
The test for each step is whether its correct output can be stated as a function of its input. A length check can be stated and a tone call cannot. The first goes in a node, and the second goes to the agent, which in n8n's words "decides which tools to call to complete a task."
Postiz makes the split harder than a tool list suggests. Its docs describe schedulePostTool as "Schedule, draft, or immediately publish posts to social media platforms," so there is no publish tool to leave out. Three controls hold the line instead, and none is the prompt:
- Tools to Include: Selected on the MCP Client Tool node, so the agent only sees the tools you name.
- Tool-level human review on
schedulePostTool, so a call cannot run until a person approves it. - A fixed step that forces
type: draft, so the workflow sets the field and the model does not.
A system prompt that says "only draft" is still worth writing, but the model can ignore it: the same gap as rules an AI tool reads but nothing enforces.
How Do You Expose and Consume Tools Over MCP in n8n?
Use the MCP Server Trigger to expose your own tools and the MCP Client Tool to consume Postiz's. The trigger connects only to tool nodes, serves SSE and streamable HTTP, does not support stdio, and offers None, Bearer or Header authentication. The client tool filters a server's tools with Tools to Include: All, Selected or All Except.
Postiz's setup page gives the endpoint as https://<host>/mcp/<api-key>, or /mcp with a Bearer header, over Streamable HTTP. I would use the header, since a key in the path ends up in access logs. n8n's Client Tool page names an "SSE Endpoint" parameter, so check that your node version speaks streamable HTTP first. I did not run that pairing.
For the demo I built the other direction, a trigger exposing two tools, so a real client could call them. Trimmed from the workflow JSON:
{
"name": "MCP Server Trigger",
"type": "@n8n/n8n-nodes-langchain.mcpTrigger",
"parameters": { "authentication": "bearerAuth", "path": "content-pipeline" }
},
{
"name": "queue_post_for_review",
"type": "@n8n/n8n-nodes-langchain.toolCode",
"parameters": {
"name": "queue_post_for_review",
"description": "Queues a drafted post for HUMAN REVIEW. It does not publish or schedule anything. Returns a review id, or REJECTED with the reason.",
"specifyInputSchema": true,
"schemaType": "manual"
}
}The tool takes channel (an enum of linkedin and x), text and an optional article_url. The client is the SDK's own:
const transport = new StreamableHTTPClientTransport(new URL(process.env.MCP_URL), {
requestInit: { headers: { Authorization: `Bearer ${process.env.MCP_TOKEN}` } },
});
const client = new Client({ name: 'demo-client', version: '1.0.0' });
await client.connect(transport);
const res = await client.callTool({
name: 'queue_post_for_review',
arguments: { channel: 'x', text: 'x'.repeat(300) },
});The capture showed three things:
- The node is the contract. The tool name and description the client received were the node's fields, verbatim, and a tool with no input schema was advertised as
{"input": string}. Whatever the agent knows about a tool is what those fields say, so "It does not publish or schedule anything" belongs in the description. - A bad or missing token returns 403, not 401. The SDK reported
Authorization data is wrong!,curlshowedHTTP/1.1 403 Forbidden, and there was noWWW-Authenticateheader. - Version negotiation steps down. A client asking for
2025-11-25got it. A client asking for2026-07-28, the revision that removes theinitializehandshake, got2025-11-25back, handshake andmcp-session-idheader included.
n8n's docs say to disable proxy buffering on the MCP endpoint. Behind nginx 1.31.6 with proxy_buffering on and then off, I saw no difference: the SSE endpoint event arrived in about 0.2 s either way and every call completed. The nginx test was a short session with no gzip, Cloudflare or long calls, so it does not show that disabling proxy buffering on the MCP endpoint is unnecessary.
Which Tool Calls Need a Human to Approve Them?
Any call that changes something outside n8n needs a human, and for Postiz that means schedulePostTool whenever type could be schedule or now. n8n 2.6.0 (released 2026-01-26) added tool-level review: a gated tool "cannot execute unless a human explicitly approves the action," including the MCP Client Tool and sub-workflows.
On approval the tool runs with the AI's input. On denial the action is cancelled and the AI is told. The docs list nine review channels, from Chat and Slack to Gmail, and expose $tool.name and $tool.parameters for the reviewer's message. The setup, in order:
- Set Tools to Include to Selected on the Postiz MCP Client Tool node and pick tools by name. The agent never sees the rest of the 13 the docs list.
- Add human review on
schedulePostTool. Build the message from$tool.parameterssotypeis the first line read, since a reviewer who sees only the post text can approve anowwithout noticing. - Tell the system prompt what a denial means, so the agent revises or stops instead of retrying the same call.
- Where the agent can hand back its draft as output, do not give it
schedulePostToolat all. A regular node after the agent posts to Postiz withtypefixed todraft.
Withholding schedulePostTool from the agent, so a regular node posts with type fixed to draft, is the option I would pick first, because a call the agent never makes needs no approval. I wrote about where human review belongs in an LLM pipeline for a Claude API pipeline: confidence has to come from checks. Tool gating applies that rule, confidence from checks, to tool calls as well.
What Fails in an n8n Agent Workflow, and Which Setting Catches It?
Three different things fail, each with a different catcher. A tool call that fails once is caught by Retry On Fail. A failed execution is caught by an error workflow. A tool that returns a wrong answer as ordinary content is caught by nothing in n8n, and needs a check you write.
Layer 1: Retry On Fail Covers a Blip, Not an Outage
On n8n 2.42.7, Retry On Fail is capped at runtime at 2 to 5 tries with 0 to 5,000 ms between them, whatever the workflow JSON says. The docs on nodes state no limits; the editor source, ndv.utils.ts, sets maxValue 5 and 5000. I checked the runtime with a counter server that answered every request with 503 and logged each hit:
maxTries10: the server received 5 requests.waitBetweenTries10000: the gaps were about 5.0 s.waitBetweenTries0: the gaps were about 1.0 s, because the engine readsnode.waitBetweenTries || 1000and 0 is falsy.
The public API stored every out-of-range value without complaint. Five tries with four 5 s waits is about 20 seconds, so retries absorb a blip and nothing longer. The execution data added two details:
- The try count is not recorded. The node shows
runs=1whatever the number of tries, so I counted at the receiving end. Log it on the server you call if you need it. - On Error: Continue still retried. The node tried 3 times, then the execution finished with
status=successand the error object passed downstream as normal output. n8n issue #9236, opened in April 2024 and now closed, reported that Max Tries and Wait Between Tries are ignored when On Error is set to Continue on the HTTP Request node. The claim in issue #9236 did not reproduce on n8n 2.42.7. A Continue node turns a persistent failure into a success downstream.
Layer 2: An Error Workflow Catches a Failed Execution
An error workflow runs when an execution fails, and it must start with the Error Trigger node. Its default payload carries execution.id, execution.url, execution.error and execution.lastNodeExecuted. The trigger only fires on automatic executions, so a manual run cannot test it. The Stop And Error node sends it custom messages, so a check can turn a wrong result into a failure.
Layer 3: A Wrong Answer That Is Not an Error
Neither retry nor an error workflow fires when a tool returns a failure as ordinary content. In the demo, queue_post_for_review answered a 300-character X post with REJECTED: 300 characters, x limit is 280. Nothing queued. and no isError flag, and the SDK call resolved. To the client that looks like success, and the Error Trigger runs only for failed executions.
Bad input behaves differently. A channel of instagram and a missing channel were both refused before the tool ran, with isError: true and a message such as Invalid enum value. Expected 'linkedin' | 'x', received 'instagram'. The input schema is a free first gate, and an enum on the channel is worth the few lines.
The check I would add sits where n8n owns it. A node after the agent tests the structured result (here, status equals queued_for_review and published is false) and routes anything else to Stop And Error, which feeds layer 2. I did not run that wiring.
What Should an n8n MCP Workflow Log, and Where Does It Go?
On the n8n side, execution data and the error workflow's payload are the log. For a stdio MCP server, stdout carries protocol messages only and logs go to stderr. The stdout-only rule does not apply to n8n's MCP Server Trigger, which serves HTTP, but it applies to any stdio server an agent connects to.
The MCP spec says the server "MUST NOT write anything to its stdout that is not a valid MCP message" and "MAY write UTF-8 strings to stderr for any logging purposes," in both the 2025-11-25 and 2026-07-28 revisions. The MCP spec does not require a stdio server to write its logs to stderr.
Stdio still turns up around n8n: its docs route Claude Desktop through mcp-remote, a gateway to stdio-based servers. I reproduced the failure with one tool, where the only difference is where log writes:
const log = process.env.LOG_TO === 'stdout' ? console.log : console.error;
// inside the tool handler:
log(`debug: counting ${text.length} characters`);With SDK 1.32.1:
console.log, with newline: each call raisedSyntaxError: Unexpected token 'd', "debug: cou"... is not valid JSONon the client'sonerrorhook. The line was dropped and the call still returned in 12 ms and 3 ms. A client that does not watchonerrorsees nothing wrong.- The same text without a trailing newline: the log fragment and the JSON-RPC response landed on one line, the line failed to parse, and the call timed out after 5,007 ms with
McpError -32001. My client set a 5 s timeout. The SDK default is 60,000 ms, so an unconfigured client would hang for a minute. console.error: no client errors, and the lines arrived on stderr.
Limitations: What Does This Not Cover?
None of the figures here come from my production pipelines. Each comes from the 12 October 2026 demo, and several claims come from documentation I did not run.
- Versions. n8n 2.42.7 (image
n8nio/n8n:latest, pulled that day), SDK 1.32.1, Node.js v24.14.1, nginx 1.31.6. n8n ships weekly, and the retry caps are runtime behaviour of this version. - No agent. How a model reacts to a denial, chooses among tools or reads a
REJECTEDstring was not tested. - Not run: Postiz, the 2.6.0 review and the fixed step. Postiz's behaviour comes from its docs, the review from n8n's docs, and the
type: draftstep is my own design. - Retry scope. I tested the HTTP Request node. Tools attached to an agent pass through a second copy of the clamp, in
get-input-connection-data.js, which uses??and would keep a 0 wait. I read that path and did not run it. - Proxy scope. The nginx test excluded gzip, Cloudflare, long calls and queue mode. With multiple webhook replicas, n8n's docs say to route all
/mcp*requests to one replica. I run a single instance and did not test that.
More Self-Hosted AI field notes are in the category.
A prompt can ask an agent to only draft, but only a parameter that n8n sets can make it so.
If you run Postiz, or any one-tool publisher, behind n8n's MCP Client Tool, what does your reviewer see when type is now, and how did you build that message so it is the first thing read?
References
- Postiz MCP docs: introduction (13 tools), tools (
schedulePostTool,type), setup (endpoint, Bearer, Streamable HTTP). - n8n docs: MCP Server Trigger, MCP Client Tool, AI Agent, Human-in-the-loop for tools, Handle errors gracefully, Error Trigger, Work with nodes.
- n8n issue #9236: retry settings and On Error: Continue, read 2026-10-12.
- n8n 2.x release notes: entry
[email protected], 2026-01-26. - n8n source:
ndv.utils.ts(retry limits,master, read 2026-10-12), andworkflow-execute.jsandget-input-connection-data.jsfrom then8n-corepackage inside the 2.42.7 image. - MCP specification, stdio transport: 2026-07-28 and 2025-11-25.
- The demo workflow, SDK client, stdio server and captures from 12 October 2026: the source for every measured figure above.