各位大家好。我在 EasyPanel 上運行自託管的 n8n(v2.21.0),單一副本,並在 Traefik 後面。我在 /mcp 路徑上使用 MCP Server Trigger 節點(Streamable HTTP),並使用 Retell AI 作為 MCP 客戶端。
短回應工具(Get many events)運作完美,並顯示在 Executions 中。但長回應工具(Create event)向客戶端返回「error parsing json response from mcp server」錯誤,Create 節點永遠不會執行。客戶端發送的資料是正確的(有效的 Start/End 與時區)。
這是在我重新安裝伺服器後才開始發生的。根據 n8n 文檔,/mcp 端點需要禁用代理緩衝和 gzip 壓縮。在 EasyPanel/Traefik 上配置這個的正確方法是什麼?任何可行的標籤/中間件示例都將非常感謝。謝謝!
@Luis_Morello 你基本上已經抓到問題所在,只是要再縮小範圍,具體來說是 gzip/compress 這一端,不是 buffering。traefik 的 compress middleware 只會在回應超過約 1KB 的時候才觸發(minResponseBodyBytes 預設值是 1024),所以你的 Get-many 輸出低於這個門檻,乾淨地通過,但較大的 Create 回應會超過它,被 gzip 壓縮,而 Retell 無法解析壓縮的串流,所以你會得到「error parsing json」,node 就不會執行。這正好就是你說的短期可行、長期失敗的情況,而且和重新安裝後對上的情形也符合,如果 proxy 的預設值被復原的話。
所以解決辦法就是確保 /mcp 回應不被 gzip 壓縮。在 easypanel 上,這個設定位在自訂的 traefik 檔案 /etc/easypanel/traefik/config/custom.yaml,然後從設定裡重啟 traefik。關鍵部分是把 mcp content types 從壓縮中排除:
http:
middlewares:
mcp-nocompress:
compress:
excludedContentTypes:
- text/event-stream
- application/json
兩種類型都要列進去,因為 streamable-HTTP 可以根據回應大小用其中任一種方式回應,所以只排除 event-stream 有時會漏掉。最乾淨的做法是確保 /mcp 路由通過某個使用這個設定的東西(或根本不用 compress middleware),而不是用 easypanel 的預設 compress。
你現在在 easypanel 上是怎麼暴露 n8n 的?用內建的 domain 欄位還是你自己的自訂標籤?這決定了你是只要調整現有的 compress 設定,還是要添加一個專用的 /mcp 路由,告訴我你用哪一種,我就給你確切的行數去貼進去。
嗨 @Luis_Morello
在 EasyPanel → Service → Advanced → Custom Labels 部分中為你的 n8n 服務添加以下標籤:
traefik.http.middlewares.n8n-mcp-compress.compress=true
traefik.http.middlewares.n8n-mcp-compress.compress.excludedcontenttypes=text/event-stream
這會告訴 Traefik 壓縮所有內容除了 MCP 串流。
traefik.http.middlewares.n8n-mcp-headers.headers.customResponseHeaders.Cache-Control=no-transform
這確保 Traefik 和 Retell AI 之間的其他代理或快取都不會嘗試修改串流。
關鍵步驟: 你必須找到你的路由器名稱(如果你有 Traefik 儀表板可以看到,或通常遵循 https-n8n-your-project 這樣的模式)。但是,在 EasyPanel/Traefik 中應用此設定的常見方式是:
traefik.http.routers.n8n-router.middlewares=n8n-mcp-compress,n8n-mcp-headers
(將 n8n-router 替換為 EasyPanel 為你的服務實際使用的路由器名稱。如果你找不到路由器名稱,請檢查 EasyPanel 中的「Labels」標籤頁,查看目前設定的 traefik.http.routers.XXXX.rule —— XXXX 就是你的路由器名稱。)
感謝 @achamm,那個 1KB 閾值的解釋完全說得通,完全符合我短篇成功/長篇失敗的分割。我是透過 EasyPanel 的內建網域框(自動產生的 *.easypanel.host 網域)而不是自訂 Docker 標籤來公開 n8n——我找到的「Middleware 1」欄位位於該內建網域的「Domains → edit → Middlewares」下方。單一複本,在 /mcp 上使用可串流的 HTTP。在那樣的設置下,我應該將確切哪些行放入 custom.yaml,以及我如何確保 /mcp 繞過 EasyPanel 的預設壓縮而不只是疊加在上面?我寧可不要透過猜測而導致整個網域停機。謝謝!
@Luis_Morello 是的,在那裡再堆疊一個 compress 不會有效,內建的 domains 中介軟體會套用到每個路徑,所以 traefik 仍然會執行預設的 compress 和它一起執行,這就是為什麼 /mcp 沒有被略過。既然你只有單一副本,而 n8n 編輯器實際上不需要 gzip,最簡單的無停機修復方案是完全從該網域移除 compress,而不是逐個路徑地與它對抗。那個 Middlewares 欄位是讓你清除或替換預設的 compress,還是只能在上面增加?這決定了這是單一欄位的改變,還是需要一個小的自訂 .yaml 路由器專門用於 /mcp。
感謝 @kjooleng,這真的很有幫助,也符合壓縮診斷。在我動手之前,我想確認以下兩點,因為我使用的是 EasyPanel 的內建網域框(而非自訂路由器標籤),我不想因為亂猜而破壞整個網域:
traefik.http.routers.<router>.middlewares=... 這一行 — 這不是會取代路由器上的完整中介軟體列表,而不是追加嗎?我的擔憂是 EasyPanel 可能已經在後台管理那個路由器上的自己的中介軟體(HTTPS 重導向等),所以覆寫列表可能會刪除這些。有沒有辦法不用清除 EasyPanel 管理的中介軟體就安全地新增這兩個中介軟體?
- @achamm 提到 n8n 的 Streamable HTTP 可以回應任一個
text/event-stream 或 application/json,取決於回應大小,並建議排除兩種內容類型。你的程式碼片段只排除了 text/event-stream — 把 application/json 也新增到 excludedcontenttypes 會更安全嗎?
我真的喜歡 Cache-Control: no-transform 標頭的想法,不管怎樣我都會保留那個。只是想把路由器部分做對,這樣才不會讓網域離線。再次謝謝!
@Luis_Morello 你說得沒錯,要擔心,.middlewares= 這一行是為那個路由器設定整個有序列表,它不是附加,而是覆蓋,所以在 easypanels 管理的路由器上覆蓋它會丟掉他們的 https-redirect 和他們放在那裡的任何其他東西。這正是為什麼我會在內建網域上單獨留下那個路由器的中間件鏈。最安全的做法是在 custom.yaml 中為 /mcp 建立一個單獨的更高優先級路由器,完全不觸及 easypanels 的路由器,/mcp 就只匹配你的路由器,沒有壓縮,而每個其他路徑都保持他們的管理鏈不被觸及。如果你貼上你的 n8n 服務名稱(easypanel 在服務配置中會顯示它),我可以為你寫出那個確切的路由器區塊。
@Luis_Morello
根據你的顧慮,以下是要使用的確切標籤。
步驟 A:定義中介軟體(這些是安全的;它們不會覆蓋任何東西)
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
步驟 B:附加到路由器(「注意」步驟) 檢查你的現有標籤。如果你看到 traefik.http.routers.XYZ.middlewares=ABC,請使用:
traefik.http.routers.XYZ.middlewares=ABC,n8n-mcp-compress,n8n-mcp-headers
最後一次檢查: 部署後,如果你想 100% 確認它正常運作,在你知道會傳回「長」回應的工具上使用 curl 指令:curl -I -H "Accept: application/json" https://your-n8n-domain.com/mcp/your-tool-path 查看 Content-Encoding 標頭。如果它顯示 gzip,表示中介軟體未正確附加。如果該標頭遺失,你就成功了。
嘿 @Luis_Morello,既然你在 EasyPanel 的內建網域上,而且 @achamm 確認了獨立的 /mcp 路由器是最安全的做法,以下是要放入 /etc/easypanel/traefik/config/ 的確切 custom.yaml 區塊
http:
middlewares:
mcp-nocompress:
compress:
excludedContentTypes:
- text/event-stream
- application/json
mcp-notransform:
headers:
customResponseHeaders:
Cache-Control: "no-transform"
routers:
n8n-mcp:
rule: "Host(`your-instance.easypanel.host`) && PathPrefix(`/mcp`)"
priority: 100
service: "your-n8n-service@docker"
middlewares:
- mcp-nocompress
- mcp-notransform
tls: {}
將 your-instance.easypanel.host 替換為你的實際網域,將 your-n8n-service@docker 替換為 EasyPanel 中顯示的服務名稱(通常是 -@docker)。priority: 100 確保此路由器在 EasyPanel 的受管路由器選擇它之前先匹配 /mcp,這樣他們的 HTTPS 重定向和其他中介軟體保持完全不受影響。
保存後,從 EasyPanel 設置重啟 Traefik,然後執行 @kjooleng curl -I 檢查以確認回應中的 Content-Encoding: gzip 已消失。
關閉此問題 — 結果證實不是 Traefik 的問題。我用 wget 驗證了 MCP 端點在內部 (localhost:5678) 和外部 (通過 Traefik 的公開 URL) 返回相同的回應:相同的 text/event-stream、分塊傳輸、無 gzip、無截斷。真正的原因在 Retell 一側 — 我的電話號碼被綁定到一個舊的代理版本,該版本仍然使用已停用的 n8n URL,所以真實通話在 MCP 握手時命中 404。將號碼重新連結到目前發佈的版本修復了此問題。不需要更改 Traefik。感謝你的指點!