Files
mcp-synology-container/CLAUDE.md
T
marcus c8cda5ef2b Fix container hash-prefix + status-aware redeploy
Bug 1: Container name hash-prefix (e.g. f93cb8b504f7_jenkins)
- _strip_hash_prefix(): strips 12-char hex prefix and leading slash
- _resolve_container_name(): looks up actual DSM name from container list
- Applied in list_containers (display), container_stats (matching),
  get_container_status/get_container_logs/exec_in_container (lookup)

Bug 2: redeploy_project DSM 2101/1202 on wrong project state
- Fetch project status before acting
- RUNNING     → stop then start
- STOPPED     → start directly (nothing to stop)
- BUILD_FAILED → suppress stop error, then start
- Other       → return error with workaround hint

36 tests all passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 21:43:02 +02:00

197 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# mcp-synology-container
## Kontext
Dieses Projekt entwickelt und betreibt `mcp-synology-container` einen MCP-Server
für die vollständige Verwaltung von Docker-Projekten auf einer Synology DiskStation
via Container Manager. Der MCP-Server ist in Claude Desktop aktiv verbunden.
---
## Infrastruktur
| | |
|---|---|
| **NAS** | `https://dsm.gecheckt.de` (Split-DNS, intern direkt aufgelöst) |
| **Compose-Pfade** | `/volume1/docker/<projektname>/` |
| **Gitea** | `https://gitea.gecheckt.de/marcus/mcp-synology-container` |
| **Lokaler Code** | `D:\Dev\Projects\mcp-synology-container` |
| **Sprache** | Python 3.12+, `uv`, MCP SDK, `httpx`, `keyring`, `click`, `rich` |
---
## Deploy-Workflow (nach jeder Code-Änderung)
```
1. Claude Code committet und pusht
2. uv tool install --reinstall git+https://gitea.gecheckt.de/marcus/mcp-synology-container.git
3. Claude Desktop neu starten (Tray-Icon → Quit → neu starten)
```
---
## Aktueller Stand
### Implementierte Tools (17)
| Kategorie | Tools |
|---|---|
| Projekte | `list_projects`, `get_project_status`, `start_project`, `stop_project`, `redeploy_project` |
| Container | `list_containers`, `get_container_status`, `get_container_logs`, `exec_in_container`, `container_stats` |
| Compose | `read_compose`, `update_compose`, `update_image_tag`, `update_env_var` |
| Images | `check_image_updates`, `list_images`, `delete_image` |
| Netzwerke | `list_networks`, `create_network`, `delete_network` |
| System | `system_df`, `system_prune` |
### Bekannte Bugs
~~- Container-Name mit Hash-Präfix (`f93cb8b504f7_jenkins`) → behoben: `_strip_hash_prefix` + `_resolve_container_name` in allen Container-Tools~~
~~- `redeploy_project` DSM 2101/1202 bei falschem Status → behoben: status-aware Logik (RUNNING/STOPPED/BUILD_FAILED)~~
---
## Roadmap (geplante Erweiterungen)
~~Alle geplanten Erweiterungen implementiert.~~
Volumes entfällt — SYNO.Docker.Volume existiert nicht (kein DSM-Endpunkt).
---
## NAS Laufende Container (Stand April 2026)
| Container | Image | Status |
|---|---|---|
| `jenkins` | `jenkins/jenkins:2.558-jdk21` | running |
| `openwebui` | `ghcr.io/open-webui/open-webui:v0.8.10` | running |
| `frostiq-gitea` | `gitea/gitea:1.25.4` | running |
| `frostiq-wildfly` | `frostiq/wildfly:39.0.0.Final-jdk21-pg` | running |
| `bookstack` | `linuxserver/bookstack:latest` | running |
| `bookstack-mariadb` | `mariadb:11.8` | running |
| `homeassistant` | `homeassistant/home-assistant:stable` | running |
| `pgadmin` | `dpage/pgadmin4:latest` | running |
| `postgres17` | `postgres:17.7` | running |
| `synology_docviewer_1` | `synology/docviewer:1.3.0.0126` | stopped |
| `synology_docviewer_2` | `synology/docviewer:1.3.0.0126` | stopped |
---
## Claude Code Implementierungsregeln
- Confirmation vor destruktiven Operationen: `stop_project`, `redeploy_project`,
`exec_in_container`, `update_image_tag`, `update_env_var`, `update_compose`
- Nach Compose-Änderungen: `redeploy_project` vorschlagen
- Fehlerbehandlung: DSM-Fehler als verständliche Meldung zurückgeben, keine Stacktraces
- Keine Secrets in stderr-Ausgaben
- Type Hints und Docstrings konsequent verwenden
- Formatter: `ruff format`, Linter: `ruff check`, Tests: `pytest`
- Alle Texte (Docstrings, Kommentare, README): Englisch
---
## Hintergrund
Marcus ist Senior Software Engineer (Java, Jakarta EE).
FrostIQ ist eines seiner größeren Projekte (Jenkins, WildFly, Gitea).
Präferenz: State-of-the-Art, Best Practices, saubere Architektur.
Automatisierung spart Zeit für die Familie. 🌱
---
## Aktueller Auftrag (Stand April 2026)
### Rollenteilung
- **Claude Code** Implementierung + Unit Tests (pytest, gemockter DSM-Client)
- **Marcus** Deploy-Workflow (commit → push → install → Desktop-Neustart)
- **Claude Chat** Produkttest live gegen die NAS nach jedem Deploy
### Implementierungsreihenfolge
Implementiere **eine Gruppe nach der anderen**. Commit + Push nach jeder Gruppe, dann Marcus Bescheid geben.
---
#### Gruppe 1 Images `modules/images.py` ✦ Prio: hoch
**`list_images`**
- DSM API: `SYNO.Docker.Image`, method `list`
- Ausgabe: Name:Tag, Größe (human-readable), Erstellungsdatum, ob aktuell von einem Container verwendet
- Sortierung: Größe absteigend
- Confirmation: nein
**`delete_image`**
- Signatur: `delete_image(image_id: str, confirmed: bool = False) -> str`
- `image_id` akzeptiert `name:tag` und Image-Hash
- DSM API: `SYNO.Docker.Image`, method `delete`
- Ohne `confirmed=True`: nur Preview, nicht löschen
- Fehler wenn Image von laufendem Container verwendet → klare Meldung, kein Stacktrace
- Erfolg: `Deleted nouchka/sqlite3:latest 76 MiB freed`
- Confirmation: **ja**
---
#### Gruppe 2 Container `modules/containers.py` ✦ Prio: mittel ✅ erledigt
**`container_stats`** ✅
- Signatur: `container_stats(container_name: str) -> str`
- Ausgabe: CPU %, RAM verwendet/limit, Netzwerk I/O, Block I/O
- Confirmation: nein
**`rename_container`** entfällt (DSM bietet kein Container-Umbenennen)
---
#### Gruppe 3 System `modules/system.py` (neu) ✦ Prio: hoch
**`system_df`**
- DSM API: `SYNO.Docker.System` oder Docker Engine `/system/df`
- Ausgabe: Images (Anzahl, Gesamtgröße, reclaimable), Container, Volumes tabellarisch
- Confirmation: nein
**`system_prune`**
- Signatur: `system_prune(confirmed: bool = False) -> str`
- Löscht: dangling Images, gestoppte Container, ungenutzte Netzwerke
- Ohne `confirmed=True`: zeigt was gelöscht würde
- Ausgabe: freigegebener Speicher
- Confirmation: **ja**
---
#### Gruppe 4 Netzwerke `modules/networks.py` (neu) ✦ Prio: mittel
**`list_networks`**
- DSM API: `SYNO.Docker.Network`, method `list`
- Ausgabe: Name, Driver, Subnet, angebundene Container
- Confirmation: nein
**`create_network`**
- Signatur: `create_network(name: str, driver: str = "bridge", confirmed: bool = False) -> str`
- Confirmation: **ja**
**`delete_network`**
- Signatur: `delete_network(name: str, confirmed: bool = False) -> str`
- Fehler wenn Container angebunden → klare Meldung
- Confirmation: **ja**
---
#### Gruppe 6 Images Ergänzung entfällt
`pull_image` entfällt — SYNO.Docker.Image/pull liefert keinen nutzbaren Endpunkt
(DSM-Methode unbekannt / nicht über WebAPI erreichbar).
---
#### Gruppe 7 Registries entfällt
`list_registries` entfällt — SYNO.Docker.Registry/get funktioniert nicht wie erwartet
(DSM-Methode unbekannt / Produkttest fehlgeschlagen).
---
### Referenz für DSM API-Calls
- `cmeans/mcp-synology` (GitHub) Auth, Keyring, CLI-Struktur
- `N4S4/synology-api` `docker_api.py` (GitHub) SYNO.Docker.* API-Calls
### Start
Beginne mit **Gruppe 1** (`list_images` + `delete_image`).