مشغل خادم MCP: فشل إنشاء أداة مع خطأ "error parsing json" خلف reverse proxy (EasyPanel/Traefik)

مرحباً بالجميع. أنا أقوم بتشغيل n8n ذاتي الاستضافة (v2.21.0) على EasyPanel، نسخة واحدة، خلف Traefik. أستخدم عقدة MCP Server Trigger (Streamable HTTP) على مسار /mcp، مع Retell AI كعميل MCP.
الأدوات قصيرة الاستجابة (Get many events) تعمل بشكل مثالي وتظهر في Executions. لكن الأدوات الطويلة الاستجابة (Create event) تُرجع “error parsing json response from mcp server” للعميل، والعقدة Create لا تُنفذ أبداً. البيانات المرسلة من قبل العميل صحيحة (Start/End صالحة مع المنطقة الزمنية).
بدأ هذا مباشرة بعد إعادة تثبيت الخادم. وفقاً لوثائق n8n، يتطلب هذا تعطيل التخزين المؤقت للوكيل وضغط gzip لنقطة نهاية /mcp. ما هي الطريقة الصحيحة لتكوين هذا على EasyPanel/Traefik؟ أي مثال عملي للعلامات/البرمجيات الوسيطة سيكون مفيداً جداً. شكراً!

@Luis_Morello أنت أصبت السبب بشكل أساسي، لكن عند تضييق النطاق، المشكلة في جانب gzip/compress تحديداً، وليس في buffering. middleware الضغط في traefik يعمل فقط على الاستجابات الأكبر من ~1KB (minResponseBodyBytes الافتراضي هو 1024)، لذا مخرجات Get-many الخاصة بك تمر بدون تأثر، لكن استجابة Create الأكبر تتجاوز الحد، تُضغط، و Retell لا يستطيع تحليل المجرى المضغوط فتحصل على “error parsing json” والعقدة لا تعمل أبداً. هذا بالضبط انقسامك بين short-works و long-fails، وتطابقه مع إعادة التثبيت منطقي إذا عادت قيم proxy الافتراضية.

الحل ببساطة هو التأكد من أن /mcp responses لا تُضغط. في easypanel هذا يوجد في ملف traefik مخصص في /etc/easypanel/traefik/config/custom.yaml، ثم أعد تشغيل traefik من الإعدادات. الجزء الأساسي هو استبعاد أنواع محتوى mcp من الضغط:

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

كلا النوعين موجودان هنا لأن streamable-HTTP يمكنه الإجابة بأي منهما حسب حجم الاستجابة، لذا استبعاد event-stream فقط قد يفوتك الحالات الأخرى. الحل الأنظف هو التأكد من أن طلبات /mcp تمر عبر شيء يستخدم هذا (أو بدون middleware ضغط على الإطلاق) بدلاً من ضغط easypanel الافتراضي.

كيف تعرض n8n حالياً في easypanel، من خلال صندوق النطاق المدمج أم من خلال label مخصص خاص بك؟ هذا يحدد ما إذا كنت ستعدل فقط الضغط الموجود أم تضيف موجه /mcp مخصص، أخبرني أي واحد وسأعطيك الأسطر الدقيقة التي تضعها.

مرحبا @Luis_Morello

أضف التسميات التالية إلى خدمة n8n الخاصة بك في قسم EasyPanel → Service → Advanced → Custom Labels:

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 لخدمتك. إذا لم تتمكن من العثور على اسم جهاز التوجيه، فتحقق من تبويب “Labels” في EasyPanel لترى ما هو traefik.http.routers.XXXX.rule المعيّن حاليًا—XXXX هو اسم جهاز التوجيه الخاص بك.)

شكراً @achamm، شرح حد 1KB هذا منطقي تماماً ويطابق تقسيمي بين الأعمال القصيرة والأعمال الطويلة التي تفشل بشكل دقيق. أنا أكشف n8n من خلال صندوق المجال المدمج في EasyPanel (مجال *.easypanel.host الذي يتم إنشاؤه تلقائياً)، وليس من خلال تسميات Docker مخصصة — حقل “Middleware 1” الذي عثرت عليه موجود ضمن Domains → edit → Middlewares في هذا المجال المدمج. نسخة واحدة، Streamable HTTP على /mcp. بناءً على هذا الإعداد، ما هي الأسطر الدقيقة التي يجب أن أضيفها إلى custom.yaml، وكيف أتأكد من أن /mcp يتجاوز ضغط EasyPanel الافتراضي بدلاً من مجرد التراكم فوقه؟ أفضل عدم إطفاء المجال بالكامل بسبب التخمين. شكراً!

@Luis_Morello نعم، إضافة ضغط آخر هناك لن يحل المشكلة، middleware النطاقات المدمج ينطبق على كل مسار، لذلك لا يزال traefik يشغل الضغط الافتراضي جنباً إلى جنب معه، هذا هو السبب في أن /mcp لا يتجاوزه. بما أنك تملك نسخة واحدة ومحرر n8n لا يحتاج فعلاً إلى gzip، فإن أبسط إصلاح بدون توقف هو إزالة الضغط عن ذلك النطاق بالكامل بدلاً من محاربته لكل مسار. هل يسمح لك حقل Middlewares بمسح أو استبدال الضغط الافتراضي، أم أنه يسمح فقط بالإضافة عليه؟ هذا هو ما يحدد ما إذا كان هذا تغييراً في حقل واحد أو موجه custom.yaml صغير فقط لـ /mcp.

شكراً @kjooleng، هذا مفيد حقاً ويتوافق مع تشخيص الضغط. هناك شيئان أريد أن أتأكد من فهمهما قبل أن ألمس أي شيء، حيث أنني أستخدم صندوق النطاق المدمج في EasyPanel (وليس تسميات التوجيه المخصصة) وأفضل عدم كسر النطاق بأكمله بسبب التخمين:

  1. سطر traefik.http.routers.<router>.middlewares=... — ألا يؤدي هذا إلى استبدال قائمة البرمجيات الوسيطة الكاملة على المسار بدلاً من إلحاقها؟ قلقي هو أن EasyPanel قد تدير بالفعل برمجياتها الوسيطة الخاصة على هذا المسار (إعادة توجيه HTTPS، إلخ) خلف الكواليس، لذا قد يؤدي الكتابة فوق القائمة إلى حذف تلك. هل هناك طريقة آمنة لإضافة هاتين البرمجيتين الوسيطتين دون حذف برمجيات EasyPanel المدارة؟
  2. ذكر @achamm أن Streamable HTTP الخاص بـ n8n يمكنه الرد بـ text/event-stream أو application/json حسب حجم الاستجابة، واقترح استبعاد كلا نوعي المحتوى. مقتطفك يستبعد فقط text/event-stream — هل سيكون من الأأمن أن أضيف application/json إلى excludedcontenttypes أيضاً؟

أنا أحب حقاً فكرة رأس Cache-Control: no-transform، سأحتفظ بها بغض النظر. أريد فقط الحصول على جزء التوجيه الصحيح حتى لا أوقف النطاق. شكراً مرة أخرى!

@Luis_Morello أنت محق في القلق، السطر .middlewares= يعيّن قائمة مرتبة كاملة لذلك الموجّه، إنه ليس إضافة، لذا الكتابة فوقه على موجّه easypanels المُدار قد تحذف إعادة التوجيه https الخاصة بهم وأي شيء آخر وضعوه هناك. هذا بالضبط السبب في أنني أترك سلسلة middleware هذا الموجّه وحدها على النطاق المدمج. الأسلوب الأأمن هو موجّه منفصل ذو أولوية أعلى لـ /mcp في custom.yaml لا يمس موجّه easypanels على الإطلاق، /mcp يطابق روتينك أولاً بدون ضغط وكل مسار آخر يحتفظ بسلسلة مُدارة غير مُلمسة. إذا ألصقت اسم خدمة n8n الخاص بك (يعرضه easypanel في إعدادات الخدمة) يمكنني أن أكتب لك كتلة الموجّه الدقيقة هذه.

@Luis_Morello

بناءً على مخاوفك، إليك التسميات الدقيقة المراد استخدامها.

الخطوة أ: تحديد Middlewares (هذه آمنة؛ لا تقوم بالكتابة فوق أي شيء)

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

الخطوة ب: الربط بـ Router (خطوة “التحذير”) تحقق من تسمياتك الحالية. إذا رأيت 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، فإن middleware غير مرفق بشكل صحيح. إذا كان هذا الرأس مفقودًا، فأنت قد نجحت.

مرحباً @Luis_Morello، بما أنك تستخدم النطاق المدمج في EasyPanel و@achamm أكد أن موجه /mcp منفصل هو الخيار الأكثر أماناً، إليك كتلة custom.yaml الدقيقة لإسقاطها في /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(`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 أن موجه /mcp يطابق أولاً، قبل أن يختاره موجه EasyPanel المُدار، بحيث يبقى إعادة توجيه HTTPS الخاص بهم والبرامج الوسيطة الأخرى دون تغيير تماماً.

بعد الحفظ، أعد تشغيل Traefik من إعدادات EasyPanel، ثم قم بتشغيل فحص curl @kjooleng -I للتأكد من اختفاء Content-Encoding: gzip من الاستجابة.

إغلاق هذا — اتضح أنها ليست مشكلة Traefik. تحققت باستخدام wget من أن نقطة نهاية MCP أرجعت ردود متطابقة داخليًا (localhost:5678) وخارجيًا (عنوان URL عام عبر Traefik): نفس text/event-stream، مقسمة إلى أجزاء، بدون gzip، بدون قطع. كانت السبب الحقيقي من جانب Retell — رقم هاتفي كان مرتبطًا بإصدار وكيل قديم كان لا يزال يستخدم عنوان URL n8n ميت، لذا حققت المكالمات الفعلية خطأ 404 عند مصافحة MCP. إعادة ربط الرقم بالإصدار المنشور الحالي حل المشكلة. لم تكن هناك حاجة لأي تغييرات على Traefik. شكرًا على الإشارات!