Zum Inhalt

MCP-Server — Neuinstallation von Grund auf

Diese Seite dokumentiert den vollständigen Aufbau des MCP-Servers von Null — für eine Neuinstallation oder als Vorlage für ein anderes Heimnetz.

Architektur und laufendes System: mcp-server.md
Arbeitsregeln für KIs: mcp-arbeitsregeln.md


Überblick

Was am Ende läuft:

Cloudflare Access (JWT-Auth)
mcp.diebrocks.com  (Cloudflare Tunnel, CT 102)
    ├── /mcp  →  :3000  MCP-Server (Node.js, User mcp)
    └── /admin → :3001  Admin-UI   (Node.js, root)
         (beide in CT 108, 10.10.10.33)
         SSH als mcp-agent → Proxmox, ebusd-*, cloudflared, mcp-server
         SSH als ssh       → Home Assistant
         SSH als mcp       → MikroTik

wiki.diebrocks.com  (Cloudflare Pages)
    └── GitHub Repo (HerrBausW/smarthome-wiki) → Cloudflare Pages Build
         Build-Befehl: pip install -r requirements.txt && mkdocs build
         Output: site/
         Branch: master — automatische Bereitstellung aktiv
         Cloudflare Pages baut automatisch bei jedem git push

Anpassen für ein anderes Heimnetz: IPs, Domain, Cloudflare-Team-Domain, AUD-Tag, Admin-E-Mail und die HOSTS-Map in index.js müssen auf die eigene Umgebung angepasst werden. Der restliche Code und alle Systemd-Units sind generisch.


Voraussetzungen

  • Proxmox-Host läuft, Netz ist konfiguriert
  • Cloudflare-Account mit eigener Domain und Zero Trust (kostenlos)
  • Cloudflare Tunnel (CT 102) läuft — siehe cloudflare-tunnel.md
  • GitHub-Repo für das Wiki (oder Wiki-Teil weglassen)

1. LXC-Container anlegen (Proxmox)

Im Proxmox-UI oder per CLI:

# Debian 13 Template vorher herunterladen:
# Datacenter → pve → local → CT Templates → debian-13-standard herunterladen

pct create 108 local:vztmpl/debian-13-standard_13.x_amd64.tar.zst \
  --hostname mcp-server \
  --unprivileged 1 \
  --features nesting=1 \
  --memory 512 \
  --swap 512 \
  --cores 1 \
  --rootfs local-lvm:8 \
  --net0 name=eth0,bridge=vmbr0,ip=10.10.10.33/24,gw=10.10.10.1 \
  --nameserver 10.10.10.1 \
  --start 1

Start-Reihenfolge setzen (Proxmox UI → CT 108 → Optionen → Start/Shutdown): - Order: 2 (nach cloudflared) - Up: 10 s - Down: 30 s


2. Grundkonfiguration (im Container)

pct enter 108
apt-get update && apt-get upgrade -y
apt-get install -y ssh curl git openssh-server
systemctl enable --now ssh
passwd

3. User mcp anlegen

Der MCP-Server läuft als unprivilegierter User mcp (uid 999).

useradd -m -u 999 -s /bin/bash mcp
passwd -l mcp   # Kein Passwort-Login, nur SSH

mkdir -p /home/mcp/.ssh
chmod 700 /home/mcp/.ssh
chown mcp:mcp /home/mcp/.ssh

4. SSH-Keys generieren

Alle Keys werden als User mcp erzeugt:

su - mcp

ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -C "mcp-remote" -N ""
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_wiki -C "mcp-wiki" -N ""

cat > ~/.ssh/config << 'EOF'
Host github-wiki
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_ed25519_wiki
    StrictHostKeyChecking no
EOF
chmod 600 ~/.ssh/config

echo "=== mcp-remote ===" && cat ~/.ssh/id_ed25519.pub
echo "=== wiki ===" && cat ~/.ssh/id_ed25519_wiki.pub
exit

5. Ziel-Hosts vorbereiten

5a. Proxmox-Host

useradd -m -s /bin/bash mcp-agent
mkdir -p /home/mcp-agent/.ssh && chmod 700 /home/mcp-agent/.ssh

echo 'no-port-forwarding,no-agent-forwarding,no-X11-forwarding ssh-ed25519 AAAA...KEY... mcp-remote' \
  >> /home/mcp-agent/.ssh/authorized_keys
chmod 600 /home/mcp-agent/.ssh/authorized_keys
chown -R mcp-agent:mcp-agent /home/mcp-agent/.ssh

echo "mcp-agent ALL=(root) NOPASSWD: /usr/local/bin/mcp-pct, /usr/local/bin/mcp-maintenance" \
  > /etc/sudoers.d/mcp-agent
chmod 440 /etc/sudoers.d/mcp-agent

5b. Arbeits-Container (cloudflared/102, ebusd-heizung/106, ebusd-lueftung/107)

useradd -m -s /bin/bash mcp-agent
mkdir -p /home/mcp-agent/.ssh && chmod 700 /home/mcp-agent/.ssh

echo 'no-port-forwarding,no-agent-forwarding,no-X11-forwarding ssh-ed25519 AAAA...KEY... mcp-remote' \
  >> /home/mcp-agent/.ssh/authorized_keys
chmod 600 /home/mcp-agent/.ssh/authorized_keys
chown -R mcp-agent:mcp-agent /home/mcp-agent/.ssh

echo "mcp-agent ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/mcp-agent
chmod 440 /etc/sudoers.d/mcp-agent

5c. MCP-Server selbst (CT 108)

# KEIN sudoers-Eintrag — Gate-Schutz
echo 'no-port-forwarding,no-agent-forwarding,no-X11-forwarding ssh-ed25519 AAAA...KEY... mcp-remote' \
  >> /home/mcp-agent/.ssh/authorized_keys

5d. Home Assistant

  1. Einstellungen → Add-ons → Advanced SSH & Web Terminal installieren
  2. Public Key aus Schritt 4 im Addon eintragen

5e. MikroTik

/user add name=mcp group=read password=""
/user ssh-keys import user=mcp public-key-file=mcp-remote.pub

6. sudo-Wrapper installieren

6a. mcp-pct auf dem Proxmox-Host

cat > /usr/local/bin/mcp-pct << 'SCRIPT'
#!/bin/bash
set -euo pipefail
cmd="${1:-}"; shift || true
case "$cmd" in
  list) exec /usr/sbin/pct list ;;
  status|start|stop|restart)
    id="${1:?Container-ID fehlt}"
    [[ "$id" =~ ^[0-9]+$ ]] || { echo "Ungueltige ID" >&2; exit 1; }
    exec /usr/sbin/pct "$cmd" "$id" ;;
  exec)
    id="${1:?Container-ID fehlt}"; shift
    [[ "$id" =~ ^[0-9]+$ ]] || { echo "Ungueltige ID" >&2; exit 1; }
    sub="${1:-}"
    case "$sub" in
      systemctl)
        action="${2:-}"; svc="${3:-}"
        [[ "$action" =~ ^(status|start|stop|restart)$ ]] || { echo "Aktion verboten" >&2; exit 1; }
        [[ "$svc" =~ ^[A-Za-z0-9@._-]+$ ]] || { echo "Ungueltiger Service" >&2; exit 1; }
        exec /usr/sbin/pct exec "$id" -- systemctl "$action" "$svc" --no-pager ;;
      journalctl-unit)
        svc="${2:?Service fehlt}"; n="${3:-50}"
        [[ "$svc" =~ ^[A-Za-z0-9@._-]+$ && "$n" =~ ^[0-9]+$ ]] || { echo "Ungueltige Argumente" >&2; exit 1; }
        exec /usr/sbin/pct exec "$id" -- journalctl -u "$svc" -n "$n" --no-pager ;;
      journalctl-clear)
        exec /usr/sbin/pct exec "$id" -- bash -c "journalctl --rotate && journalctl --vacuum-time=1s && echo OK" ;;
      *) echo "Subkommando verboten: $sub" >&2; exit 1 ;;
    esac ;;
  *) echo "Nutzung: mcp-pct list|status|start|stop|restart|exec ..." >&2; exit 1 ;;
esac
SCRIPT

chmod 755 /usr/local/bin/mcp-pct && chown root:root /usr/local/bin/mcp-pct

6b. mcp-maintenance auf allen Debian-Hosts

cat > /usr/local/bin/mcp-maintenance << 'SCRIPT'
#!/bin/bash
set -euo pipefail
export DEBIAN_FRONTEND=noninteractive
case "${1:-}" in
  check)   apt-get update -qq 2>/dev/null || true; apt list --upgradable 2>/dev/null ;;
  upgrade) apt-get upgrade -y 2>&1 ;;
  cleanup) apt-get autoremove -y 2>&1; apt-get autoclean 2>&1; echo '=== Fertig ===' ;;
  *) echo "Nutzung: mcp-maintenance check|upgrade|cleanup" >&2; exit 1 ;;
esac
SCRIPT

chmod 755 /usr/local/bin/mcp-maintenance && chown root:root /usr/local/bin/mcp-maintenance

7. Node.js installieren (CT 108)

pct enter 108
curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
apt-get install -y nodejs
node --version   # sollte v22.x ausgeben

8. MCP-Server einrichten (CT 108)

Verzeichnisse anlegen

mkdir -p /opt/mcp-shell && chown root:root /opt/mcp-shell && chmod 755 /opt/mcp-shell
touch /var/log/mcp-shell-audit.jsonl
chown mcp:mcp /var/log/mcp-shell-audit.jsonl && chmod 640 /var/log/mcp-shell-audit.jsonl

package.json

cat > /opt/mcp-shell/package.json << 'EOF'
{
  "name": "mcp-shell",
  "version": "1.0.0",
  "type": "module",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.29.0",
    "express": "^5.2.1",
    "jsonwebtoken": "^9.0.3",
    "jwks-rsa": "^4.1.0",
    "zod": "^4.4.3"
  }
}
EOF
cd /opt/mcp-shell && npm install
chown -R root:root /opt/mcp-shell/node_modules

index.js

Anpassen: TEAM_DOMAIN, AUD, IDENTITIES, HOSTS, PROXMOX_SSH.

# index.js nach /opt/mcp-shell/index.js schreiben (siehe produktiver Stand)
chown root:root /opt/mcp-shell/index.js && chmod 644 /opt/mcp-shell/index.js

tool-config.json

cat > /opt/mcp-shell/tool-config.json << 'EOF'
{
  "disabled_tools": [],
  "write_allow": {
    "ebusd-heizung": ["/etc/ebusd/", "/tmp/"],
    "ebusd-lueftung": ["/etc/ebusd/", "/tmp/"],
    "home-assistant": ["/config/", "/tmp/"],
    "mcp-server": ["/tmp/"],
    "proxmox": [],
    "cloudflared": []
  }
}
EOF
chown root:root /opt/mcp-shell/tool-config.json && chmod 644 /opt/mcp-shell/tool-config.json

9. Admin-UI einrichten (CT 108)

mkdir -p /opt/mcp-admin
ln -s /opt/mcp-shell/node_modules /opt/mcp-admin/node_modules
# server.js nach /opt/mcp-admin/server.js schreiben (TEAM_DOMAIN, AUD, ADMIN_EMAILS anpassen)
chown root:root /opt/mcp-admin/server.js && chmod 644 /opt/mcp-admin/server.js

10. Systemd-Services einrichten (CT 108)

cat > /etc/systemd/system/mcp-shell.service << 'EOF'
[Unit]
Description=MCP Shell Server (laeuft als unprivilegierter User mcp)
After=network.target

[Service]
Type=simple
User=mcp
Group=mcp
WorkingDirectory=/opt/mcp-shell
ExecStart=/usr/bin/node index.js
Restart=always
RestartSec=3
NoNewPrivileges=yes

[Install]
WantedBy=multi-user.target
EOF

cat > /etc/systemd/system/mcp-admin.service << 'EOF'
[Unit]
Description=MCP Admin UI (root, Port 3001)
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=/opt/mcp-admin
ExecStart=/usr/bin/node server.js
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now mcp-shell mcp-admin

11. Wiki-Repo klonen & Cloudflare Pages einrichten

Das Wiki wird als statische Site über Cloudflare Pages ausgeliefert. Der MCP-Server pusht Änderungen per git push ins GitHub-Repo — Cloudflare Pages baut dann automatisch mit mkdocs build.

11a. GitHub-Repo anlegen

  1. Neues GitHub-Repo anlegen (z.B. smarthome-wiki)
  2. mkdocs.yml, requirements.txt und docs/-Verzeichnis committen
  3. Public Key ~/.ssh/id_ed25519_wiki.pub als Deploy Key mit Write-Zugriff eintragen:
  4. GitHub Repo → Settings → Deploy Keys → Add deploy key → Allow write access

requirements.txt Mindestinhalt:

mkdocs-material

11b. Wiki-Repo auf CT 108 klonen

su - mcp
git clone github-wiki:GITHUB_USER/smarthome-wiki /opt/wiki
ls /opt/wiki/docs/
exit

11c. Cloudflare Pages konfigurieren

  1. Cloudflare Dashboard → Workers & Pages → Create → Pages → Connect to Git
  2. GitHub-Repo auswählen
  3. Build-Einstellungen:
Einstellung Wert
Build-Befehl pip install -r requirements.txt && mkdocs build
Build-Ausgabe site
Stammverzeichnis (leer)
Build-Systemversion 3
Produktions-Branch master
Automatische Bereitstellungen Aktiviert
  1. Save and Deploy — erster Build startet automatisch

11d. Eigene Domain verknüpfen

Pages-Projekt → Benutzerdefinierte Domänen → Domäne hinzufügen → wiki.DEINE-DOMAIN.com

Cloudflare fügt den CNAME automatisch ein (da Domain bereits bei Cloudflare liegt).

11e. Automatischer Build bei Wiki-Änderungen

Ab jetzt reicht ein git push vom MCP-Server — wird automatisch durch wiki_write ausgeführt. Cloudflare Pages erkennt den Push und baut die Site neu (~30 Sekunden). Kein Webhook oder Cronjob nötig.


12. Cloudflare Zero Trust konfigurieren

12a. Access Application anlegen

  1. Zero Trust → Access → Applications → Self-hosted
  2. Domain: mcp.DEINE-DOMAIN.com
  3. AUD-Tag nach dem Anlegen notieren → in index.js und server.js eintragen

12b. Access Policy

  • Action: Allow
  • Include: Emails → eigene E-Mail

12c. Service Token für KI-Connector

  1. Zero Trust → Access → Service Auth → Service Tokens → Create
  2. Name: z.B. claude-mcp
  3. Client ID und Secret sofort notieren — Secret wird nur einmal angezeigt

13. Cloudflare Tunnel Ingress ergänzen (CT 102)

ingress:
  - hostname: mcp.DEINE-DOMAIN.com
    path: "^/admin"
    service: http://10.10.10.33:3001

  - hostname: mcp.DEINE-DOMAIN.com
    service: http://10.10.10.33:3000

  - service: http_status:404
cloudflared tunnel route dns <tunnel-name> mcp.DEINE-DOMAIN.com
systemctl restart cloudflared

14. KI-Client konfigurieren

{
  "mcpServers": {
    "heimnetz-mcp": {
      "type": "http",
      "url": "https://mcp.DEINE-DOMAIN.com/mcp",
      "headers": {
        "CF-Access-Client-Id": "<client-id>",
        "CF-Access-Client-Secret": "<client-secret>"
      }
    }
  }
}

15. Funktionstest

curl -X POST https://mcp.DEINE-DOMAIN.com/mcp \
  -H "Content-Type: application/json" \
  -H "CF-Access-Client-Id: <id>" \
  -H "CF-Access-Client-Secret: <secret>" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

Checkliste Neuinstallation

  • [ ] CT 108 angelegt, Debian installiert, SSH aktiv
  • [ ] User mcp angelegt (uid 999, kein sudo)
  • [ ] SSH-Keys generiert (id_ed25519, id_ed25519_wiki)
  • [ ] mcp-agent auf Proxmox, CT 102, 106, 107, 108 eingerichtet (authorized_keys + sudoers)
  • [ ] SSH-User auf HA und MikroTik eingerichtet
  • [ ] mcp-pct auf Proxmox installiert
  • [ ] mcp-maintenance auf allen Debian-Hosts installiert
  • [ ] Node.js installiert
  • [ ] /opt/mcp-shell/ mit index.js, package.json, tool-config.json (root-owned)
  • [ ] /opt/mcp-admin/ mit server.js (root-owned), Symlink auf node_modules
  • [ ] Audit-Log angelegt (/var/log/mcp-shell-audit.jsonl, Owner mcp)
  • [ ] Systemd-Services mcp-shell und mcp-admin aktiviert und laufend
  • [ ] GitHub-Repo angelegt, requirements.txt und mkdocs.yml committet
  • [ ] Deploy Key (id_ed25519_wiki) in GitHub eingetragen
  • [ ] Wiki-Repo in /opt/wiki geklont (Owner mcp)
  • [ ] Cloudflare Pages konfiguriert (Build: pip install -r requirements.txt && mkdocs build, Output: site, System v3)
  • [ ] Eigene Domain in Cloudflare Pages verknüpft
  • [ ] Cloudflare Application angelegt (AUD notiert, in Code eingetragen)
  • [ ] Service Token angelegt (Secret notiert)
  • [ ] Cloudflare Tunnel Ingress für mcp.DEINE-DOMAIN.com ergänzt
  • [ ] KI-Client konfiguriert
  • [ ] Funktionstest erfolgreich