N8n Workflow Tool node/MCP Server Trigger behaviour - Not running sub-workflows on worker nodes

Describe the problem/error/question

What is the expected behaviour of the n8n Workflow Tool node when used with an MCP Server Trigger on a self-hosted instance setup to use Queue mode? Specifically, where should I expect the sub-workflow to execute?

What is the error message (if any)?

No error message but the expected behaviour would be for the tool node to enqueue the sub-workflow and for it to be picked up and executed by one of the worker nodes. The actual behaviour is that the sub-workflow is executed directly on my mcp webhook node.

Please share your workflow

Example workflow:

Summary

{
“nodes”: [
{
“parameters”: {
“path”: “b3665368-bcea-4b96-8b8e-2fbd29fd9968”
},
“type”: “@n8n/n8n-nodes-langchain.mcpTrigger”,
“typeVersion”: 2,
“position”: [
-336,
-112
],
“id”: “ac1833d8-69ae-4cfe-9f40-876fa44bf077”,
“name”: “MCP Server Trigger”,
“webhookId”: “b3665368-bcea-4b96-8b8e-2fbd29fd9968”
},
{
“parameters”: {
“description”: “Call this tool to verify the inputs as valid. If they are the valid the tool will return the same input as output. If invalid an error will be returned.”,
“workflowId”: {
“__rl”: true,
“value”: “c15ejvedylR9yAVG52Bft”,
“mode”: “list”,
“cachedResultUrl”: “/workflow/c15ejvedylR9yAVG52Bft”,
“cachedResultName”: “TestSubFlow”
},
“workflowInputs”: {
“mappingMode”: “defineBelow”,
“value”: {
“Param1”: 7,
“Param2”: “Feb26”
},
“matchingColumns”: ,
“schema”: [
{
“id”: “Param1”,
“displayName”: “Param1”,
“required”: false,
“defaultMatch”: false,
“display”: true,
“canBeUsedToMatch”: true,
“type”: “number”,
“removed”: false
},
{
“id”: “Param2”,
“displayName”: “Param2”,
“required”: false,
“defaultMatch”: false,
“display”: true,
“canBeUsedToMatch”: true,
“type”: “string”,
“removed”: false
}
],
“attemptToConvertTypes”: false,
“convertFieldsToString”: false
}
},
“type”: “@n8n/n8n-nodes-langchain.toolWorkflow”,
“typeVersion”: 2.2,
“position”: [
-256,
96
],
“id”: “95b3df20-19a5-4176-8718-8661d037503e”,
“name”: “Call 'TestSubFlow’1”
}
],
“connections”: {
“Call 'TestSubFlow’1”: {
“ai_tool”: [
[
{
“node”: “MCP Server Trigger”,
“type”: “ai_tool”,
“index”: 0
}
]
]
}
},
“pinData”: {},
“meta”: {
“instanceId”: “e767d9ca77f1b1254fad67c4902bdfb388c1ca313b8c6be63014ff7138234575”
}
}

Example logs from the mcp webhook node:

Summary

{“level”:“debug”,“message”:“Received webhook “POST” for path “b3665368-bcea-4b96-8b8e-2fbd29fd9968””,“metadata”:{“file”:“live-webhooks.js”,“function”:“executeWebhook”,“timestamp”:“2026-01-29T14:36:11.452Z”}}
{“level”:“debug”,“message”:“New session initialized: fdee1195-81cf-4852-a0dd-7d5cd31b338b”,“metadata”:{“file”:“McpServer.js”,“function”:“onsessioninitialized”,“timestamp”:“2026-01-29T14:36:11.467Z”}}
{“level”:“debug”,“message”:“Received webhook “POST” for path “b3665368-bcea-4b96-8b8e-2fbd29fd9968””,“metadata”:{“file”:“live-webhooks.js”,“function”:“executeWebhook”,“timestamp”:“2026-01-29T14:36:11.471Z”}}
{“level”:“debug”,“message”:“Received webhook “GET” for path “b3665368-bcea-4b96-8b8e-2fbd29fd9968””,“metadata”:{“file”:“live-webhooks.js”,“function”:“executeWebhook”,“timestamp”:“2026-01-29T14:36:11.494Z”}}
{“level”:“debug”,“message”:“Received webhook “POST” for path “b3665368-bcea-4b96-8b8e-2fbd29fd9968””,“metadata”:{“file”:“live-webhooks.js”,“function”:“executeWebhook”,“timestamp”:“2026-01-29T14:36:11.501Z”}}
{“level”:“debug”,“message”:“Execution added”,“metadata”:{“executionId”:“318554”,“file”:“active-executions.js”,“function”:“add”,“timestamp”:“2026-01-29T14:36:11.529Z”}}
{“level”:“debug”,“message”:“Workflow execution started”,“metadata”:{“file”:“logger-proxy.js”,“function”:“exports.debug”,“timestamp”:“2026-01-29T14:36:11.539Z”,“workflowId”:“c15ejvedylR9yAVG52Bft”}}
{“level”:“debug”,“message”:“Start executing node “When Executed by Another Workflow””,“metadata”:{“file”:“logger-proxy.js”,“function”:“exports.debug”,“node”:“When Executed by Another Workflow”,“timestamp”:“2026-01-29T14:36:11.539Z”,“workflowId”:“c15ejvedylR9yAVG52Bft”}}
{“level”:“debug”,“message”:“Running node “When Executed by Another Workflow” started”,“metadata”:{“file”:“logger-proxy.js”,“function”:“exports.debug”,“node”:“When Executed by Another Workflow”,“timestamp”:“2026-01-29T14:36:11.539Z”,“workflowId”:“c15ejvedylR9yAVG52Bft”}}
{“level”:“debug”,“message”:“Running node “When Executed by Another Workflow” finished successfully”,“metadata”:{“file”:“logger-proxy.js”,“function”:“exports.debug”,“node”:“When Executed by Another Workflow”,“timestamp”:“2026-01-29T14:36:11.540Z”,“workflowId”:“c15ejvedylR9yAVG52Bft”}}
{“level”:“debug”,“message”:“Workflow execution finished successfully”,“metadata”:{“file”:“logger-proxy.js”,“function”:“exports.debug”,“timestamp”:“2026-01-29T14:36:11.540Z”,“workflowId”:“c15ejvedylR9yAVG52Bft”}}
{“level”:“debug”,“message”:“Executing hook (hookFunctionsSave)”,“metadata”:{“executionId”:“318554”,“file”:“execution-lifecycle-hooks.js”,“timestamp”:“2026-01-29T14:36:11.540Z”,“workflowId”:“c15ejvedylR9yAVG52Bft”}}
{“level”:“debug”,“message”:“Save execution data to database for execution ID 318554”,“metadata”:{“executionId”:“318554”,“file”:“shared-hook-functions.js”,“finished”:true,“function”:“updateExistingExecution”,“stoppedAt”:“2026-01-29T14:36:11.540Z”,“timestamp”:“2026-01-29T14:36:11.540Z”,“workflowId”:“c15ejvedylR9yAVG52Bft”}}
{“level”:“debug”,“message”:“Execution finalized”,“metadata”:{“executionId”:“318554”,“file”:“active-executions.js”,“function”:“finalizeExecution”,“timestamp”:“2026-01-29T14:36:11.548Z”}}
{“level”:“debug”,“message”:“Execution removed”,“metadata”:{“executionId”:“318554”,“file”:“active-executions.js”,“timestamp”:“2026-01-29T14:36:11.548Z”}}
{“level”:“debug”,“message”:“Got request for Call_TestSubFlow_1, and executed it.”,“metadata”:{“file”:“McpServer.js”,“timestamp”:“2026-01-29T14:36:11.548Z”}}

Information on your n8n setup

  • n8n version: 2.4.4
  • Database (default: SQLite): Postgres
  • n8n EXECUTIONS_PROCESS setting (default: own, main):
  • Running n8n via (Docker, npm, n8n cloud, desktop app): AWS EKS
  • Operating system: Bottlerocket

Hi @all-you-can-pete !

Based on the official n8n documentation and community discussions, your observation about the behavior of the Workflow Tool (which internally uses Execute Sub-workflow) together with the MCP Server Trigger in Queue Mode is correct and matches the current n8n design.

Below is a focused explanation of why this happens and the recommended approach for your use case.


Expected Behavior in Queue Mode

1. Workflow Tool / Execute Sub-workflow

  • The Workflow Tool operates internally as Execute Sub-workflow.
  • Synchronous by design: sub-workflows run within the same execution context as the parent workflow.
  • No Redis enqueue: once a worker (or webhook processor acting as a worker) starts the parent workflow, it executes all nested sub-workflows locally.
  • Sub-workflows are not created as separate jobs and therefore cannot be picked up by other workers.

This is confirmed by the n8n team and community:

When using sub-workflow nodes, the entire execution chain runs on the same worker.


2. Execution Flow with MCP Server Trigger

  1. The MCP Server Trigger receives the request (via main process or webhook processor).
  2. The parent workflow execution is enqueued and picked up by a worker.
  3. When the Workflow Tool node is reached, the sub-workflow executes inside the same process, not as a new queued execution.

If you see the sub-workflow running on the MCP/webhook process, it indicates that this process is also handling execution (or you are observing it before handoff completes).

In short: sub-workflows are not distributed because Workflow Tool executes them locally by design.


Recommended Solution: Use Webhooks for Distributed Execution

To force sub-workflows to run as independent, distributed executions, the recommended and documented pattern is to use Webhooks.

Design Pattern

  • Sub-workflow: start with a Webhook Trigger.
  • Parent workflow: replace Workflow Tool with an HTTP Request calling the sub-workflow’s webhook URL.

This causes the sub-workflow to:

  • Start as a new execution
  • Be enqueued in Redis
  • Be processed by the next available worker, as expected in Queue Mode
1 Like

Thanks @tamy.santos, I found that this behaviour is causing an issue with Code Nodes as captured here: Code Tool nodes hang when MCP Server Trigger runs on dedicated webhook service (queue mode) · Issue #23004 · n8n-io/n8n · GitHub so would hope there will be some work to either address this or warn about the behviour in the official documentation.

Regarding documentation, you said

To force sub-workflows to run as independent, distributed executions, the recommended and documented pattern is to use Webhooks

Can you point to where this is documented please as I struggled to find it.

Many thanks!

Hi @all-you-can-pete

The recommendation to use Webhooks + HTTP Request to get separate, distributed executions comes from community answers by n8n staff , not from the main docs pages.

Here are the relevant places:

  • The core docs for sub-workflows and queue mode don’t explicitly say “to distribute sub-workflows across workers, you must use webhooks.” [Sub-workflows docs; Queue mode docs]

The “recommended pattern” to use Webhook Trigger + HTTP Request for independent, distributed executions is documented in the official n8n workflow template “Pattern for Parallel Sub-Workflow Execution Followed by Wait-For-All Loop.” [Sub-workflows in queue mode; Parallel sub-workflow pattern]

If you’re looking for something to link as “documentation,” those two community threads and the template are currently the closest references available that I was able to find during my research.

Okay, thanks for the links. Rather annoying though as standard sub-workflow executions don’t count towards the execution count specified by your license. Switching to webhooks will make those sub-workflows count and for some of our workflows this is unsustainable :frowning: