Token-efficient MCP proxy for Slay the Spire 2 via Gennadiyev/STS2MCP
STS2 Compact MCP
简体中文 | Copy-paste AI installation prompt
A token-efficient MCP proxy for Gennadiyev/STS2MCP.
It leaves the game mod and its REST API unchanged. Instead of exposing 64 verbose MCP tools, this proxy exposes three:
observe: compact, delta, or full game stateact: every game action through one stable interfaceinspect: full map, wiki, profile, and other details on demand
Project scope
This repository contains only the external Python MCP proxy. Development and issue reports should be directed according to the component involved:
| Component | Repository | What it owns |
| --- | --- | --- |
| Game mod and REST API | Gennadiyev/STS2MCP | STS2_MCP.dll, STS2_MCP.json, game integration, port 15526, raw state and actions |
| Compact MCP proxy | This repository | token reduction, delta state, definition cache, deterministic automation, three-tool MCP interface |
Changes in this repository do not rebuild, replace, or patch the game DLL. Install and update the game mod from the upstream project. Modify this project when changing model-facing output, token usage, caching, automatic Continue or reward handling, or MCP client behavior.
Why
The upstream MCP is intentionally descriptive and complete. That is useful for general clients, but expensive for autonomous runs:
- 64 tool schemas are loaded into model context
- full map data is repeated on every map observation
- card text, relic text, pile contents, and keyword definitions repeat often
- an action and the following state read normally require separate calls
This proxy keeps interactive fields such as card indices, targets, legal options, and current card text, while removing repeated glossary and static metadata from the default response.
Measured against STS2MCP v0.4.0 in this repository:
| MCP surface | Tools | Serialized tool schema | | --- | ---: | ---: | | Upstream | 64 | 41,988 bytes | | Compact proxy v0.3 | 3 | 1,906 bytes |
That is a 95.5% reduction before game state is exchanged. The tool list remains intentionally fixed and is serialized in a stable order, so clients can reuse the same prompt prefix across turns.
Automatic deterministic actions
The proxy now removes transitions that do not need model judgment:
- clicks main-menu
continue - advances Ancient dialogue
- clicks an event's sole unlocked Continue/Proceed option
- collects gold
- collects a potion when a slot is free
- opens card rewards, but never chooses a card
- proceeds after all automatically collectable rewards are gone
- proceeds after a chosen rest-site or treasure action completes
- proceeds after a completed Crystal Sphere or defeated fake merchant
Safety boundaries:
- it never ends a combat turn
- it never chooses a card, relic, map node, shop purchase, rest action, or event branch
- it stops when potion slots are full
- it emits a short alert when exactly three potions are held
- it leaves non-requested reward types, including relic rewards, for the model
- leaving a shop or skipping an unused rest site remains a model decision
Requirements
- Python 3.11+
uv- Slay the Spire 2 running with STS2MCP enabled
- STS2MCP REST server available at
http://localhost:15526
Installation
1. Install the upstream game mod
Download the
latest STS2MCP release.
Copy these two release files into the game's mods directory:
STS2_MCP.dll
STS2_MCP.json
On macOS, the default directory is:
~/Library/Application Support/Steam/steamapps/common/Slay the Spire 2/
SlayTheSpire2.app/Contents/MacOS/mods/
You can create it and copy the files from Terminal:
GAME_DIR="$HOME/Library/Application Support/Steam/steamapps/common/Slay the Spire 2"
MODS_DIR="$GAME_DIR/SlayTheSpire2.app/Contents/MacOS/mods"
mkdir -p "$MODS_DIR"
cp STS2_MCP.dll STS2_MCP.json "$MODS_DIR/"
Launch the game, open Settings -> Mods, enable STS2MCP, and accept the first-launch consent dialog. Verify the upstream REST server:
curl -s http://localhost:15526/
A JSON response with "status":"ok" means the game-side mod is ready.
For Windows, Linux, source builds, and current compatibility notes, follow the
upstream installation guide.
2. Install this compact proxy
Install uv and clone this repository:
brew install uv
git clone https://github.com/chiang881/sts2-compact-mcp.git
cd sts2-compact-mcp
uv sync
Run the proxy:
uv run sts2-compact-mcp
Or directly from a checkout:
uv run --directory /absolute/path/to/sts2-compact-mcp sts2-compact-mcp
3. Configure the AI client
{
"mcpServers": {
"sts2-compact": {
"command": "/opt/homebrew/bin/uv",
"args": [
"run",
"--directory",
"/absolute/path/to/sts2-compact-mcp",
"sts2-compact-mcp"
]
}
}
}
Disable the original sts2 MCP entry when using this proxy so the client does
not load both sets of tool schemas. Keep the upstream game mod enabled:
only its verbose Python MCP server is replaced by this compact proxy.
Restart the AI client after changing its MCP configuration. The game must be running with the upstream mod enabled whenever the proxy is used.
Tools
observe
observe(mode="delta", multiplayer=false)
Modes:
delta: only changes since the previous observationcompact: complete current screen in compact formfull: untouched upstream JSON for debugging
The first delta observation returns a complete compact state.
act
act(
action="play_card",
args={"card_index": 0, "target": "JAW_WORM_0"},
revision=12
)
act accepts upstream raw action names and familiar aliases such as
combat_play_card, map_choose_node, and rewards_pick_card.
Legacy argument names such as node_index, reward_index, and option_index
are translated automatically. An mp_ action prefix automatically selects
the multiplayer endpoint.
After an action, the proxy waits for animation or enemy-turn transitions and
returns the next meaningful decision state. Successful upstream acknowledgments
and automatic clicks are omitted from the response. Supplying revision
protects against acting on stale card or reward indices.
Stable card, potion, relic, and status metadata is emitted in a top-level
defs dictionary only when first encountered or when it changes. This includes
names, card types, rarity, targets, and rules text. Subsequent states refer to
the item by ID while retaining dynamic fields such as index, cost, legality,
upgrade state, and counters.
inspect
inspect(kind="map")
inspect(kind="wiki", query="Bash")
inspect(kind="profile")
inspect(kind="compendium")
inspect(kind="state")
inspect(kind="metrics")
Use inspect(kind="state") as the escape hatch for the full original state.
inspect(kind="metrics") reports cumulative MCP result bytes and a rough
token estimate. It is opt-in and is never attached to normal gameplay output.
The MCP process cannot see provider-side cached_tokens, model output tokens,
or the host application's complete conversation prompt.
What the model receives
The proxy does not build the model prompt. The host application combines its instructions, conversation history, the three MCP tool schemas, and each MCP result. This proxy controls only the tool schemas and tool results.
At each meaningful decision:
- combat sends the complete current hand because playing a card shifts indices
- draw, discard, and exhaust piles are grouped by card ID and count
- card reward and selection screens send every current candidate
- map state sends only the current position and immediately reachable nodes
- descriptions and other stable item metadata use lazy
defs - an
actresult already contains the next decision state, so an immediate follow-upobserveis unnecessary
It does not send the entire deck on every turn. Full upstream state is returned
only by an explicit observe(mode="full") or inspect(kind="state").
Prompt-cache behavior
Provider prompt caching depends on an exact shared prefix. This project helps by keeping the tool set fixed, sorting JSON object keys deterministically, and moving repeated definitions into an ID cache. Dynamic game state remains at the end of the conversation as tool results.
The MCP server cannot set the host's provider cache key or read its exact cache
hit count. Inspect the API response usage or provider dashboard for authoritative
cached_tokens; use inspect(kind="metrics") only for the MCP result portion.
In a representative four-card combat sample, compared with v0.2:
| Payload | v0.2 | v0.3 | | --- | ---: | ---: | | First encounter, including definitions | 1,095 bytes | 1,026 bytes | | Repeated state after definition cache hit | 957 bytes | 601 bytes |
The repeated state was about 37% smaller. Actual savings depend on hand size, duplicate cards, statuses, potions, and how often definitions recur.
Suggested agent policy
- Start with
observe("compact"). - Pass the latest
revto everyact. - Use the state returned by
act; do not immediately callobserveagain. - Use
observe("delta")after external UI changes. - Use
inspectonly when a decision needs information omitted from compact state. - Do not call reward-claim or continue actions unless the proxy stopped because a real choice remains.
Compatibility
The proxy does not modify STS2MCP, the game DLL, save data, action names, card indices, or target IDs. The original server can remain installed and is still available for clients that require all 64 legacy tools.
Development
uv sync --extra dev
uv run pytest
The test suite includes deterministic reward collection, full-potion-slot
handling, hardwired Continue behavior, stale-state protection, and an explicit
guard against automatic end_turn.