recovery: recuperation commit local orphelin (master jamais pousse) - MCP-NAS-SCRIPTING.md
This commit is contained in:
parent
afc8fb8370
commit
cb6565453e
|
|
@ -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 '<B64>' | 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":"<depuis .env>"}).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: <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
|
||||
Loading…
Reference in New Issue