Instance-level MCP OAuth re-auth fails with invalid_target on n8n 2.29.7; works on earlier version

Describe the problem/error/question

Instance-level MCP OAuth re-authentication appears to regress on n8n 2.29.7.
After upgrading a self-hosted n8n instance to 2.29.7, remote MCP OAuth re-authentication fails for both OpenAI/ChatGPT and Claude web connectors with invalid_target / Invalid resource indicator.
The same MCP hostname, Cloudflare route, and remote clients work again immediately when I switch back to an earlier n8n copy. Local MCP access also continues to work, so this looks specific to the remote OAuth authorize/resource-indicator path in 2.29.7 rather than a general MCP outage.

What is the error message (if any)?

{“error”:“invalid_target”,“error_description”:“Invalid resource indicator”}

Please share your workflow

Not workflow-specific. This is Instance-level MCP / OAuth re-authentication, not a workflow execution issue.

Information on your n8n setup

n8n version: 2.29.7 fails. Earlier 2.x Proxmox LXC copy works with the same remote MCP setup.
Database: likely default community-scripts setup / SQLite. Not 100% confirmed.
n8n EXECUTIONS_PROCESS / EXECUTIONS_MODE: default/single-main-process mode, not queue mode.
Running n8n via: self-hosted Proxmox LXC, installed/managed through the community-scripts Proxmox n8n helper, running as a systemd service.
Operating system: Debian GNU/Linux in Proxmox LXC.
Reverse proxy/network: Cloudflare Tunnel / Cloudflare WAF in front. Main n8n hostname and MCP hostname are separate; the MCP hostname routes to the same n8n backend.
Instance-level MCP is enabled.

Extra details / reproduction steps

Steps tested:

  1. Run n8n 2.29.7 with Instance-level MCP enabled.
  2. Expose the MCP OAuth route through a separate hostname, e.g. n8n-mcp.[domain].
  3. Attempt to reconnect/re-authenticate the OpenAI/ChatGPT remote MCP connector.
  4. It fails with:
    {“error”:“invalid_target”,“error_description”:“Invalid resource indicator”}
  5. Attempt to reconnect/re-authenticate the Claude remote MCP connector.
  6. It fails with the same error.
  7. Cloudflare telemetry shows the request is passed through to n8n, not blocked.
  8. Protected-resource discovery for /mcp-server/http returns the expected resource.
  9. Local MCP access still works.
  10. Stop the 2.29.7 instance and start an earlier n8n copy using the same route.
  11. OpenAI/ChatGPT and Claude reconnect successfully, and remote MCP reads work again.
    Expected result:
    Remote MCP OAuth re-authentication should complete successfully for both OpenAI/ChatGPT and Claude, as it does on the earlier n8n copy.
    Actual result:
    n8n 2.29.7 rejects the OAuth authorize request with invalid_target / Invalid resource indicator.
    Hypothesis:
    This may be a regression in Instance-level MCP OAuth protected-resource / resource-indicator validation in 2.29.7, possibly when the MCP OAuth hostname is a separate alias from the main n8n hostname.

Thanks @Andyp27, I have created ADO-5561 as the internal dev ticket to fix this

That error is RFC 8707 resource-indicator validation, and your “separate alias” hypothesis is almost certainly right. 2.29.7 looks to have tightened it to an exact match: when ChatGPT/Claude send the resource param, it’s compared against n8n’s canonical resource URL. If MCP is served on n8n-mcp.domain but N8N_EDITOR_BASE_URL / WEBHOOK_URL still point at the main hostname, they won’t match and you get invalid_target.

Workaround worth trying before the fix lands: make them identical. Point n8n’s base URL at the exact MCP hostname the clients connect to, and confirm the resource value in the /.well-known protected-resource doc matches character-for-character (scheme, host, no stray trailing slash).

Why is the main hostname and MCP hostname different? Trying to understand the reason behind it before trying to patch it.

Hi Syed and Ricardo,

Thanks for the replies and promptness, Syed - that’s exactly the area I suspected after testing.

The reason the main hostname and MCP hostname are different is intentional separation of trust boundaries.

In my setup:

  • n8n.[domain] is the main human/admin UI.
  • n8n-mcp.[domain] is only for remote MCP clients such as ChatGPT/OpenAI and Claude.

The main n8n hostname is protected by Cloudflare Zero Trust / Access. That works well for the browser UI and for controlled local tooling, but it does not work cleanly for the remote MCP clients during OAuth/re-auth, because the clients need to reach n8n’s MCP/OAuth endpoints directly and cannot complete or carry the Cloudflare Access flow in the same way a human browser session can.

So the separate MCP hostname exists to avoid exposing the main UI while still allowing MCP OAuth to function. The MCP hostname is not open in the same way as the UI. It is locked down with Cloudflare WAF default-deny rules so only the MCP/OAuth-related paths are allowed to the AI agent calls, for example /mcp-server, /mcp-oauth, /.well-known, /oauth/consent and the minimal n8n frontend/static paths needed for the OAuth consent flow. Everything else, including the normal UI surface, API/webhook paths, etc., is blocked at the MCP hostname.

So the intention is:

  • Main UI hostname: protected by Cloudflare Access / Zero Trust.
  • MCP hostname: narrow public MCP/OAuth door, protected by n8n OAuth plus Cloudflare WAF path allow-listing.

That separation worked on the earlier n8n version. After upgrading to 2.29.7, the same setup fails with invalid_target / Invalid resource indicator for both OpenAI and Claude remote MCP re-auth. Switching back to the earlier n8n copy, with the same DNS/WAF/client setup, makes both remote connectors work again.

Your explanation makes sense: if 2.29.7 now validates the RFC 8707 resource indicator against the canonical URL generated from N8N_EDITOR_BASE_URL / WEBHOOK_URL, then the separate MCP hostname would no longer match and would be rejected.

The workaround of making the base URL identical to the MCP hostname might prove the cause, and I can test that on a staging copy. But for production it is not ideal, because setting the global n8n base URL to the MCP hostname would effectively make the MCP hostname the canonical UI/webhook hostname too, which defeats the reason for separating the UI trust boundary from the MCP/OAuth trust boundary.

The ideal fix, from my point of view, would be one of:

  1. Allow configuring a separate public MCP/OAuth base URL/resource URL, distinct from N8N_EDITOR_BASE_URL / WEBHOOK_URL.
  2. Generate/validate the MCP protected-resource metadata against the actual MCP hostname being used, e.g. Host / X-Forwarded-Host, where safe.
  3. Allow an explicit list of trusted MCP resource indicators/hostnames for self-hosted reverse-proxy setups.

Happy to test a staging workaround or provide headers / discovery output if useful.

Yup, same issue here and want to echo Andyp27’s reasoning. We also have a different domain for mcp clients, because our primary / human access is gated behind AWS verified access, to ensure only internal users are able to accesss our n8n deployment.

We have an AWS API gateway that allows for MCP access, but that’s on a different domain and is restricted to specific paths.

Thanks for the detailed response and for sharing your thoughts on the potential fixes. I’ll take a look and address this soon.

Quick update: we have a fix ready. It does require setting one environment variable, so I want to explain why.

The strict resource validation that broke your setup in 2.29 was introduced as a security hardening (RFC 8707 audience binding for MCP OAuth tokens), so reverting it isn’t on the table. And honestly, your report was the first time the split-hostname topology was on my radar (I work on the MCP server). It’s a legitimate setup and the detail in your write-up made the diagnosis straightforward, so thank you for that.

The fix adds a dedicated setting for exactly your case:

N8N_MCP_BASE_URL=https://n8n-mcp.

When set, n8n treats that hostname as the canonical public URL of the MCP server: it’s advertised in discovery, accepted as the RFC 8707 resource indicator, and used as the token audience. Your N8N_EDITOR_BASE_URL and webhook URLs stay untouched, so the trust-boundary separation you described is preserved, and clients connecting through the main hostname keep working too.

Thanks Ricardo, that’s great news and will work nicely.

Appreciate the effort and attention in getting the new environment variable for MCP seperate URL.

Out of interest, when is the next patch release due and will this likely land in the version? Thanks

Trying to get into tomorrow’s release!

New version n8n@2.31.0 got released which includes the GitHub PR 34028.