A mod for Shadows of Forbidden Gods that embeds a Model Context Protocol server in the running game. Any MCP client (Claude Code, Claude Desktop, custom scripts) can then query the live game state — locations, units, persons, societies, your god — and command your agents: move them, perform challenges, use powers, end the turn.
Claude Code (Mac/PC) ──HTTP──▶ ShadowsMCP.dll inside the game (Windows PC)
tools/call │ marshalled to Unity's main thread
▼
live Map object
- Install the mod: copy
dist/ShadowsMCP/into the game's local mod folder:C:\Program Files (x86)\Steam\steamapps\common\Shadows of Forbidden Gods\data\optionalData\ShadowsMCP\ - Enable it in the game's mod menu and start (or load) a game.
- Windows Firewall: allow the game when prompted (the server listens on the LAN by default).
- Connect a client from any machine on your LAN:
Or verify with curl first:
claude mcp add --transport http shadows http://<game-pc-ip>:8017/mcp
curl -X POST http://<game-pc-ip>:8017/mcp -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"game_overview","arguments":{}}}'
See docs/manual-test-checklist.md for a full end-to-end test script.
| Tool | What it does |
|---|---|
game_overview |
Turn & time budget (endless flag, maxTurns / turnsRemaining — null in an endless game), your god, victory mode & progress, panic breakdown, war & investigation counts, world counts, pending decision |
get_threats |
The game's Threats panel: heroes hunting your agents, the Chosen One's prophecy, incoming wars, holy-order mood (sorted by severity) |
list_locations / get_location |
The world map. Detail adds settlement economy (population, prosperity, food, ruler/heir, holy order, current action), property influences, and the clues heroes hold there |
list_units / get_unit |
Units (default scope: yours); kind, position, hp, task. Detail adds menace/profile (with their sticky floors and hunt radius), agent internals (minions, combat, corruption/fatigue) and the investigation building against it |
list_persons / get_person |
People: rulers, nobles. Detail is the full sheet — stats, XP, kills, traits/items (with descriptions), alerts, relationships, house curses |
list_social_groups / get_social_group |
Societies and factions, military, wars. Detail adds diplomacy, the national action underway, and a holy-order block for religions |
list_wars |
Every active war: attacker, defender, the attacker's objective, start & projected end |
list_investigations |
Detection dashboard: every clue pointing at your agents, with investigators, weight and location |
list_holy_orders |
Religions: enshadowment, prophet, temples, worshippers, divine entity, and each tenet with its status, range and which way it can currently be influenced. orderId/verbose add descriptions and example calls |
get_recent_events |
The game's turn message log — what changed recently (newest first) |
get_player_state |
Your god, power, agents, recruitment capacity, powers, and the win-condition sheet (seal thresholds, agent-cap curve, victory text) |
list_recruitable_agents |
Recruitment capacity, enthrallable archetypes, and corruptible heroes |
list_powers |
Your god's powers and whether each is castable now |
list_challenges |
Challenges available to one of your agents where it stands |
get_tips |
Curated, agent-facing explanations of game mechanics (infiltration, politics, magic, economy, gods, factions). No args = index; id/category for one tip or a topic. The same tips also surface in game_overview/end_turn under tips, and the core mechanics are in the server's initialize instructions |
inspect |
Query ANY element by path, e.g. map.locations[4].settlement (read-only reflection) |
move_unit |
Send one of your agents toward a location |
cancel_task |
Clear an agent's current order |
perform_challenge |
Have an agent start a challenge |
use_power |
Cast one of your god's powers |
recruit_agent |
Spend a recruitment point to enthrall a new agent (archetype onto a location, or corrupt an eligible hero in place) |
command_army |
A commandable military unit's special orders: raze the settlement it stands on, drive back an enemy hero, or attack an enemy army on its tile |
command_agent |
An agent's actions against another agent on its tile: attack a hostile hero (starting the duel cancels their ritual for good, even if you flee), rob a weaker one, trade items with one of your own, or follow a merchant. Available orders are listed under orders in get_unit/list_units |
influence_holy_order_tenet |
Spend a religion's banked Elder influence to shift one of its tenets (toward_elder / toward_human) — the holy-order screen's doctrine lever |
oppose_divinity |
Undermine (−1 power, −10 strength) or exile the divine entity behind a religion |
get_pending_decision / resolve_decision |
Read and answer a pending decision popup (level-up trait pick, event choice, agent-death notice) |
end_turn |
Advance the turn; if a decision popup blocks it, returns the options and accepts resolveOptionIndex to answer them (so decisions can be resolved without the two tools above). Always returns a digest of what happened across every turn advanced — popups force-dismissed (named, not just counted), the turn's notable news (razing, battles, deaths, wars), and lost: your own units that died, which also stops a batch with stopReason:"unitLost". Once the game is over (endOfGameAchieved), returns gameOver with the outcome and does not advance |
new_game |
Start a fresh game headlessly — from the main menu or over a running one (confirm:true required then; the old game is abandoned unsaved). Pick the god (or random), seed, map size, difficulty and turn limit; slow (~30–120 s of map gen + burn-in), returns the seed used and a full game_overview-style summary |
Other mods' content largely works out of the box: modded units, challenges, popups and gods flow
through the game's own lists, the generic popup handler and inspect. A content mod can go
further and advertise its content — agent-facing tips for its mechanics, which of its popups
are safe to auto-dismiss, a new_game key for its god, ability previews for its archetypes — by
declaring one duck-typed method (string getShadowsMcpManifest()) on its ModKernel returning a
JSON manifest. No assembly reference in either direction. game_overview.mcpExtensions lists the
mods that did. Schema, integration details and a copy-paste prompt for adding support to a mod
with a coding agent: docs/mcp-extension-guide.md.
Locations use the game's own index (L3). Units/persons/social groups/challenges get
session-scoped ids (U17, P42, SG5, C8) assigned on first serialization.
Ids are stable within one game session only — after loading a save or starting a new
game, re-query instead of reusing old ids.
Defaults: listen on all interfaces, port 8017 (retries 8018…8026 if busy). Options (port, LAN vs localhost-only) are exposed through the game's per-mod config.
Security note: the server has no authentication — anyone on your network can read your game and move your agents. That's the intended scope (home LAN). Switch to localhost-only if the machine is on an untrusted network.
Runs in the background: MCP requests are processed on the game's main thread, which Unity pauses when the window loses focus. So the mod forces
Application.runInBackground = true(and downgrades exclusive fullscreen to borderless, which would otherwise minimize and pause on focus loss). This keeps MCP calls responsive while you work in another window, at the cost of the game continuing to run/render when unfocused.
To analyse what a real session costs (response sizes / token usage), route the client through the
logging proxy in tools/ — MCP clients have no built-in way to capture request/response bodies over
HTTP transport. On the machine running the client:
node tools/mcp-tee.mjs 9017 http://<game-pc-ip>:8017/mcp game-log.jsonl # forward to the game + tee to JSONL
claude mcp remove shadows && claude mcp add --transport http shadows http://localhost:9017/mcp
# …play a session, then:
tools/mcp-log-report.sh game-log.jsonl # ranked per-tool byte/token cost (needs jq)mcp-tee.mjs forwards every call unchanged and logs each exchange with reqBytes/resBytes;
mcp-log-report.sh ranks response bytes per tool. Both are self-documented in their headers.
Needs the .NET SDK (8+) on any OS, plus the game's assemblies:
- Copy
<game>/ShadowsOfForbiddenGods_Data/Managed/→lib/Managed/(gitignored, never redistribute). ./build.sh— runs the protocol and save-analysis smoke tests, builds the mod and savecli (Debug + Release), and assembles bothdist/ShadowsMCP/(local install) anddist/upload/ShadowsMCP/(Workshop upload).
Repo map: src/Core/ = game-independent MCP/JSON/HTTP layer (also compiled into
src/TestHost/, a Linux console host used by tools/smoke-test.sh); src/SaveAnalysis/ =
save-file reading engine shared by src/SaveCli/ (see below) and, potentially later, the mod;
src/Mod/ = the game-facing mod layer; docs/ = game data-model reference, modding tutorial,
test checklist.
savecli reads the game's save files (*.sv in ApplicationData/ShadowsForbiddenGodsSaves)
directly from disk — for playthrough post-mortems and state debugging without launching the game.
Saves are a text header plus the whole Map object graph as FullSerializer JSON; the analyzer
resolves the $id/$ref/$type reference model itself, so it needs no game assemblies.
(*.mapsv files are scenario scripts, not saves.)
dotnet build src/SaveCli -c Release
alias savecli='dotnet src/SaveCli/bin/Release/net10.0/savecli.dll'
savecli list # saves, newest first (--dir to override the folder)
savecli summary quicksave # turn, god, panic, counts, agents, social groups
savecli inspect quicksave 'locations[4].settlement' --depth 2 # same path syntax as the inspect tool
savecli raw quicksave 'overmind' # verbatim subtree, $refs left unresolvedLocation/person/social-group indices in the output equal the live L#/P#/SG# ids used by the
MCP tools, so save output cross-references playthrough transcripts. U#/C# ids are per-session
and not recoverable from a save.
The engine (src/SaveAnalysis/) is net472-compatible and depends only on src/Core/Json, so it
can later be compiled into the mod to expose list_saves / save_overview / inspect_save MCP
tools (register via RegisterServerThread, no live-map guard) if in-game save analysis turns out
to be useful.
The release version lives in one place: <Version> in src/Mod/ShadowsMCP.csproj. It flows
automatically into the DLL, into serverInfo.version (MCP initialize), into the modVersion
field of the game_overview tool (so a connected client can confirm which build it's talking to),
and into the Workshop description (build.sh stamps Build X.Y.Z). Per release, while pre-1.0:
- Bump
<Version>insrc/Mod/ShadowsMCP.csproj(semver:0.MINOR.PATCH). ./build.shand sanity-check.- Commit, then tag:
git tag vX.Y.Z && git push --tags. - Publish to the Workshop — see
docs/modding-tutorial.md§8:build.shassembles the upload layout atdist/upload/ShadowsMCP/; copy it into the game'smodUploadFolder/and publish in-game.
docs/game-data-model.md— the game's internal data model, class by classdocs/modding-tutorial.md— how to mod Shadows of Forbidden Gods, from zero to this moddocs/manual-test-checklist.md— end-to-end test script for the moddocs/mcp-extension-guide.md— how a content mod advertises its gods/tips/popups to the MCP