DESIGN.md covers the three-layer architecture (facts in code, Jev for tactics, gated escalation for macro). research/ documents the engine and mod surface, the Jev classifier's measured behavior, the STS2MCP HTTP interface, state shapes, failure modes, decision architecture, and a run log of the first four sessions.
168 lines
5.7 KiB
Markdown
168 lines
5.7 KiB
Markdown
# 03 — STS2MCP interface
|
|
|
|
A community mod by `kunology` that exposes the running game over a local HTTP
|
|
API. Repository: `github.com/Gennadiyev/STS2MCP`, MIT.
|
|
|
|
It is the actuator for this project: it reads state and performs one action at
|
|
a time. It does **not** alter gameplay (`affects_gameplay: false`).
|
|
|
|
## Endpoints
|
|
|
|
Base URL `http://localhost:15526`, no authentication.
|
|
|
|
| Method | Path | Purpose |
|
|
|---|---|---|
|
|
| GET | `/api/v1/singleplayer?format=json\|markdown` | Read state |
|
|
| POST | `/api/v1/singleplayer` | Perform one action |
|
|
| GET | `/api/v1/multiplayer` | Read multiplayer state |
|
|
| POST | `/api/v1/multiplayer` | Multiplayer action |
|
|
| GET | `/api/v1/profile` | Persistent profile progress |
|
|
| GET | `/api/v1/compendium` | Compendium-shaped progress |
|
|
| GET | `/api/v1/wiki?query=&item_type=&limit=` | Fuzzy card/relic lookup |
|
|
| GET/POST | `/api/v1/profiles` | List / switch / delete profile slots |
|
|
|
|
All POST bodies carry an `"action"` field. Responses carry
|
|
`{"status": "ok"|"error", "message": ...}`.
|
|
|
|
## `state_type` is the action enumerator
|
|
|
|
Every response carries `state_type`, and each type has a small fixed action
|
|
set. **This is the action space.** There is no need to write one.
|
|
|
|
| `state_type` | Legal actions |
|
|
|---|---|
|
|
| `menu`, `game_over` | `menu_select` |
|
|
| `monster` / `elite` / `boss` | `play_card`, `use_potion`, `end_turn` |
|
|
| `hand_select` | `combat_select_card`, `combat_confirm_selection` |
|
|
| `rewards` | `claim_reward`, `proceed` |
|
|
| `card_reward` | `select_card_reward`, `skip_card_reward` |
|
|
| `map` | `choose_map_node` |
|
|
| `event` | `choose_event_option`, `advance_dialogue` |
|
|
| `rest_site` | `choose_rest_option`, `proceed` |
|
|
| `shop`, `fake_merchant` | `shop_purchase`, `proceed` |
|
|
| `treasure` | `claim_treasure_relic`, `proceed` |
|
|
| `card_select` | `select_card`, `confirm_selection`, `cancel_selection` |
|
|
| `bundle_select` | `select_bundle`, `confirm_bundle_selection`, `cancel_bundle_selection` |
|
|
| `relic_select` | `select_relic`, `skip_relic_selection` |
|
|
| `crystal_sphere` | `crystal_sphere_set_tool`, `_click_cell`, `_proceed` |
|
|
| `overlay`, `unknown` | none (transitions, or manual) |
|
|
|
|
`use_potion` and `discard_potion` work in any state where potions are
|
|
reachable, including outside combat.
|
|
|
|
## VERSION DRIFT — release 0.4.0 is broken on this build
|
|
|
|
### Symptom
|
|
|
|
`GET /api/v1/singleplayer` returns **HTTP 500** as soon as combat starts:
|
|
|
|
```json
|
|
{
|
|
"error": "Failed to read game state: Method not found:
|
|
'Boolean MegaCrit.Sts2.Core.Combat.CombatManager.get_IsPlayPhase()'.",
|
|
"exception_type": "System.MissingMethodException",
|
|
"stack_trace": " at STS2_MCP.McpMod.BuildBattleState(RunState, CombatRoom)"
|
|
}
|
|
```
|
|
|
|
Menu, character select, map, rewards, and events all work. **Only combat
|
|
fails** — the one state that matters most.
|
|
|
|
### Root cause
|
|
|
|
`CombatManager.IsPlayPhase` was removed in game `v0.107.1`. Verified:
|
|
|
|
```bash
|
|
grep -c IsPlayPhase sts2.xml # -> 0
|
|
```
|
|
|
|
Replacements present in `v0.107.1`:
|
|
|
|
- `MegaCrit.Sts2.Core.Combat.PlayerTurnPhase.Play` (per-player)
|
|
- `PlayerCombatState.Phase`
|
|
- `ActionSynchronizerCombatState.PlayPhase` / `.NotPlayPhase`
|
|
- `CombatManager.DebugOnlyGetState().CurrentSide == CombatSide.Player`
|
|
|
|
### Timeline
|
|
|
|
| Event | Date |
|
|
|---|---|
|
|
| Release `0.4.0` (broken) | 2026-05-05 |
|
|
| Fix commit `55e06485`, PR #123 | 2026-07-29 |
|
|
| Newer tag | none |
|
|
| CI build artifact | none |
|
|
| Fork shipping a prebuilt DLL | none (checked 12 forks) |
|
|
|
|
The fix exists in source but was never released.
|
|
|
|
### Resolution
|
|
|
|
Build from upstream `main`. The fix is present and documented in the code:
|
|
|
|
```csharp
|
|
// STS2 v0.107 removed CombatManager.IsPlayPhase. The turn phase now lives per-player on
|
|
// PlayerCombatState.Phase
|
|
internal static bool IsPlayPhase(Player? player)
|
|
{
|
|
if (!IsPlayerSideTurn()) return false;
|
|
return player?.PlayerCombatState?.Phase == PlayerTurnPhase.Play;
|
|
}
|
|
```
|
|
|
|
```bash
|
|
nix shell nixpkgs#dotnet-sdk_9 --command bash -c '
|
|
dotnet build STS2_MCP.csproj -c Release -o out/STS2_MCP \
|
|
-p:STS2GameDir="$HOME/Library/Application Support/Steam/steamapps/common/Slay the Spire 2"'
|
|
```
|
|
|
|
`Build succeeded. 0 Warning(s) 0 Error(s).` Result: 236,544 B DLL, replacing
|
|
the 194,560 B release. The release DLL is kept at
|
|
`vendor/STS2_MCP.dll.release-0.4.0`.
|
|
|
|
**Lesson:** the game moves faster than the mod's releases. Upstream open issues
|
|
#131 and #132 already track `v0.111` compatibility. Expect this class of break
|
|
to recur after any game update. Pin the mod build to the game build.
|
|
|
|
## Known doc drift inside the mod
|
|
|
|
`AGENTS.md` in the repo uses MCP **tool** names; `docs/raw-simplified.md` uses
|
|
the HTTP **action** names. They do not match:
|
|
|
|
| AGENTS.md | HTTP action |
|
|
|---|---|
|
|
| `combat_end_turn` | `end_turn` |
|
|
| `event_choose_option` | `choose_event_option` |
|
|
| `rewards_pick_card` | `select_card_reward` |
|
|
| `rest_choose_option` | `choose_rest_option` |
|
|
| `proceed_to_map` | `proceed` |
|
|
|
|
Trust `raw-simplified.md` and the live API for direct HTTP use.
|
|
|
|
## Hard blocker: Timeline cannot be automated
|
|
|
|
After a run ends with a pending epoch, the mod refuses:
|
|
|
|
```json
|
|
{"status":"error",
|
|
"error":"Timeline has obtained epochs that still need to be revealed manually;
|
|
not opening Timeline because this game state logs invalid unlock-state
|
|
errors when entered through automation",
|
|
"pending_epoch_ids":["NEOW_EPOCH"],
|
|
"manual_action_required":true}
|
|
```
|
|
|
|
This is a **deliberate guard**, not a bug. Entering Timeline through automation
|
|
would corrupt unlock state. A human must open the Timeline and reveal the
|
|
epoch. Until then the main menu offers only `settings` and `quit` and no run
|
|
can be started.
|
|
|
|
## Mod config
|
|
|
|
The mod writes `mods/STS2_MCP.conf`:
|
|
|
|
```json
|
|
{ "port": 15526 }
|
|
```
|
|
|
|
It also injects an **"Instant Mode"** checkbox into the game settings, which
|
|
shortens animations. Worth enabling for batch runs.
|