Files
mcp-synology-container/CLAUDE.md
T
marcus 5fe8f5bc73 Add pull_image + list_registries; remove Gruppe 5 (no Volume API)
- pull_image: SYNO.Docker.Image/pull with repository+tag split via
  rpartition; polls image list every 3 s until image appears, 120 s timeout
- list_registries: SYNO.Docker.Registry/get; shows name, URL, active marker
- Gruppe 5 (Volumes) removed from roadmap — SYNO.Docker.Volume does not exist
- CLAUDE.md: tool count 17 → 19, Volumes section removed
- 28 tests all passing

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

205 lines
6.8 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 (19)
| 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`, `pull_image` |
| Netzwerke | `list_networks`, `create_network`, `delete_network` |
| System | `system_df`, `system_prune` |
| Registries | `list_registries` |
### Bekannte Bugs
- Container-Name wird manchmal mit Hash-Präfix zurückgegeben (z.B. `f93cb8b504f7_jenkins`)
→ Tritt auf wenn Service-Name in compose.yaml von `container_name` abweicht
- `redeploy_project` schlägt mit DSM 2101/1202 fehl bei falschem Projektstatus
→ Workaround: `stop_project` + `start_project` separat oder `docker compose` per SSH
---
## 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 `modules/images.py` ✦ Prio: niedrig
**`pull_image`**
- Signatur: `pull_image(image: str) -> str` (z.B. `"postgres:17.8"`)
- DSM API: `SYNO.Docker.Image`, method `create` oder pull-Endpunkt
- Fortschritt streamen wenn möglich, sonst polling
- Confirmation: nein
---
#### Gruppe 7 Registries `modules/registries.py` (neu) ✦ Prio: niedrig
**`list_registries`**
- DSM API: `SYNO.Docker.Registry`, method `list`
- Ausgabe: Name, URL, ob authentifiziert
- Confirmation: nein
---
### 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`).