From cb6565453e1f7a102daee605da1be17ca09bf8cc Mon Sep 17 00:00:00 2001 From: bolbol Date: Thu, 2 Jul 2026 21:13:02 +0000 Subject: [PATCH] recovery: recuperation commit local orphelin (master jamais pousse) - MCP-NAS-SCRIPTING.md --- common/MCP-NAS-SCRIPTING.md | 184 ++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 common/MCP-NAS-SCRIPTING.md diff --git a/common/MCP-NAS-SCRIPTING.md b/common/MCP-NAS-SCRIPTING.md new file mode 100644 index 0000000..f146e96 --- /dev/null +++ b/common/MCP-NAS-SCRIPTING.md @@ -0,0 +1,184 @@ +# MCP NAS — Référence de scripting fiable + +**Date** : 2026-06-23 | Session debug Veille/Bifrost (40+ tool calls observés) + +--- + +## Environnement du container + +| Propriété | Valeur | +|-----------|--------| +| Shell | `/bin/bash` | +| User | `root` uid=0 | +| Python | `3.11.15` — `/usr/local/bin/python3` | +| Modules Python | `requests`, `sqlite3`, `json`, `urllib`, `subprocess` | +| Filesystem `/mnt` | NAS Synology 11 To, R/W | +| Réseau host Docker | `172.17.0.1` | +| Réseau LAN NAS | `192.168.100.33` — **INACCESSIBLE depuis container** | + +### Binaires (vérifié 2026-06-23) + +| Outil | Dispo | Note | +|-------|-------|------| +| `curl` | oui | `/usr/bin/curl` — soumis limite 350 chars | +| `base64` | oui | `/usr/bin/base64` | +| `python3` | oui | 3.11 complet | +| `git` | oui | via subprocess Python | +| `sqlite3` CLI | non | utiliser module Python `import sqlite3` | +| `docker` CLI | non | pas de socket monté — Portainer API | +| `paramiko` | non | pas de SSH natif Python | + +--- + +## RÈGLE 1 — Sessions : poisoning + +Après 3+ erreurs consécutives, la session refuse même `echo hello`. +Symptôme : `{"error": "Error occurred during tool execution"}` sur commande triviale. + +Sessions mortes dans cette session : `claude`, `db_update`, `verify`. +Sessions stables : `model_test`, `emergency`, `s1` (utilisées fraîchement). + +**Toujours spécifier `session=` dans run_command** (sans = erreur systématique). +Après 2 erreurs d'affilée : changer de session immédiatement. +Si toutes poisonnées : `new_session("fresh1")`. + +--- + +## RÈGLE 2 — Heredoc interdit + +`<< 'EOF'` dans toute forme cause un **timeout 120s** qui tue la session. +Cela inclut `cat > f << 'EOF'`, `python3 << 'PYEOF'`, `bash << 'EOF'`. + +Cause : l'injection tmux ne gère pas l'attente stdin que heredoc déclenche. + +--- + +## RÈGLE 3 — Limite 350 chars par commande + +Au-delà de ~350 chars dans le champ `command` : timeout, troncature ou erreur silencieuse. + +**Méthode 1 — Code court (< 200 chars)** + +Deux appels séparés : +``` +printf 'import json\nprint("ok")\n' > /tmp/s.py +python3 /tmp/s.py +``` + +**Méthode 2 — Code moyen (200–350 chars)** + +Appender en plusieurs appels : +``` +printf 'import sqlite3\nconn=sqlite3.connect("/mnt/docker/bifrost/data/config.db")\n' > /tmp/s.py +printf 'cur=conn.cursor()\ncur.execute("SELECT * FROM config_keys")\n' >> /tmp/s.py +printf 'for r in cur.fetchall(): print(r)\n' >> /tmp/s.py +python3 /tmp/s.py +``` + +**Méthode 3 — Code long ou avec quotes simples (> 350 chars)** + +Base64 encode côté Claude (bash_tool), decode sur NAS (2 run_command) : +``` +# bash_tool : base64 -w0 fichier.py → obtenir la chaîne B64 +# run_command 1 : printf '' | base64 -d > /tmp/s.py +# run_command 2 : python3 /tmp/s.py +``` + +Piège : une quote simple `'` dans printf termine la chaîne → code tronqué sans erreur. +Dès qu'il y a des `'` dans le code Python → base64 obligatoire. + +--- + +## RÈGLE 4 — Services internes (172.17.0.1) + +**Portainer — restart container** +```python +import urllib.request, json +# Auth +body = json.dumps({"username":"bestof","password":""}).encode() +req = urllib.request.Request("http://172.17.0.1:9000/api/auth", data=body, + headers={"Content-Type":"application/json"}) +jwt = json.loads(urllib.request.urlopen(req).read())["jwt"] +h = {"Authorization": "Bearer " + jwt} +# ID dynamique — change après recreate +req = urllib.request.Request( + "http://172.17.0.1:9000/api/endpoints/2/docker/containers/json", headers=h) +cs = json.loads(urllib.request.urlopen(req).read()) +cid = [c["Id"][:12] for c in cs if "bifrost" in str(c.get("Names","")).lower() + and "proxy" not in str(c.get("Names","")).lower()][0] +# Stop + Start +for action in ["stop","start"]: + req = urllib.request.Request( + f"http://172.17.0.1:9000/api/endpoints/2/docker/containers/{cid}/{action}", + data=b"", headers=h) + urllib.request.urlopen(req) +``` + +**n8n API — PUT workflow** +```python +# Uniquement ces 4 champs acceptés — autres -> 400 +payload = {"name": wf["name"], "nodes": wf["nodes"], + "connections": wf["connections"], "settings": wf.get("settings", {})} +# Header : X-N8N-API-KEY (pas Authorization Bearer) +``` + +**Bifrost** +``` +Auth : header x-bf-vk: — PAS Authorization Bearer (-> 401) +URL : http://172.17.0.1:3085 +``` + +**Gitea push** +```python +import subprocess, os +env = open("/mnt/docker/hermes-platform/.env").read() +pwd = [l.split("=",1)[1].strip() for l in env.splitlines() if "PASSWORD_GITEA" in l][0] +os.chdir("/mnt/docker/nas-runbooks") +subprocess.run(["git","add","."]) +subprocess.run(["git","commit","-m","message"]) +subprocess.run(["git","push", f"http://bolbol:{pwd}@172.17.0.1:3232/bolbol/nas-runbooks.git"]) +``` + +**NyoraNotes** +```python +tok = open("/mnt/docker/nyora-notes/data/hermes-perso.token").read().strip() +note = {"title":"...","content":"...","tags":["infra","runbook","terminee"]} +body = json.dumps(note).encode() +req = urllib.request.Request("http://172.17.0.1:8787/notes", data=body, + headers={"Authorization":"Bearer "+tok,"Content-Type":"application/json"}) +urllib.request.urlopen(req) # -> 201 +``` + +**SQLite Bifrost DB** +```python +import sqlite3, shutil +# Lecture : copier d'abord (WAL actif) +shutil.copy("/mnt/docker/bifrost/data/config.db", "/tmp/bifrost.db") +conn = sqlite3.connect("/tmp/bifrost.db") +# Écriture live (restart Bifrost après pour prise en compte) +conn = sqlite3.connect("/mnt/docker/bifrost/data/config.db") +``` + +--- + +## Tableau de décision rapide + +| Besoin | Faire | +|--------|-------| +| Code Python < 200 chars | printf > /tmp + python3 (2 appels) | +| Code Python > 350 chars ou avec quotes | base64 encode -> decode -> exec | +| Heredoc | Jamais. Python open().write() en /tmp | +| Restart container | Portainer API, ID dynamique | +| sqlite3 | Python import sqlite3 | +| Session instable | list_sessions -> session fraîche | +| Gitea push | subprocess git avec http://bolbol:pwd@ | + +--- + +## Améliorations suggérées linux-mcp-nas + +1. **Monter docker.sock** : `-v /var/run/docker.sock:/var/run/docker.sock` — Docker CLI direct, supprime Portainer API workaround +2. **Installer sqlite3 CLI** : `apt install -y sqlite3` dans l'image — inspection DB sans Python +3. **Augmenter timeout run_command** : 120s trop court pour git sur gros repos +4. **Nommer sessions par rôle** : `git`, `db`, `api`, `test` — rotation propre entre tâches +5. **Installer paramiko** : `pip install paramiko` — SSH natif si besoin accès NAS direct