From adb9586055cd272325ab519f321a703e74c99287 Mon Sep 17 00:00:00 2001 From: Chaim Date: Wed, 5 Aug 2026 14:03:15 +0000 Subject: [PATCH] =?UTF-8?q?fix(ops):=20=D7=A9=D7=A8=D7=AA=20=D7=94-MCP=20?= =?UTF-8?q?=D7=A2=D7=95=D7=91=D7=A8=20=D7=9C-S3=20=E2=80=94=20=D7=A1=D7=95?= =?UTF-8?q?=D7=92=D7=A8=20=D7=90=D7=AA=20=D7=93=D7=A8=D7=99=D7=A4=D7=98=20?= =?UTF-8?q?=D7=94=D7=90=D7=97=D7=A1=D7=95=D7=9F=20=D7=9E=D7=95=D7=9C=20?= =?UTF-8?q?=D7=94=D7=A7=D7=95=D7=A0=D7=98=D7=99=D7=99=D7=A0=D7=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit הקונטיינר רץ `STORAGE_BACKEND=s3` ושרת ה-MCP נפל ל-`filesystem`. כלומר מסמך שסוכן כתב דרך MCP נחת על הדיסק בעוד ה-web חיפש אותו ב-S3 — שני מסלולים לאותו נכס, שמתפצלים בשקט (G2 / INV-STG1). מה נדרש כדי לסגור, ומה נמצא בדרך - **הסודות לא היו קיימים במקור-האמת.** `MINIO_ENDPOINT`/`ACCESS_KEY`/ `SECRET_KEY` חיו רק ב-env של Coolify. נוצרו ב-Infisical תחת `/apps/legal-ai` (tag `credentials`, באישור חיים) ונטענים ל-`~/.legal-mcp-http.env` — אותו דפוס כמו MCP_HTTP_SHARED_SECRET. הערכים עברו ב-stdin דרך infisical-set.sh; לא ב-argv ולא בהיסטוריה. - **`aioboto3` לא היה מותקן ב-venv המקומי.** מוצהר ב-pyproject אך חסר. מסלול ה-S3 מייבא אותו **עצלנית**, ולכן השירות היה עולה בהצלחה ונשבר רק בפעולת-הקובץ הראשונה — כשל שקט עד לשימוש. זו בדיוק הסיבה שהבדיקה רצה לפני ההפעלה ולא אחריה. אימות מהמארח לפני ההפעלה: put/get/delete מלא מול https://s3.nautilus.marcusgroup.org, וקריאת בלוב-ייצור קיים (573KB) מתוך 2,283 אובייקטים בדלי. ה-runbook עודכן: בלוק-האזהרה הוחלף בתיעוד המצב הסגור, כולל מלכודת הייבוא-העצל ופקודת-אימות שאפשר להעתיק. invariants: G2 (מסלול-אחסון אחד לשתי הדלתות) · INV-STG1 · §6 --- docs/operations-runbook.md | 39 ++++++++++++++++++++----------- scripts/legal-mcp-http.config.cjs | 18 +++++++++----- 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/docs/operations-runbook.md b/docs/operations-runbook.md index 6755e07..c54ea4f 100644 --- a/docs/operations-runbook.md +++ b/docs/operations-runbook.md @@ -58,21 +58,34 @@ - שער Bearer (`MCP_HTTP_SHARED_SECRET`); השרת **מסרב לעלות** בלי טוקן. בדיקה: ללא טוקן → `401`, עם טוקן → `200`. - **⚠️ שינוי קוד ב-`mcp-server/` לא נכנס לתוקף עד `pm2 restart legal-mcp-http`.** זה תהליך ארוך-חיים, בניגוד ל-stdio שנטען מחדש בכל סשן. אחרי מיזוג שנוגע ב-`mcp-server/` — להפעיל מחדש, אחרת הסוכנים ירוצו על קוד ישן בשקט. -> #### 🚨 דריפט תצורה פתוח — `STORAGE_BACKEND` -> הקונטיינר רץ `STORAGE_BACKEND=s3`; שרת ה-MCP נופל לברירת-המחדל `filesystem`, כי -> `~/.env` אינו מגדיר אותו. כלומר **סוכן שכותב קובץ דרך MCP כותב לדיסק, בעוד ה-web -> קורא מ-S3** — שני מסלולים שמתפצלים (G2 / INV-STG1). +> #### ✅ תצורת האחסון והאחזור מיושרת לקונטיינר (2026-08-05) +> `STORAGE_BACKEND=s3` ו-`MULTIMODAL_ENABLED=true` — זהים לקונטיינר, כך שמסמך +> שסוכן כותב דרך MCP הוא זה שה-web קורא, ושתי הדלתות מדרגות את אותו קורפוס +> באותה צורה (G2). > -> **למה זה עדיין לא תוקן:** ההעברה ל-s3 דורשת `MINIO_ENDPOINT` + `MINIO_ACCESS_KEY` + -> `MINIO_SECRET_KEY`, ואלה **אינם קיימים ב-Infisical** — רק ב-env של Coolify. הפיכת -> הדגל בלעדיהם תפתור את `MINIO_ENDPOINT` לברירת-המחדל `http://minio:9000` (שם-רשת -> פנימי של דוקר, לא נגיש מהמארח) ותשבור כל פעולת-בלוב. +> **הסודות:** `MINIO_ENDPOINT` · `MINIO_ACCESS_KEY` · `MINIO_SECRET_KEY` נוצרו +> ב-Infisical תחת `/apps/legal-ai` (tag `credentials`), ונטענים ל-`~/.legal-mcp-http.env` +> (chmod 600) — אותו דפוס כמו `MCP_HTTP_SHARED_SECRET`. **רוטציה:** לעדכן +> ב-Infisical → למשוך מחדש לקובץ → `pm2 restart legal-mcp-http`. > -> **מה שצריך כדי לסגור:** ליצור את שלושת המפתחות ב-Infisical תחת `/apps/legal-ai` -> (תגית `credentials`, באישור חיים — ראה כלל "Saving a NEW secret" ב-`~/CLAUDE.md`), -> לטעון אותם בקונפיג ה-pm2 כמו `MCP_HTTP_SHARED_SECRET`, ואז להוסיף -> `STORAGE_BACKEND: "s3"`. MinIO **כן** נגיש מהמארח: `https://s3.nautilus.marcusgroup.org` -> (אומת — `/minio/health/live` מחזיר 200). +> **⚠️ מלכודת — `aioboto3` מיובא עצלנית.** מסלול ה-S3 מייבא אותו רק בקריאה +> הראשונה, ולכן venv בלי החבילה **עולה בהצלחה** ונשבר רק בפעולת-הקובץ הראשונה +> — כשל שקט עד לשימוש. הוא מוצהר ב-`mcp-server/pyproject.toml`, אבל לא היה +> מותקן ב-venv המקומי. אחרי כל בנייה מחדש של ה-venv: +> `~/legal-ai/mcp-server/.venv/bin/pip install -e ~/legal-ai/mcp-server`. +> +> **אימות לפני הפעלה** (הרץ מהמארח, לא מהקונטיינר): +> ```bash +> cd ~/legal-ai/mcp-server && set -a && . ~/.legal-mcp-http.env && set +a +> STORAGE_BACKEND=s3 HOME=/home/chaim DOTENV_PATH=/home/chaim/.env \ +> .venv/bin/python -c "import asyncio;from legal_mcp.services import storage +> async def m(): +> st=storage.get_storage() +> print(len(await st.list_keys('', bucket=storage.Bucket.DOCUMENTS))) +> asyncio.run(m())" +> ``` +> מצופה: מספר האובייקטים בדלי (היו 2,283 ב-2026-08-05). שגיאת +> `ModuleNotFoundError: aioboto3` = ה-venv חסר את התלות. --- diff --git a/scripts/legal-mcp-http.config.cjs b/scripts/legal-mcp-http.config.cjs index f0708f2..ce9c7cf 100644 --- a/scripts/legal-mcp-http.config.cjs +++ b/scripts/legal-mcp-http.config.cjs @@ -63,12 +63,18 @@ const env = { // question from different result sets. ~/.env sets neither, so both fell to // the code defaults (false) — the drift was silent. // - // NOT set here: STORAGE_BACKEND. The container runs `s3`, this service falls - // back to `filesystem`, and closing that gap needs MinIO credentials that do - // not exist in Infisical yet (only in Coolify's env). Flipping the flag - // without them would resolve MINIO_ENDPOINT to the in-cluster default - // `http://minio:9000` — unreachable from the host — and break every blob - // read. Tracked separately; see the note in docs/operations-runbook.md. + // Blob storage must match the container too, for the same reason: a document + // an agent writes through MCP has to be the one the web reads back. The MinIO + // credentials now live in Infisical (/apps/legal-ai) and are loaded from the + // runtime file below, exactly like MCP_HTTP_SHARED_SECRET. + // + // Verified from the host before enabling: put/get/delete round-trip against + // https://s3.nautilus.marcusgroup.org, plus a read of an existing production + // blob out of 2,283 objects. Note the S3 path imports aioboto3 LAZILY, so a + // missing dependency does not fail at boot — it fails on the first blob + // operation. It is declared in mcp-server/pyproject.toml; make sure the venv + // actually has it (`.venv/bin/pip install -e mcp-server`). + STORAGE_BACKEND: "s3", MULTIMODAL_ENABLED: "true", MCP_TRANSPORT: "streamable-http", MCP_HTTP_HOST: "127.0.0.1",