MCP Server Trigger: Falha ao criar tool com "erro ao analisar json" atrás de reverse proxy (EasyPanel/Traefik)

Oi a todos. Estou rodando n8n auto-hospedado (v2.21.0) no EasyPanel, com uma única réplica, atrás do Traefik. Uso o nó MCP Server Trigger (Streamable HTTP) no caminho /mcp, com Retell AI como cliente MCP.
As ferramentas com respostas curtas (Get many events) funcionam perfeitamente e aparecem em Executions. Mas ferramentas com respostas mais longas (Create event) retornam “error parsing json response from mcp server” ao cliente, e o nó Create nunca é executado. Os dados enviados pelo cliente estão corretos (Start/End válidos com timezone).
Isso começou logo após eu reinstalar o servidor. De acordo com a documentação do n8n, isso requer que o buffering de proxy e a compressão gzip sejam desabilitados para o endpoint /mcp. Qual é a maneira correta de configurar isso no EasyPanel/Traefik? Algum exemplo funcionando dos labels/middleware seria muito apreciado. Obrigado!

@Luis_Morello você basicamente identificou a causa, só afunilando, é especificamente o lado gzip/compress, não o buffering. O middleware de compress do traefik só funciona em respostas maiores que ~1KB (minResponseBodyBytes padrão é 1024), então sua saída Get-many passa por baixo disso e passa limpa, mas a resposta Create maior cruza o limite, fica compactada, e o Retell não consegue fazer parse da stream compactada então você recebe “error parsing json” e o node nunca executa. Isso é exatamente seu split short-works long-fails, e se alinhar com a reinstalação faz sentido também se os padrões do proxy voltaram.

então o fix é só garantir que respostas /mcp não sejam gzipadas. no easypanel isso fica em um arquivo traefik customizado em /etc/easypanel/traefik/config/custom.yaml, aí restart o traefik nas settings. o detalhe importante é excluir os content types do mcp da compressão:

http:
  middlewares:
    mcp-nocompress:
      compress:
        excludedContentTypes:
          - text/event-stream
          - application/json

os dois tipos estão aí porque streamable-HTTP pode responder como qualquer um dependendo do tamanho da resposta, então excluir só event-stream às vezes não pega. o mais limpo é garantir que rotas /mcp passem por algo que use isso (ou nenhum middleware compress) ao invés do compress padrão do easypanel.

como você está expondo n8n no easypanel agora, a caixa de domínio built-in ou seus próprios labels customizados? isso decide se você só faz tweak no compress existente ou adiciona um router /mcp dedicado, me fala qual e eu dou as linhas exatas pra colar.

Oi @Luis_Morello

Adicione os seguintes rótulos ao seu serviço n8n na seção EasyPanel → Service → Advanced → Custom Labels:

traefik.http.middlewares.n8n-mcp-compress.compress=true
traefik.http.middlewares.n8n-mcp-compress.compress.excludedcontenttypes=text/event-stream

Isso informa ao Traefik para comprimir tudo exceto o stream MCP.

traefik.http.middlewares.n8n-mcp-headers.headers.customResponseHeaders.Cache-Control=no-transform

Isso garante que nenhum outro proxy ou cache entre o Traefik e o Retell AI tente modificar o stream.

Etapa Crucial: Você deve encontrar o nome do seu roteador (visível no painel do Traefik se você o tiver, ou geralmente segue um padrão como https-n8n-seu-projeto). Porém, uma maneira comum de aplicar isso no EasyPanel/Traefik é:

traefik.http.routers.n8n-router.middlewares=n8n-mcp-compress,n8n-mcp-headers

(Substitua n8n-router pelo nome do roteador real usado pelo EasyPanel para seu serviço. Se não conseguir encontrar o nome do roteador, verifique a aba “Labels” no EasyPanel para ver qual traefik.http.routers.XXXX.rule está atualmente configurado—o XXXX é o nome do seu roteador.)

Obrigado @achamm, aquela explicação do limite de 1KB faz total sentido e combina perfeitamente com minha divisão entre works curtos e long-fails. Estou expondo o n8n através da caixa de domínio integrada do EasyPanel (o domínio auto-gerado *.easypanel.host), não de rótulos Docker customizados — o campo “Middleware 1” que encontrei fica em Domains → edit → Middlewares naquele domínio integrado. Uma única réplica, HTTP Streamable em /mcp. Com essa configuração, quais linhas exatas devo colocar em custom.yaml, e como garantir que /mcp contorne a compressão padrão do EasyPanel em vez de apenas se empilhar em cima dela? Prefiro não derrubar o domínio inteiro chutando. Obrigado!

@Luis_Morello yeah stacking another compress ali não vai resolver, o middleware de domínios built-in se aplica a todo path, então o traefik ainda roda o compress padrão junto, é por isso que /mcp não tá burlando. como você tem single replica e o editor do n8n na verdade não precisa de gzip, o fix mais simples sem downtime é remover compress desse domínio inteiro em vez de ficar brigando por path. esse campo Middlewares deixa você limpar ou substituir o compress padrão, ou só adiciona por cima? isso é que decide se é uma mudança de um campo só ou um custom.yaml router pequeno só pra /mcp.

Obrigado @kjooleng, isso é muito útil e se alinha com o diagnóstico de compressão. Duas coisas que quero ter certeza de entender antes de mexer em qualquer coisa, já que estou usando a caixa de domínio integrada da EasyPanel (não rótulos de roteador personalizados) e prefiro não quebrar o domínio inteiro chutando:

  1. A linha traefik.http.routers.<router>.middlewares=... — isso não substitui a lista completa de middlewares no roteador em vez de apenas adicionar? Minha preocupação é que a EasyPanel já gerencia seus próprios middlewares nesse roteador (redirecionamento HTTPS, etc.) nos bastidores, então sobrescrever a lista pode remover esses. Existe uma forma segura de adicionar esses dois middlewares sem apagar os gerenciados pela EasyPanel?
  2. @achamm mencionou que o HTTP Streamable do n8n pode responder como tanto text/event-stream quanto application/json dependendo do tamanho da resposta, e sugeriu excluir ambos os tipos de conteúdo. Seu snippet apenas exclui text/event-stream — seria mais seguro adicionar application/json a excludedcontenttypes também?

Eu realmente gosto da ideia do header Cache-Control: no-transform, vou manter isso independentemente. Só quero acertar a parte do roteador para não derrubar o domínio. Obrigado novamente!

@Luis_Morello você tem razão em se preocupar, a linha .middlewares= define toda a lista ordenada para aquele router, não é um append, então sobrescrever isso no router gerenciado do easypanel removeria o https-redirect e qualquer outra coisa que eles colocaram lá. é exatamente por isso que eu deixaria aquela cadeia de middleware do router sozinha no domínio built-in. o mais seguro é um router separado com prioridade maior para /mcp no custom.yaml que não toca no router do easypanel, /mcp apenas corresponde ao seu primeiro sem compress e todos os outros caminhos mantêm sua cadeia gerenciada intacta. se você colar o nome do seu serviço n8n (easypanel mostra isso na configuração do serviço) eu posso escrever esse bloco de router exato para você.

@Luis_Morello

Com base nas suas preocupações, aqui estão os rótulos exatos para usar.

Etapa A: Defina os Middlewares (Estes são seguros; eles não sobrescrevem nada)

traefik.http.middlewares.n8n-mcp-compress.compress=true
traefik.http.middlewares.n8n-mcp-compress.compress.excludedcontenttypes=text/event-stream,application/json
traefik.http.middlewares.n8n-mcp-headers.headers.customResponseHeaders.Cache-Control=no-transform

Etapa B: Anexe ao Router (A Etapa de “Cuidado”) Verifique seus rótulos existentes. Se você vir traefik.http.routers.XYZ.middlewares=ABC, use:

traefik.http.routers.XYZ.middlewares=ABC,n8n-mcp-compress,n8n-mcp-headers

Uma verificação final: Depois de fazer o deploy, se você quiser ter 100% de certeza de que está funcionando, use um comando curl em uma ferramenta que você sabe que retorna uma resposta „longa": curl -I -H "Accept: application/json" https://your-n8n-domain.com/mcp/your-tool-path Procure pelo cabeçalho Content-Encoding. Se disser gzip, o middleware não foi anexado corretamente. Se esse cabeçalho estiver ausente, você venceu.

Oi @Luis_Morello, já que você está no domínio integrado do EasyPanel e @achamm confirmou que um roteador /mcp separado é o caminho mais seguro, aqui está o bloco custom.yaml exato para colocar em /etc/easypanel/traefik/config/

http:

  middlewares:

    mcp-nocompress:

      compress:

        excludedContentTypes:

          - text/event-stream

          - application/json

    mcp-notransform:

      headers:

        customResponseHeaders:

          Cache-Control: "no-transform"



  routers:

    n8n-mcp:

      rule: "Host(`seu-instancia.easypanel.host`) && PathPrefix(`/mcp`)"

      priority: 100

      service: "seu-servico-n8n@docker"

      middlewares:

        - mcp-nocompress

        - mcp-notransform

      tls: {}

Substituir seu-instancia.easypanel.host pelo seu domínio real e seu-servico-n8n@docker pelo nome do serviço mostrado no EasyPanel (geralmente -<serviço>@docker). O priority: 100 garante que este roteador encontre /mcp primeiro, antes do roteador gerenciado do EasyPanel pegá-lo, então o redirecionamento HTTPS e outros middlewares deles continuam completamente intactos.

Depois de salvar, reinicie o Traefik nas Configurações do EasyPanel, depois execute a verificação curl -I do @kjooleng para confirmar que Content-Encoding: gzip desapareceu da resposta.

Resolvido — afinal NÃO era um problema do Traefik. Verifiquei com wget que o endpoint MCP retornou respostas idênticas internamente (localhost:5678) e externamente (URL pública através do Traefik): mesmo text/event-stream, chunked, sem gzip, sem truncamento. A causa real estava no lado do Retell — meu número de telefone estava vinculado a uma versão antiga do agente que ainda usava uma URL n8n morta, então chamadas reais recebiam um 404 no handshake do MCP. Re-vincular o número à versão publicada atual resolveu. Nenhuma alteração no Traefik foi necessária. Obrigado pelas dicas!