Files
MUA-Mikes-Unraid-Agent/README.md
T

383 lines
14 KiB
Markdown

# MUA — Mikes Unraid Agent
MCP-Server für Unraid: Docker-Container, Netzwerk und System-Tools.
Kompiliertes Bun-Binary (TypeScript), läuft als SysVinit-Service auf Unraid.
- **MCP-Endpunkt:** `http://UNRAID-IP:3002/mcp` (Streamable HTTP, POST-only, JSON-RPC 2.0)
- **Health-Check:** `http://UNRAID-IP:3002/health` (offen, ohne Auth)
- **Config-Server:** `http://127.0.0.1:3013/config` (localhost + separater Admin-Token)
- **Auth:** API-Key (Bearer-Token) für `/mcp`
- **Tools:** 35 (Docker, Community Applications, Netzwerk, Unraid-System und optionale Toolbox)
---
## Für Hermes und andere KI-Agenten
Dieses Repository enthält bereits den fertigen MUA-MCP. Ein Agent soll keinen
zweiten Unraid-MCP entwickeln, sondern zuerst [`AGENTS.md`](AGENTS.md) und die
kompakte [`Hermes-Anleitung`](docs/HERMES.md) lesen. Darin stehen Erkennung,
direkte Registrierung, Secret-Behandlung, Tests und Rückbau vollständig.
Kurzform für Hermes:
```bash
hermes mcp add unraid \
--url http://UNRAID-IP:3002/mcp \
--auth header \
--connect-timeout 30
hermes mcp test unraid
```
Hermes fragt den MUA-API-Key verdeckt ab und speichert ihn außerhalb des
Repositories. Läuft `/health` bereits erfolgreich, ist kein Build und keine
erneute MUA-Installation nötig.
---
## Architektur
```
┌─────────────────────────────────────────────────────────┐
│ Unraid (UNRAID-IP) │
│ │
│ /usr/local/bin/mua (kompiliertes Bun-Binary) │
│ ├── MCP-Server 0.0.0.0:3002 (Bearer-Auth) │
│ └── Config-Server 127.0.0.1:3013 (nur localhost) │
│ │
│ /etc/rc.d/rc.mua (SysVinit-Service) │
│ /boot/config/plugins/mua/mua.conf (API-Key + Tools) │
│ /usr/local/emhttp/plugins/mua/mua.page (WebGUI-Tab) │
└─────────────────────────────────────────────────────────┘
▲ ▲
│ HTTP + Bearer-Token │ HTTP (localhost)
│ │
┌────────┴─────────┐ ┌────────┴─────────┐
│ MCP-Client │ │ Unraid WebGUI │
│ (Hermes, etc.) │ │ User Utilities → MUA │
└──────────────────┘ └──────────────────┘
```
**Wichtig:** Der MCP-Endpunkt (Port 3002) ist **extern** erreichbar und braucht
einen Bearer-Token. Der Config-Server (Port 3013) läuft **nur auf localhost**
und verlangt zusätzlich einen bei jedem Start neu erzeugten Admin-Token.
Die WebGUI schützt schreibende Formulare außerdem mit einem CSRF-Token.
## Sicherheitsmodell
- Neuinstallationen starten im Profil **Nur Lesen**. Container-Steuerung,
aktive Netzwerktests, Container-Umbauten und die Root-Shell sind aus.
- Für Diagnosen steht `unraid_system_shell_readonly` bereit. Es startet nur
fest freigegebene Leseprogramme als direkte Argumentliste, niemals über
`/bin/sh`; Verkettungen, Pipes, Umleitungen und schreibende Optionen werden
dadurch verhindert. Die freie Root-Shell bleibt separat und kritisch.
- `none` bedeutet tatsächlich **keine Tools aktiv**; `all` ist ein expliziter
Vollzugriff und wird in der GUI deutlich gewarnt.
- Container-Umgebungswerte werden nie ausgegeben, nur ihre Variablennamen.
- Häufige Secret-Formate in Logs werden zusätzlich redigiert. Logs können
trotzdem Nutzdaten enthalten und sollten gezielt abgefragt werden.
- Der API-Key erscheint nur unmittelbar nach seiner Erzeugung vollständig.
- Tool-Aufrufe werden ohne Argumente oder Ausgaben in
`/var/log/plugins/mua-audit.log` protokolliert.
- Community-Applications-Installationen laufen immer über Suche, Vorschau
und ein exakt an diese Änderung gebundenes, zehn Minuten gültiges
Freigabeticket. Das Ergebnis bleibt über ein reguläres Unraid-
Benutzertemplate vollständig in der Docker-GUI verwaltbar.
- CORS ist standardmäßig aus. Requests sind auf 1 MiB, 120 pro Minute je
Client und vier gleichzeitig laufende Werkzeuge begrenzt. Diese Grenzen
lassen sich per Umgebungsvariable anpassen.
---
## Installation
### 1. Plugin installieren
Über die Unraid-WebGUI: **Settings → Plugins → Community Plugins** →
`MUA (Mikes Unraid Agent)` suchen und installieren.
Oder manuell:
```bash
# .txz von Gitea laden
curl -O https://git.casaderoll.de/michael/MUA-Mikes-Unraid-Agent/raw/branch/main/dist/mua-2026.09.21.r029-x86_64-1.txz
# Installieren
upgradepkg --install-new mua-2026.09.21.r029-x86_64-1.txz
```
### 2. Service starten
```bash
/etc/rc.d/rc.mua start
```
Der Service startet automatisch bei jedem Boot (SysVinit).
### 3. Health-Check
```bash
curl http://UNRAID-IP:3002/health
# → {"status":"ok","version":"2026.08.24.r022","auth":"required"}
```
---
## API-Key generieren (WebGUI)
1. **Settings → User Utilities → MUA** in der Unraid-WebGUI öffnen
2. Unter **„API-Key (Authentifizierung)"** auf **„Neuen API-Key generieren"** klicken
3. Den einmalig angezeigten Key kopieren (64-stellige Hex-Zeichenfolge)
Der Key wird in `/boot/config/plugins/mua/mua.conf` gespeichert (chmod 600).
> **Hinweis:** Der Key wird nur einmal angezeigt. Wenn du ihn verlierst,
> generiere einen neuen — der alte funktioniert dann nicht mehr.
---
## MCP-Client konfigurieren
MUA ist ein **Standard-MCP-Server** (Streamable HTTP, POST-only, JSON-RPC 2.0).
Jeder MCP-Client kann es nutzen — URL + Bearer-Token reichen:
```
URL: http://UNRAID-IP:3002/mcp
Auth: Authorization: Bearer <dein-api-key>
```
MUA läuft direkt auf Unraid und spricht **Streamable HTTP**. Ein SSH-Wrapper
ist weder erforderlich noch empfohlen. Dadurch bleiben Verbindungen kompakt
und routinemäßige Tool-Aufrufe erzeugen keine SSH-Anmeldungen im Unraid-Syslog.
### Beispiel: Hermes Agent
Hermes bindet MUA direkt per HTTP an. Wenn MCPHub eingesetzt wird, zeigt Hermes
stattdessen auf die vom Hub veröffentlichte Route; MCPHub verbindet sich dann
direkt mit diesem MUA-Endpunkt.
```yaml
# Direkte Anbindung ohne MCPHub
mcp_servers:
unraid:
url: http://UNRAID-IP:3002/mcp
headers:
Authorization: Bearer <dein-api-key>
timeout: 1800
connect_timeout: 30
enabled: true
```
Bei einer zentralen MCPHub-Installation wird dieselbe URL einmalig im Hub
registriert. Alle Clients verwenden anschließend die Hub-Route und benötigen
keine eigene SSH- oder MUA-Konfiguration.
### Beispiel: Direkter HTTP-Test (curl)
```bash
# Health-Check (ohne Auth)
curl http://UNRAID-IP:3002/health
# MCP-Initialisierung (mit Auth)
curl -X POST http://UNRAID-IP:3002/mcp \
-H "Authorization: Bearer <dein-api-key>" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'
# Tool-Liste
curl -X POST http://UNRAID-IP:3002/mcp \
-H "Authorization: Bearer <dein-api-key>" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
# Tool aufrufen
curl -X POST http://UNRAID-IP:3002/mcp \
-H "Authorization: Bearer <dein-api-key>" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"unraid_docker_list","arguments":{}}}'
```
### Ohne API-Key → 401
```bash
curl -X POST http://UNRAID-IP:3002/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}'
# → 401 Unauthorized
```
---
## Tools aktivieren/deaktivieren (WebGUI)
In **Settings → User Utilities → MUA** stehen vier Sicherheitsprofile bereit:
- **Nur Lesen (empfohlen):** Status, Diagnose, Logs und Inventar
- **Betrieb + Diagnose:** zusätzlich aktive Prüfungen und Start/Stop/Restart
- **Alles sperren:** MCP bleibt erreichbar, bietet aber keine Werkzeuge an
- **Vollzugriff:** einschließlich Container-Umbau und uneingeschränkter Root-Shell
Zusätzlich kann jedes Werkzeug einzeln nach Risikostufe freigegeben werden:
| Gruppe | Tools |
|--------|-------|
| **Docker (16)** | `unraid_docker_list`, `unraid_docker_inspect`, `unraid_docker_logs`, `unraid_docker_analyze_logs`, `unraid_docker_processes`, `unraid_docker_stats`, `unraid_docker_info`, `unraid_docker_update_status`, `unraid_docker_start`, `unraid_docker_stop`, `unraid_docker_restart`, `unraid_docker_create`, `unraid_docker_modify`, `unraid_docker_update`, `unraid_docker_update_verified_batch`, `unraid_docker_rebuild` |
| **Community Applications (3)** | `unraid_ca_search`, `unraid_ca_install_preview`, `unraid_ca_install` |
| **Netzwerk (6)** | `unraid_network_inventory`, `unraid_network_list`, `unraid_network_inspect`, `unraid_network_host_state`, `unraid_network_audit_tcp`, `unraid_network_lan_probe` |
| **System (13)** | `unraid_system_health`, `unraid_storage_status`, `unraid_disk_health`, `unraid_notifications_list`, `unraid_shares_list`, `unraid_share_inspect`, `unraid_files_inventory`, `unraid_system_connection_test`, `unraid_system_shell_readonly`, `unraid_system_shell`, `unraid_system_job_start`, `unraid_system_job_status`, `unraid_system_job_cleanup` |
Deaktivierte Tools werden vom MCP-Server gefiltert — sie erscheinen nicht in
`tools/list` und können nicht aufgerufen werden (→ `ERROR: Tool disabled`).
### Optionaler Agent-Werkzeugcontainer
MUA kann einen allgemeinen Werkzeugcontainer dynamisch über das Docker-Label
`mike.ai.role=toolbox` erkennen. Sobald mindestens ein solcher Container
existiert, wird in der WebGUI der Schalter **„Werkzeugcontainer für
KI-Agenten bekanntgeben“** verfügbar. Fehlt das Label, bleibt er ausgegraut.
Bei aktiviertem Schalter stellt MUA zwei eigene, dynamisch sichtbare Werkzeuge
bereit:
- `unraid_toolbox_status` erkennt den Container und prüft angefragte Programme
wie `ffmpeg`, `ffprobe`, `yt-dlp` oder `mediainfo`.
- `unraid_toolbox_exec` führt Befehle direkt darin aus und kann auf ausdrücklichen
Auftrag fehlende Pakete dort installieren.
Der Containername ist nicht fest in MUA eingebaut. Wird der Container ersetzt,
genügt dasselbe Label am Nachfolger. Die Toolbox-Werkzeuge erscheinen in keinem
MCP-Client, solange der Schalter aus ist oder kein passender Container existiert.
Beispiel für Docker beziehungsweise DockerMan `ExtraParams`:
```text
--label=mike.ai.role=toolbox
```
Bei freigegebenem Vollzugriff ist `unraid_system_shell` das direkte Terminal
auf dem Unraid-Host. Agenten sollen es für normale Docker-, Datei- und
Administrationsaufträge unmittelbar verwenden und weder den MUA-Quellcode
untersuchen noch Befehle über ein lokales Client-Terminal weiterreichen. Das
Werkzeug akzeptiert mehrzeilige Skripte bis 256 KiB und Laufzeiten bis 30
Minuten. Nur noch längere Arbeiten gehören in `unraid_system_job_start`.
---
## Service-Verwaltung
| Aktion | Befehl |
|--------|--------|
| Starten | `/etc/rc.d/rc.mua start` |
| Stoppen | `/etc/rc.d/rc.mua stop` |
| Neu starten | `/etc/rc.d/rc.mua restart` |
| Status | `/etc/rc.d/rc.mua status` |
| Logs | `tail -f /var/log/plugins/mua.log` |
### Auto-Restart bei Update
Das Binary prüft alle 30 s, ob sich das Binary auf Platte geändert hat
(mtime + size). Wenn ja → `rc.mua restart` + sauber beenden. Der neue
Prozess startet automatisch. Zusätzlich führt der POST-INSTALL-Hook im
`.plg` nach jedem Update `rc.mua restart` aus.
---
## Konfiguration
| Datei | Zweck |
|-------|-------|
| `/boot/config/plugins/mua/mua.conf` | API-Key + Tool-Filter (chmod 600) |
| `/usr/local/emhttp/plugins/mua/mua.page` | WebGUI-Tab (PHP) |
| `/etc/rc.d/rc.mua` | SysVinit-Service-Script |
| `/usr/local/bin/mua` | Kompiliertes Bun-Binary |
### Config-Datei-Format (`mua.conf`)
```ini
MUA_API_KEY=a1b2c3d4...
MUA_ENABLED_TOOLS=unraid_docker_list,unraid_docker_inspect,unraid_network_list
MUA_TOOLBOX_ENABLED=false
```
Sonderwerte: `all` aktiviert ausdrücklich alles, `none` deaktiviert alles.
---
## Entwicklung
### Voraussetzungen
- [Bun](https://bun.sh) (Runtime + Build-Tool)
- `xz` (für `.txz`-Packing)
- `xmllint` (für `.plg`-Validierung)
### Build
```bash
# TypeScript-Check
bunx tsc --noEmit
# Linux-Binary (für Unraid)
bun build src/index.ts --compile --target=bun-linux-x64 --outfile dist/mua
# macOS-Binary (für lokalen Test)
bun build src/index.ts --compile --target=bun-darwin-arm64 --outfile dist/mua-mac
# .txz packen (Version wird aus package.json gelesen)
bash scripts/package.sh
```
### Lokaler Test
Die PHP-Statushelper lassen sich unabhängig vom laufenden MUA-Dienst testen
(PHP 8 erforderlich):
```bash
php scripts/test-notifications.php
php scripts/test-docker-update-status.php
```
Der Benachrichtigungsparser liest auch mehrzeilige URBM-Meldungen und erhält
Text wörtlich, ohne INI-Variablen wie `${HOME}` auszuwerten.
```bash
# Test-Setup (ohne Unraid)
MUA_CONFIG_DIR=/tmp/mua-test MUA_PORT=3999 MUA_CONFIG_PORT=3998 ./dist/mua-mac
# Health-Check
curl http://127.0.0.1:3999/health
# Config-Server
curl http://127.0.0.1:3998/config
```
### Versionierung
Format: `YYYY.MM.DD.rNNN` (Release-Counter hinter Datum).
Version in **allen** Dateien aktualisieren:
- `package.json`
- `src/helpers.ts` (`MUA_VERSION`)
- `plugin/plugin.json`
- `plugin/mua.plg` (Version + txzURL + CHANGES)
- `mcp/server.php`
- `mcp/helpers.php`
Nach Build: **SHA256** im `.plg` aktualisieren:
```bash
shasum -a 256 dist/mua-*.txz
```
### Release-Prozess
1. Version erhöhen (alle Dateien)
2. `bunx tsc --noEmit`
3. `bun build src/index.ts --compile --target=bun-linux-x64 --outfile dist/mua`
4. `bash scripts/package.sh`
5. SHA256 im `.plg` aktualisieren
6. `xmllint --noout plugin/mua.plg`
7. `git add -A && git commit -m "rNNN: ..." && git push`
8. Auf Unraid: **Plugins → MUA → Update**