StarCraft-Kampagnen-MCP-Server ("Missions-Baumeister") (#1)

* Projekt-Spezifikation für StarCraft-Kampagnen-MCP-Server in README festhalten

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NaZBZofC1on2gkSMb5UWZy

* StarCraft-Kampagnen-MCP-Server: vollstaendige Implementierung

FastMCP-Server (stdio + Streamable HTTP) mit 13 sc_-Tools fuer das Lesen und
Schreiben von StarCraft-Brood-War-Karten (.scm/.scx) ueber RichChk:

- sc_list_maps, sc_describe_map, sc_list_locations, sc_list_triggers (lesen)
- sc_create_location, sc_rename_location, sc_set_player_setup
- sc_add_trigger (Kern-Tool: 10 Condition- und 20 Action-Typen), sc_remove_trigger,
  sc_clear_triggers
- sc_embed_wav (Voiceover/Sound-Einbettung), sc_save_map, sc_reset_map

Tolerante Pydantic-Eingaben, hilfreiche Fehlermeldungen, readOnly/destructive-Hints.
Karten-Sitzung im Speicher pro Basis-Karte; Basis-Karte bleibt unveraendert.

Deployment: Dockerfile (python:3.12-slim), docker-compose, Caddyfile, run.sh.
Beispiel-Basis-Karte in data/maps. Selbsttest beweist die ganze Pipeline.

Verifiziert: Pipeline + alle Tools + WAV-Einbettung + Streamable-HTTP-Handshake
(echter MCP-Client), unter Python 3.11 und 3.12.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NaZBZofC1on2gkSMb5UWZy

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Kenearos 2026-06-22 11:29:34 +02:00 committed by GitHub
parent 57502546bc
commit 4075cf4ebd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 1886 additions and 1 deletions

226
README.md
View file

@ -1 +1,225 @@
# Star-Edit
# Star-Edit — StarCraft-Kampagnen-MCP-Server ("Missions-Baumeister")
Ein **MCP-Server**, mit dem Claude StarCraft-Brood-War-Karten (`.scm`/`.scx`) liest und
schreibt. Du lieferst die Kreativ-Seite (Story, Dialoge, Voiceover, Sounds, Ablauf),
Claude baut daraus echte, **spielbare Missionsdateien** — komplette Logik, Texte und
Sound-Einbettung.
> **Das Gelände wird NICHT generiert.** Jede Mission startet von einer vorhandenen
> Basis-Karte als Leinwand; der Server bearbeitet nur deren Daten-/Logik-Sektionen
> (Trigger, Locations, Player-Setup, Sounds).
Die ganze Karten-Manipulation läuft über die Python-Bibliothek
[RichChk](https://github.com/sethmachine/richchk).
---
## In 3 Schritten loslegen
### 1. Server starten (ein Befehl)
Auf dem VPS, im Projektverzeichnis:
```bash
./run.sh
```
Das baut das Docker-Image (beim ersten Mal) und startet den Container `sc-mcp`.
Er lauscht intern auf Port **8000** mit Streamable-HTTP unter dem Pfad **`/mcp`**.
Weitere Befehle:
```bash
./run.sh logs # Live-Logs ansehen
./run.sh selftest # den eingebauten Selbsttest laufen lassen
./run.sh stop # Container stoppen
```
### 2. Subdomain via Caddy (TLS)
Caddy terminiert TLS und leitet die Subdomain an den Container weiter. Trage den Block
aus der [`Caddyfile`](./Caddyfile) in deine bestehende Caddy-Konfiguration ein:
```caddy
sc-mcp.pixel-by-design.de {
reverse_proxy sc-mcp:8000
}
```
Voraussetzung: Caddy und der `sc-mcp`-Container hängen im selben Docker-Netzwerk (in
`docker-compose.yml` als externes Netzwerk `caddy` eingetragen — passe den Namen an
dein Setup an).
### 3. In Claude als Custom Connector eintragen
Die URL, die du in Claude einträgst:
```
https://sc-mcp.pixel-by-design.de/mcp
```
In Claude (Web/Desktop): **Einstellungen → Connectors → Custom Connector hinzufügen**
obige URL als Streamable-HTTP-/Remote-MCP-Endpunkt eintragen. Danach stehen die
`sc_`-Tools im Chat zur Verfügung.
---
## Wo liegen Karten, WAVs und Missionen?
Alles im Verzeichnis **`./data/maps`** (im Container als `/data/maps` gemountet):
- **Basis-Karten** (`.scx`/`.scm`) — die Leinwand für neue Missionen.
- **WAV-Dateien** (`.wav`) — Voiceover/Sounds, die eingebettet werden.
- **Fertige Missionen** — schreibt der Server hierhin.
Eine **Beispiel-Basis-Karte** liegt bereits unter
[`data/maps/base-map.scx`](./data/maps/base-map.scx) (256×256, Jungle), damit du sofort
die erste Mission bauen kannst.
---
## Die Tools (Prefix `sc_`)
| Tool | Zweck |
|------|-------|
| `sc_list_maps` | listet alle Karten/Missionen im Verzeichnis *(nur lesen)* |
| `sc_describe_map` | Übersicht: Tileset, Größe, Player-Setup, Locations, Trigger-Zusammenfassung *(nur lesen)* |
| `sc_list_locations` | Locations auflisten *(nur lesen)* |
| `sc_create_location` | Location anlegen (Mittelpunkt + Größe in Pixeln) |
| `sc_rename_location` | Location umbenennen |
| `sc_set_player_setup` | Owner-Typ, Rasse und Force je Spieler setzen (+ Force-Namen) |
| `sc_add_trigger` | **Kern-Tool:** Trigger mit Bedingungen + Aktionen hinzufügen |
| `sc_list_triggers` | Trigger mit Klartext-Zusammenfassung auflisten *(nur lesen)* |
| `sc_remove_trigger` | einzelnen Trigger entfernen *(destruktiv)* |
| `sc_clear_triggers` | alle Trigger entfernen *(destruktiv)* |
| `sc_embed_wav` | WAV einbetten und referenzierbar machen |
| `sc_save_map` | aktuellen Stand als neue `.scx`/`.scm` schreiben |
| `sc_reset_map` | nicht gespeicherte Änderungen verwerfen *(destruktiv)* |
**Arbeitsmodell:** Die Bearbeitung läuft pro Basis-Karte als Sitzung im Speicher des
Servers. Mehrere Tool-Aufrufe (Location anlegen, Trigger hinzufügen, WAV einbetten)
sammeln sich an; erst `sc_save_map` schreibt eine neue Datei. Die Basis-Karte bleibt
unverändert. `sc_reset_map` verwirft den Zwischenstand.
### Das Kern-Tool `sc_add_trigger`
Ein Trigger besteht aus **Bedingungen** (`conditions`, UND-verknüpft) und **Aktionen**
(`actions`), plus den **Spielern** (`players`), für die er läuft. Werte dürfen tolerant
angegeben werden: per Bezeichner (`TERRAN_MARINE`), Anzeigename (`Terran Marine`) oder
Zahl (`0`).
**Unterstützte Bedingungen (`type`):** `always`, `never`, `elapsed_time`,
`countdown_timer`, `bring`, `command`, `kill`, `deaths`, `accumulate`, `opponents`.
**Unterstützte Aktionen (`type`):** `display_text`, `set_mission_objectives`,
`play_wav`, `create_unit`, `kill_unit_at_location`, `remove_unit_at_location`,
`move_unit`, `give_units`, `set_resources`, `set_deaths`, `set_countdown_timer`,
`pause_timer`, `unpause_timer`, `run_ai_script`, `run_ai_script_at_location`,
`center_view`, `minimap_ping`, `set_alliance_status`, `victory`, `defeat`.
Beispiel (Spielstart: Text + 4 Marines an der Location "Basis"):
```json
{
"map": "base-map.scx",
"players": ["PLAYER_1"],
"conditions": [{ "type": "always" }],
"actions": [
{ "type": "display_text", "text": "Mission 1 Start" },
{ "type": "create_unit", "player": "PLAYER_1", "amount": 4,
"unit": "TERRAN_MARINE", "location": "Basis" }
]
}
```
**Funksprüche / Voiceover:** Zuerst `sc_embed_wav` aufrufen (gibt einen `wav_path`
zurück), dann im Trigger `play_wav` (mit diesem `wav_path`) zusammen mit `display_text`
und optional `center_view` verwenden.
---
## Selbsttest (beweist die ganze Pipeline)
```bash
./run.sh selftest # im Container
# oder lokal:
python selftest.py
```
Der Test: Basis-Karte laden → Location anlegen → Trigger (Text + Einheiten bei Start) →
als neue `.scx` speichern → neu laden → Trigger + Location bestätigt.
---
## Lokal testen (ohne Docker, stdio)
```bash
python3 -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt
SC_MAPS_DIR=./data/maps python -m starcraft_mcp.server # stdio-Transport
```
Für HTTP lokal: `SC_TRANSPORT=http SC_PORT=8000 SC_MAPS_DIR=./data/maps python -m starcraft_mcp.server`
→ Endpunkt `http://127.0.0.1:8000/mcp`.
### Konfiguration (Umgebungsvariablen)
| Variable | Standard | Bedeutung |
|----------|----------|-----------|
| `SC_TRANSPORT` | `stdio` | `http` für Streamable-HTTP-Betrieb |
| `SC_HOST` | `0.0.0.0` (HTTP) | Bind-Adresse |
| `SC_PORT` | `8000` | Port |
| `SC_MAPS_DIR` | `/data/maps` | Karten-/WAV-/Missions-Verzeichnis |
---
## Gewählte Defaults
- **Beispiel-Basis-Karte:** die MIT-lizenzierte `base-map.scx` aus dem RichChk-Repo
(256×256, Jungle). Liegt in `data/maps/`.
- **Locations** werden als Box um einen Mittelpunkt angelegt (Standard 96×96 px;
32 px = 1 Kachel).
- **`preserve=true`** ist Standard bei `sc_add_trigger` (Trigger bleibt nach Auslösen
bestehen — passt für fast alle Kampagnen-Trigger).
- **RichChk-Logging** ist auf `CRITICAL` gesetzt, damit die Server-Ausgabe sauber bleibt.
- **Netzwerk:** Der Container wird **nicht** direkt nach außen exponiert; Caddy spricht
ihn intern über `sc-mcp:8000` an (sicherer). Zum reinen Lokaltest sind die `ports:`
in der `docker-compose.yml` auskommentiert vorbereitet.
---
## Bekannte Einschränkungen / TODO
- **Transmission mit Portrait (`Transmission`/`TalkingPortrait`):** RichChk hat in dieser
Version **kein** High-Level-Modell für diese Aktion. Funksprüche baust du deshalb aus
`play_wav` + `display_text` (+ optional `center_view`) zusammen. → TODO, sobald RichChk
es unterstützt.
- **Mission-Briefing (`sc_set_briefing`, MBRF):** RichChk bietet (noch) keinen
High-Level-Editor für die MBRF-Sektion. Für v1 bewusst weggelassen. → TODO.
- **Schalter (Switches) in Bedingungen/Aktionen:** für v1 nicht exponiert. → TODO.
---
## Projektstruktur
```
starcraft_mcp/
server.py FastMCP-Server mit allen sc_-Tools
workspace.py Karten-Sitzung im Speicher (Laden/Bearbeiten/Speichern, WAV-Einbettung)
triggers.py Pydantic-Schemas + Builder für Conditions/Actions (Herzstück-Doku)
enums.py tolerante Resolver (Einheiten, Spieler, Vergleiche, …)
richchk_logging.yaml setzt RichChk-Logging auf CRITICAL
selftest.py End-to-End-Selbsttest durch die echten Tools
data/maps/ Karten-Volume (Basis-Karte, WAVs, fertige Missionen)
Dockerfile, docker-compose.yml, Caddyfile, run.sh Betrieb
```
---
## Hinweis zur Erstellung
Code und gesamte Pipeline wurden in einer Linux-Umgebung gebaut und **getestet**
(RichChk-Lese-/Schreib-Pipeline, alle 13 Tools, WAV-Einbettung, sowie der
Streamable-HTTP-Endpunkt per echtem MCP-Client-Handshake). Das tatsächliche Deployment
auf deinem Hetzner-VPS (Docker-Build, Caddy-Subdomain, DNS) führst du mit `./run.sh`
selbst aus — das konnte aus der Build-Umgebung heraus nicht für deinen VPS erfolgen.