🕹️ Agents Plugins
The CS Modding marketplace (csmodding) of agent plugins for Claude Code and OpenAI CodexCLI.
Give your agent eyes and hands inside a runningCoherent Gameface game UI.Generic tooling: works with any game or application embedding Gameface, not justCities: Skylines II.
Install · See it in action ·MCP Tool reference ·Gameface MCP npm package
The marketplace hosts one plugin, coherent-gameface: a generic toolkit for driving arunning Coherent Gameface UI, whatever the game or application embedding it.
Coherent Gameface (Cohtml) is theHTML/CSS/JS UI engine many games embed.The plugin ships gameface-devtools-mcp, an MCPserver that lets your agent drive any Gameface UI over a direct Chrome DevTools Protocol (CDP)connection: evaluate JavaScript, take screenshots, inspect and drive the DOM, capture the console,and even set JS breakpoints.
It also ships two skills.
- The
gamefaceskill teaches the agent the engine itself: how Gameface is, and is not, abrowser; version-gating features against the changelog; data binding; and how to probe therunning game for what the docs cannot promise. - The
gameface-drivingskill is the operating manual for thegame_*tools: field-verifiedprocedures for finding elements, acting then verifying, surviving reloads, and debugging withoutfreezing the game; the traps the tool schemas cannot tell.
[!NOTE]Generic, but developed against Cities: Skylines II. The server makes no assumptions about aspecific application; it works against any Gameface CDP endpoint. It is developed and verifiedagainst Cities: Skylines II's Gameface UI, which is the reference target.
[!TIP]Not using Claude Code or Codex CLI? The MCP server is also published on npm as
@csmodding/gameface-devtools-mcpand works with any MCP client (Cursor, Gemini CLI, VS Code, …).Seemcp/README.mdfor the tool reference andper-client install snippets.
Table of contents
- What it looks like in practice
- Requirements
- Install
- Claude Code
- Codex CLI
- Configuration
- Development
- Troubleshooting
What it looks like in practice
Short, abridged transcripts. The skill supplies the engine knowledge; the server supplies thehands.
"My panel looks right in Chrome but wrong in-game"
You: My settings panel is a two-column grid in Chrome, but in the game everything stacksinto one column. Why?
Agent: Gameface has no CSS Grid in any version, and unsupported CSS is silently ignored:your
display: gridjust falls back to the default, and in Gameface every element defaults todisplay: flexin column direction, hence the single column. I rewrote the layout with nestedflex rows, and agame_screenshotof.settings-panelconfirms the two columns now renderin-game.
"The UI stops updating after the first refresh"
You: My mod pushes stats to the UI every second, but the panel freezes on the first value.No errors anywhere.
Agent:
game_consoleshows no exceptions, andgame_evalon the bound nodes shows theyare stuck on the first payload. This is a known engine gotcha: Cohtml caches a model's shape byits type name, and two payloads sharing a type name must expose the same set of PRESENTproperties. Your second update omitsdeltawhen nothing changed; an omitted property is adifferent shape than a null one, and the cached binding breaks silently. Senddelta: nullinstead. Verified in-game across two refresh cycles.
"Test the new toolbar button end to end"
You: I just built the milestones toolbar button. Can you verify the whole flow in therunning game?
Agent:
game_clickon[data-tooltip="Milestones"]: Gameface accepts CDP input commandsbut never delivers them to the UI, so the click is dispatched as real bubbling DOM events,which your React handlers receive.game_waituntil.milestones-panelis visible,game_screenshotto check the render,game_consoleshows no new errors. The flow works;screenshot attached.
"Something throws when the trade panel opens, no idea where"
You: Opening the trade panel logs
TypeError: cannot read 'income' of undefinedand Icannot reproduce it outside the game.Agent:
game_consolecaptures the stack, pointing attrade-panel.js:412.game_debug_set_breakpointthere with the conditiondistrict == null, thengame_clickonthe trade button. It pauses; the UI thread is frozen while paused, so I inspect quickly:game_debug_pause_stateshowsdistrictis undefined exactly when the row is an outsideconnection, thengame_debug_step resume. Fix: guard the outside-connection case, which has nodistrict.
Requirements
- A Gameface application running with its CDP debug endpoint reachable (default
http://localhost:9444). Verify with:
You should get back a JSON array containing acurl http://localhost:9444/json/list"type": "page"target. Set the host/port to matchyour application if it differs (see Configuration). - Node 22.4+ to launch the server.No
npm installis needed: the plugin launches the server from a committed, self-containedbundle, so it works offline and stays version-locked to the plugin.
Install
Both harnesses add this repository as a marketplace, install the plugin from it, and launch thesame committed server bundle. The full tool reference (UI tools and JS debugger tools) lives inmcp/README.md.
Claude Code
In Claude Code, add this repository as a marketplace, then install the plugin from it:
/plugin marketplace add CitiesSkylinesModding/agents-plugins
/plugin install coherent-gameface@csmodding
Or from your terminal:
claude plugin marketplace add CitiesSkylinesModding/agents-plugins
claude plugin install coherent-gameface@csmodding
Once enabled, Claude Code autoloads the gameface MCP server from the plugin's.mcp.json.Run /mcp to confirm it connected, then Claude will use this MCP when it needs it.You can ask it to call game_status to check the MCP is working properly.
Codex CLI
Add this repository as a marketplace, then install the plugin from it:
codex plugin marketplace add CitiesSkylinesModding/agents-plugins
codex plugin add coherent-gameface@csmodding
Once enabled, Codex autoloads the gameface MCP server from.codex-plugin/mcp.json.Run /mcp to confirm it connected, then Codex will use this MCP when it needs it.You can ask it to call game_status to check the MCP is working properly.
Configuration
The server reads these environment variables (all optional):
| Variable | Default | Purpose |
|---|---|---|
GAMEFACE_HOST |
localhost |
Host of the Gameface CDP endpoint. |
GAMEFACE_PORT |
9444 |
Port of the Gameface CDP endpoint. |
GAMEFACE_CONNECT_TIMEOUT_MS |
5000 |
HTTP discovery / WebSocket open timeout. |
GAMEFACE_CALL_TIMEOUT_MS |
15000 |
Per-command reply timeout. |
On Claude Code, the plugin's .mcp.json forwards themfrom your environment(${VAR:-default}), and an extra GAMEFACE_MCP_RUNTIME variable (default node) overrides theruntime used to launch the server.
On Codex CLI, the plugin config passes no environment block (Codex does not interpolate${VAR} placeholders, and ~/.codex/config.toml cannot override a plugin-provided server), so theserver always starts with the defaults above. If you need non-default settings, register thenpm-published server manually with codex mcp add and the environment you want; it replaces theplugin's copy under the same name (see mcp/README.md).
Development
Uses mise + bun (never npx). The repository is a marketplace hosting the plugin(s) underplugins/, and a bun workspace: the root package.json carries the lint/format tooling (oxlint,oxfmt) and the lefthook git hooks, while the MCP server lives in theplugins/coherent-gameface/mcp/ workspace package (published on npm as@csmodding/gameface-devtools-mcp).
bun install # install all workspace deps (also installs the git hooks)
mise check # type-check, lint (with safe auto-fixes), and format
mise build # rebuild the server bundle (commit the result)
Run mise tasks for the full list.
After changing anything under plugins/coherent-gameface/mcp/src/, run mise check, rebuild, andcommit the updated dist/server.mjs.
Troubleshooting
/mcpshows the server failed / tools error with "Cannot reach …": the Gameface applicationis not running or the debug port is not reachable. Checkcurl http://localhost:9444/json/list.Usegame_statusfor a structured diagnosis.- Runtime not found: ensure
node(22.4+) is on yourPATH. - Read the MCP server logs: Claude Code records each server's connection attempts and capturedstderr to per-project
.jsonlfiles, the fastest way to see why a launch failed (e.g., a-32000 Connection closedfrom a bad command/path before anygame_*tool runs). They live underthe Claude CLI cache, in anmcp-logs-gameface/folder keyed by the project path (separatorsreplaced with-); newest.jsonlfirst, and eachServer stderr: ...line is what the serverprinted:- Windows:
%LocalAppData%\claude-cli-nodejs\Cache\<project-path>\mcp-logs-gameface\ - macOS / Linux:
~/.cache/claude-cli-nodejs/Cache/<project-path>/mcp-logs-gameface/
- Windows: