sts2-bot/docs/research/03-sts2mcp-interface.md
0xrsydn fb32822468 Add design doc and research notes
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.
2026-09-22 00:01:22 +07:00

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.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:

// 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.