STS2 MCP Player
Use this skill when driving the STS2 MCP mod as a gameplay agent or when validating the live MCP contract against the running game.
Server Names
This skill is transport-agnostic. It only assumes that your host agent exposes one active STS2 MCP server with the standard tool surface.
- Recommended local server name (
local_server_name):sts2-ai-agent - Recommended remote server name (
remote_server_name):sts2-ai-agent-remote - Runtime rule: always try
local_server_namefirst, thenremote_server_name. - If your host already exposes STS2 tools directly, call tools directly and skip MCP config troubleshooting.
Recommended SubAgent Config
Use a conservative SubAgent profile for STS2. The goal is to keep the tool surface small, keep one MCP action per iteration, and avoid polluting the main chat prompt with long-running game state.
- Recommended plugin settings:
max_concurrent = 1,auto_discover = false,broadcast_iteration_progress = false,inject_status_to_main_prompt = false - Recommended retention settings:
inject_completed_for_seconds = 120,status_retention_seconds = 900 - Recommended skill settings:
allowed_tool_names = ["health_check", "get_game_state", "get_raw_game_state", "get_available_actions", "get_decision_log", "get_run_summary", "get_scene_guidance", "diff_state", "get_game_data_item", "get_game_data_items", "get_relevant_game_data", "wait_for_event", "wait_until_actionable", "decide", "act"],max_mcp_tools_per_iteration = 1,share_to_main_chat = false
Simplified Config
If your host plugin supports the built-in STS2 shortcut section, prefer this minimal config over a long JSON blob:
[sts2]
enabled = true
local_server_name = "sts2-ai-agent"
remote_server_name = "sts2-ai-agent-remote"
This skill does not require a long MCP config walkthrough. At runtime, simply follow local-first fallback:
- Use
local_server_namefirst. - If local is unavailable, retry the same flow on
remote_server_name. - If aliases are unavailable but STS2 tools are already exposed, call the tools directly.
For an optional skill-local remote checklist, read references/remote-connection.md.
The in-game overlay agent loads the shared play contract below plus the screen-playbooks.md section for the screen it is on as its system prompt. Follow those same documents. Do not invent a parallel workflow.
Quick Start
- Connection checking belongs to the external MCP client or orchestrator that launched the session; the in-game play loop has no
health_check, so it reads live state instead. - Read one decision at a time:
get_game_state, thenactonce, using the stateactreturns for the step after it. Usewait_until_actionableacross animations and screen changes. Useget_raw_game_stateonly if compact state is missing a needed field. When you callact, attach a one-sentencereason-- it is optional for the protocol but it is what the player sees as the decision's rationale, so treat it as part of every act. - For cards, monsters, relics, potions, shop items, and event options, prioritize game-data tools before using memory:
get_relevant_game_data(default, scene-aware minimal context; omititem_idsand the current screen decides which ids to look up) ->get_game_data_item(single-entity lookup) ->get_game_data_items(batch compare/filter). The compact view carries the ids those lookups key on:combat.hand[].card_id,combat.enemies[].enemy_id,run.deck[].card_ids,run.relic_ids,selection.cards[].card_id,reward.cards[].card_id,shop.cards[].card_id,shop.relics[].relic_id,shop.potions[].potion_id,bundles[].cards[].card_id, andchest.relics[].relic_id. - Before every decision, call
get_game_state. - Route by
state.sessionfirst:session.modedistinguishessingleplayervsmultiplayer, andsession.phasedistinguishesmenu,character_select,multiplayer_lobby, andrun. - Only invoke actions that are present in
available_actions. actanswers with the state the action left behind, already compact: use it for the next decision instead of reading again. Re-read after apendingresponse, a screen change, or when you deliberately want the full payload withraw_state=true.- Treat multiplayer as local-player control only. Never invent teammate actions that are not present in the latest state.
- Recompute indexes from fresh payloads every time. Never reuse stale hand, node, reward, or selection indexes.
Do not trust memory over the current payload. The game mutates screens in place, overlays replace rooms, and some actions complete only after a follow-up state transition.
Game Data Priority Rules
- Never guess static game facts (card text, potion targeting, monster metadata, relic effects, event option details) from memory when game-data tools are available.
- Use
get_relevant_game_datafirst for current-scene context in combat/shop/event/reward/card-selection/chest/bundle flows. Passingitem_idsis optional: without it the tool derives the ids the screen is about (the hand in a fight, the stock in a shop, the offered cards on a reward, card-selection or bundle screen, the offered relics in a chest, the event you are in). Pass them when you want to ask about a specific id instead. - Use
get_game_data_itemwhen you need deep details for one entity id. - Use
get_game_data_itemswhen comparing multiple entities (for example, reward-card choices, shop candidates, potion options). - If state and metadata disagree, trust live state for legality and metadata for semantics; then re-read state.
Non-Negotiable State Rules
- Treat
UNKNOWNas transient only once. Re-read state once; if it persists, stop guessing and inspect the payload. - Treat
state.sessionas the source of truth for singleplayer vs multiplayer. Do not infer mode from screen names or tool names alone. - Resolve overlays before room flow.
MODAL,CARD_SELECTION, reward-card overlays, and timeline overlays take priority over map or combat planning;modal.underlying_screennames the room underneath. - Treat
pendingresponses as an instruction to stay inside the returned screen flow. - In combat,
combat.action_readinessexplains an action list that looks wrong, and it is the field to read before concluding anything is broken. It is built from the same single gate evaluation as that response'savailable_actions, so the two can never disagree:can_use_combat_actions = truealways carriesplay_card/end_turn. When it is false,reasonnames the one thing in the way.modal_openmeans clear the modal first;hand_in_card_play,action_queue_unsettled,game_action_running,not_player_action_phaseandsnapshot_stabilizingall mean wait withwait_until_actionable;combat_pausedmeans a person paused the run, so wait rather than act. A missingplay_cardonCOMBATis one of these, not a lost turn - do not spam re-reads inside the same frame and do not switch to an unrelated action to make progress. - Treat
proceedas a room action, not a universal fallback.
Action Failure Rules
- A failed action (or failed game-data call) returns an
errorobject. Readerror.codebefore deciding what to do:invalid_request,invalid_action, andinvalid_targetmean the request itself was wrong, so re-read state and fix the action or its indexes instead of repeating it. - Retry only when
error.retryableistrue(for examplestate_unavailablewhile a transition is still settling). When it is false, change your approach rather than retrying the same call. error.status_codemirrors the API status when present;internal_errormeans an unexpected failure, not a legal move you can reach by retrying.- The three game-data tools answer with the same envelope:
collection_not_found(a bad name, so fix the call),game_data_unavailable(the mod could not be reached, soretryabletells you whether to wait), andinvalid_game_data(the payload shape was wrong; do not retry).
Screen Routing
MAIN_MENU: prefercontinue_run; if unavailable, finish timeline gates (slot obtained epochs, confirm unlock overlays) or start a run fromopen_character_select. Character unlocks happen on the timeline, not onGAME_OVER. Do not callswitch_profileunless asked;option_indexis the native profile id 1..3.CHARACTER_SELECT: choose an unlocked character, wait forcharacter_select.embark = true, thenembark.MULTIPLAYER_LOBBY: stay on the same compact tool surface; useavailable_actionsforhost_multiplayer_lobby,join_multiplayer_lobby,select_character,ready_multiplayer_lobby, ordisconnect_multiplayer_lobby. Once the local player is ready,select_characterdisappears andunreadytakes its place -- re-picking a character means unreadying first.MAP: usechoose_map_nodewithmap.options[].i. In multiplayer, ifmap.local_voteis set,wait_until_actionableinstead of voting again; ifmap.votesexist and you have not voted, follow that option.COMBAT: stay inside combat actions unless a selection overlay interrupts. Budget block and pick targets fromcombat.enemies[].intents[](damage/hits/total_damage) and both sides'powerslines, not fromlethal_risksalone.REWARD: prefercollect_rewards_and_proceedunless making deliberate reward choices.CARD_SELECTION: finish the selection withselect_deck_cardand, when exposed,confirm_selection.SHOP:open_shop_inventoryfirst, then buy/remove actions, thenclose_shop_inventory, thenproceed.REST: usechoose_rest_option; if selection opens, resolve it beforeproceed.CHEST:open_chest -> choose_treasure_relic -> proceed.BUNDLE_SELECTION:choose_bundlethenconfirm_bundlewhen exposed.CAPSTONE_SELECTION:choose_capstone_optionfromcapstone.options[].i.EVENT: usechoose_event_optioneven after combat returns to a finished event.CRYSTAL_SPHERE:crystal_clear_celluntil divinations are spent, thenproceed.GAME_OVER:continue_game_overfirst. Wait whilegame_over.phase=summary_animating. Usereturn_to_main_menuonly when it is exposed.UNLOCK:confirm_unlockrepeatedly until the screen closes; never bypass it with a menu-return action. The compact top level mirrors the raw block underunlock(unlock_type,items).FAKE_MERCHANT: the Fake Merchant event screen.open_shop_inventoryopens its inventory andproceedleaves the screen.PATCH_NOTES: patch notes shown from the main menu.close_main_menu_submenucloses it.CARD_LIBRARY: the deck / compendium card viewer.close_main_menu_submenureturns to the previous screen, both from the main menu and from inside a run.CARD_PILE: a combat card pile (draw / discard / exhaust).close_cards_viewreturns to combat.CARD_INSPECT/RELIC_INSPECT: inspect overlays. The sameclose_cards_viewaction that closes the card list also closes these.FEEDBACK: the feedback form. No mod action closes it yet, so only open it when the task asks and let the player finish it.PAUSE_MENU,SETTINGS,COMPENDIUM,RELIC_COLLECTION,POTION_LAB,BESTIARY,STATS,RUN_HISTORY(andCARD_LIBRARYwhen it is opened from inside a run): the pages shown over a frozen run when a person presses pause. Room actions are suppressed andcapstoneis null on all of them;close_main_menu_submenuis the only action advertised, and it steps back one page (CARD_LIBRARY->COMPENDIUM->PAUSE_MENU). The pause menu itself offers nothing - a person resumes that one, so wait instead of trying to act.
For detailed per-screen sequences and pitfalls, read the section for the current screen in references/screen-playbooks.md. The in-game loop is handed that one section with the state; an external client reads the file whole.
Common Pitfalls
skip_reward_cardscloses the overlay but may leave the underlying reward item claimable.- Multi-select overlays may require
confirm_selection; do not assume one click is enough. - Potion targeting depends on
target_type; some potions need notarget_index. - Multiplayer targeting still controls only the local player. In the compact view the
targethint (enemy/player) plus thetargetsindex list tell you whattarget_indexmay address; the full state names the same fieldstarget_index_spaceandvalid_target_indices. Never assume teammate control. shop.open = truemeans inner inventory, not room completion (raw state spells itshop.is_open).- Timeline gates can block run start until the overlay is confirmed or the submenu is closed.
return_to_main_menuonGAME_OVERbeforecontinue_game_overskips score, unlock, and save.UNLOCKis not a card-selection screen; onlyconfirm_unlock.- After a multiplayer map vote,
map.local_votemeans wait, not pick a second node.
Minimal Decision Heuristics
- In combat, spend energy efficiently and avoid ending turn with obvious free value unused.
- In rewards, take cards only when the upgrade is clear; otherwise skip.
- In shops, check relics and removal before committing all gold.
- In events, prefer unlocked options and re-read state after every branch.
Reading a Decision
Both MCP surfaces expose the same tool face, and the read half of a decision can be one call:
decideanswers withstate(the compactagent_view, same shape asget_game_state),available_actions(the descriptors fromget_available_actions, with theirrequires_index/requires_target/ target hints), andscene_guidance(the same objectget_scene_guidancereturns). The documented loop assembles those three from three or four calls, each rebuilding the state on the game thread;decidereads one route, so the actions belong to the state beside them rather than to a frame the game may already have left.scene_guidancealways carriesscreen,scene,guidance, andplaybook.guidanceis empty on a screen with no strategic choice;playbookis the per-screen action sequence and is never empty (an unmapped screen gets the index of the sections). The Python sidecar addsevent_id/event_options/guidance_sourcefrom the offline event index, which the mod does not ship.
Reading an Action Result
actanswers with the state the action left behind, and on both surfaces thatstateis the compactagent_view: the same shapeget_game_statereturns, so it is the next decision's input rather than something to re-read.action,status,stable, andmessageare unchanged.raw_state=truereturns the full/statepayload instead. Use it only to inspect a field the compact view does not carry: it is roughly 4,000-9,500 tokens per action.- A rejected index is correctable rather than merely refused. The error object carries
error.details.field(the index you got wrong),error.details.submitted(what you sent),error.details.valid_indices(the indexes the payload accepts right now), anderror.details.valid_field(the payload path they came from:combat.hand,map.options,combat.hand[].targets, ...). Fix the call from those instead of resending the same value. - An action that is not in
available_actionsanswers with the codeinvalid_actionand the currentavailable_actionslist in its details. status: "outcome_unknown"means the action request may have completed but its response was lost. One/statereconciliation was attempted and the state it read is underreconciliation.state(compact, marked);reconciliation.state_read/action_effect_compared(alwaysfalse) /action_outcome(always"unknown") say that the state was read but the action was never compared against it, andsucceeded/statusdescribe that read rather than the action. Never replay the action automatically: read the reconciled state and decide again from it.wait_until_actionableanswers with the same compactstateasget_game_state, so a wait across an animation costs a decision-sized payload rather than the full one.raw_state=trueis its escape hatch, exactly as onact.
Run Decision Logs
- Create one markdown log per played or continued run under
agent_knowledge/run_logs/. - Use
YYYYMMDD-HHMM_<character>_<seed>.mdwhen possible; useunknown-characterorunknown-seeduntil the state exposes the missing value. - Record route choices, card/relic/potion rewards, shop buys/removes, event branches, rest choices, key combat turns, potion use, lethal planning, and MCP/action anomalies.
- Keep entries short so logging does not block play. If needed, execute the legal action first and flush the note after the returned state stabilizes.
- Use the template in
agent_knowledge/run_logs/README.mdfor the header and decision table.
Choose the Right Tool Surface
- Use the guided profile for normal play and most evaluations.
- Keep
get_relevant_game_data/get_game_data_item/get_game_data_itemsavailable in guided runs for card, monster, potion, shop, and event decisions. - Prefer
decidewhen a step needs state and guidance together; it is the same answers as the individual read tools, from one call. - Use legacy per-action tools only when a harness explicitly needs tool-by-tool coverage.
- Use
run_console_commandandinject_event_churnonly in development flows where debug actions are enabled.
Both MCP surfaces (the mod's native /mcp and this Python sidecar) expose the same tool names and
arguments. get_scene_guidance and decide.scene_guidance answer the same four keys on both:
screen, scene, guidance (empty where the screen has no strategic choice) and playbook (never
empty). The sidecar adds event_id / event_options / guidance_source from the offline index in
docs/game-knowledge/events.md, which the mod does not ship.
For validation flows, read references/debug-and-validation.md.
For the choices the shared contract does not make for you — which node to enter, when to heal instead of smith, what to buy, when to drink a potion, which enemy to kill first, and how a host and a teammate divide the work — read references/strategy.md. Every rule there names the payload field it reads. Read the file whole; the in-game loop does not, and cannot, because it has no file access — the mod injects one section of it per screen instead (see the note at the top of that file).