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.
5.7 KiB
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:
{
"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:
grep -c IsPlayPhase sts2.xml # -> 0
Replacements present in v0.107.1:
MegaCrit.Sts2.Core.Combat.PlayerTurnPhase.Play(per-player)PlayerCombatState.PhaseActionSynchronizerCombatState.PlayPhase/.NotPlayPhaseCombatManager.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:
// 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;
}
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:
{"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:
{ "port": 15526 }
It also injects an "Instant Mode" checkbox into the game settings, which shortens animations. Worth enabling for batch runs.