MCP Server Trigger returns 502 after ~125s while long-running subworkflow completes successfully and is triggered twice

MCP Server Trigger returns 502 after ~125s while subworkflow completes successfully and is triggered twice

Describe the problem/error/question

Hi everyone,
I am trying to diagnose a reproducible issue with an n8n Cloud MCP Server Trigger and a long-running tool/subworkflow.
Setup:

  • n8n Cloud, Starter plan
  • n8n version 2.36.5
  • MCP Server Trigger using the published Production URL (/mcp/…, not /mcp-test/…)
  • Authentication: n8n User Auth (OAuth2)
  • Require Workflow Execute Permission: enabled
  • External MCP client user-agent shown by n8n: openai-mcp/1.0.0
  • The MCP client is a custom MCP app in a ChatGPT Business workspace
  • The MCP tool starts another n8n workflow which calls Anthropic Claude Sonnet 5 and returns one text field
    The downstream Claude workflow itself completes successfully.
    In the first affected run:
  • the reviewer/subworkflow started at about 08:59:40
  • total runtime was 3m 13s
  • n8n status was Success
  • Claude returned a thinking block and a normal text block
  • stop_reason was end_turn
  • the following Edit Fields node successfully produced a non-empty result
    The Claude configuration was:
    Model: claude-sonnet-5
    Maximum Number of Tokens: 32768
    Simplify Output: off
    Usage in that run was approximately:
    input_tokens: 2977
    output_tokens: 16689
    thinking_tokens: 11195
    So the Claude call itself did not fail, and the expected text result existed successfully inside n8n.
    However, the external MCP caller receives a 502 instead of the completed result.
    The timing is very reproducible:
    First reviewer starts: 08:59:40
    First MCP event: 09:01:45
    Difference: ~125 seconds
    Immediately afterwards a second reviewer execution starts with the same tool payload:
    Second reviewer starts: 09:01:46
    Second MCP event: 09:03:51
    Difference: ~125 seconds
    The two incoming MCP requests have:
  • the same tool name
  • the same payload on the visible field level
  • the same request body size
  • the same openai-mcp/1.0.0 user-agent
    but they have different MCP session IDs and different request/trace identifiers.
    So they appear to be two separate external MCP requests, not one n8n execution being displayed twice.
    I have also verified that the ChatGPT MCP app is using the published n8n Production URL, not the Test URL.
    My current hypothesis, not yet verified, is that there may be a synchronous MCP timeout around 120 seconds:
    MCP request starts
    → long-running subworkflow continues
    → MCP request/connection times out after about 120 seconds
    → external caller receives 502
    → n8n subworkflow continues and eventually succeeds
    → another external MCP request arrives and starts another execution
    Questions:
  1. Is there currently a ~120 second timeout for MCP Server Trigger tool calls on n8n Cloud?
  2. Can a worker/subworkflow continue running after the synchronous MCP request has already timed out?
  3. Is such a timeout configurable on n8n Cloud?
  4. Is a second MCP request after such a timeout a known retry/reconnect behavior?
  5. Does n8n itself ever replay/retry an MCP tool call, or would a new MCP session necessarily originate from the external client?
  6. Is there a known issue in n8n 2.36.5 involving MCP Server Trigger, long-running tools and 502/timeouts?
  7. What is the recommended n8n architecture for an MCP tool that may legitimately take 2–4 minutes?
  8. Is there an n8n-native way to implement idempotency/duplicate protection so that a reconnect or retry cannot execute the same external action twice?
    I have stopped further testing for now because the downstream call has real external cost and I need to make sure that one approved tool invocation cannot silently result in two executions.

What is the error message (if any)?

Please share your workflow

I cannot share the full production workflow publicly because it contains internal configuration and confidential payload definitions.

The relevant workflow structure is very small:

MCP Server Trigger
    ↓ tool call
Execute Workflow Tool: claude_review
    ↓
Subworkflow:

When Executed by Another Workflow
    ↓
Anthropic "Message a Model" (claude-sonnet-5)
    ↓
Edit Fields

The Edit Fields node extracts the Claude text block into one field called:

claude_review

No additional HTTP Request, Code, retry, wait, loop, or response-processing nodes are present in this path.

I can provide a sanitized/minimal reproduction workflow if that is required for diagnosis.

Share the output returned by the last node

The last node of the reviewer subworkflow successfully returns a non-empty field:
{
“claude_review”: “[REDACTED - non-empty Claude review text]”
}
The Claude node immediately before it also shows:
content[0].type = “thinking”
content[1].type = “text”
stop_reason = “end_turn”
So the expected review text exists successfully inside the downstream workflow before the MCP response fails.
In the MCP execution log, however, the tool output visible after the ~125 second interval contains the original query/input structure rather than the completed claude_review result.

Information on your n8n setup

n8n version: 2.36.5

  • Database (default: SQLite): n8n Cloud managed / unknown to me
  • n8n EXECUTIONS_PROCESS setting (default: own, main): n8n Cloud managed / unknown to me
  • Running n8n via: n8n Cloud, Starter plan
  • Operating system: n8n Cloud managed / unknown to me

Hey @Stephan_Last, while you wait for a response, here are some things that might help:

Suggested resources

Automatically matched to your question.

Docs:

Forum:

@albertnerikat, @tamy.santos, @Vinicius_Lopes - you’ve helped with similar issues before, can you take a look?

Automatically suggested by n8n’s community bot. It’s a pilot - please share feedback here.

Hi @Stephan_Last Welcome!
The deadline belongs to the ChatGPT MCP client, not n8n. The MCP Server Trigger has no timeout parameter and Cloud exposes none, and n8n never replays a tool call, so the second execution is ChatGPT re-issuing it after deciding the first one failed, which is why the payload matches but the session ID is new. The sub-workflow is a separate execution, so it keeps running and succeeds after the caller has gone.
A tool that legitimately runs 2 to 4 minutes cannot answer synchronously. Split it so the MCP tool records the request and returns an id immediately, run the Claude call on its own path, and expose a second tool that returns the finished text for that id. Gate the Claude call on a Data Table marker keyed on the request, If Row Does Not Exist then Insert, so a re-issued call cannot run it twice.

Hello Anshul, thank you very much – that helps me a lot and particularly explains why the long-running subworkflow continues to succeed despite the lost MCP response.

The division into “Accept request → return ID immediately → execute long-running process separately → retrieve result later via second tool using the ID” seems very well-suited to my use case.

For the robust implementation, I still have two brief follow-up questions:

1. Idempotency / Race Condition:
Is Data Table → If Row Does Not Exist → Insert in n8n also safe when two identical tool calls with the same request/attempt ID arrive almost simultaneously? Or could both executions first see “Row does not exist” and then both start the external Claude call? Is there a recommended atomic claim/unique key mechanism for this in n8n?

2. Repeated MCP Call:
Is the behavior you described – ChatGPT calling again with the same payload after the first failed/aborted tool call – currently observed client behavior, or is there documented/binding basis for this? My concern is not to make the architecture dependent on a non-guaranteed retry behavior of the client.

Thank you again!

  1. It can race. If Row Does Not Exist and Insert are two separate operations, so two near-simultaneous executions can both pass the check before either one writes. Use the single Upsert operation instead, which carries the filter and the row in one request. Data table columns are only Boolean, Date, Number or String with no unique constraint available, so that narrows the window rather than closing it.
  2. Nothing binding. The spec has a sender cancel a request once it passes its timeout, and over Streamable HTTP closing the response stream is itself the cancellation signal. Re-issuing the call appears nowhere in it, so the second invocation is ChatGPT’s own behaviour rather than something the protocol obliges.
    That is the case for not resting on the marker. Once the tool answers in milliseconds no deadline expires, nothing is cancelled, and there is no second call left to deduplicate.
    Cancellation - Model Context Protocol

Anshul is right that the fix is architectural. One detail on the race though, because upsert alone narrows the window without closing it when there is no unique constraint underneath. The lock-free way out is claim then verify: insert a row carrying this execution’s own id, then read the row back by key and compare. If you see your own id you won the claim and may run the tool, if you see another execution’s id you lost and exit quietly. Two cheap operations, no constraint needed, and simultaneous calls stop being a coin toss.

Second detail: pick the key carefully. Your two executions came with different MCP session ids, so session is useless as identity. Key the claim on what makes the request the same request: a hash of tool name plus normalized payload, optionally bucketed to a time window. Same content arriving on a new session lands on the same row, which is exactly what you want here.

And belt plus suspenders: make the downstream idempotent too. Before the expensive part does its side effects, check whether a result already exists under that same key. Deduplication at the door narrows the window, idempotent writes close it, and a client retry then costs you nothing whichever layer catches it. One addition to the async split Anshul described: a retry with the same payload should get the same request id back, then the second call is harmless by construction.

Thank you very much for the addition. I found the pointer to a session-independent, stable request key particularly helpful, as well as the additional idempotency in the downstream. I will technically verify the proposed claim/read-back mechanism with respect to the parallelism semantics before deriving a hard exactly-once assumption from it. Thank you for the detailed explanation.