s2docs MCP Server
Summary: The s2docs MCP server is a verified Source 2 (CS2/Deadlock) knowledge and workbench server — it hosts searchable game-build data plus tools to author, validate, build, deploy and test addons on remote hosts.
Browse game data — list games and builds (
list_games), check server/DB identity (server_info), and full-text search everything (search_docs) with results keyed to detail tools.Look up ConVars/commands —
get_convar,list_convarswith defaults, ranges, flags and help text.Inspect Hammer entities —
get_entity(keyvalues, inputs/outputs, inheritance, Valve FGD labels/defaults/choices) andfind_entities_with_io.Explore engine schema —
get_schema_class,list_schema_classesfor classes/enums, fields, metadata, netvars and VData keys.Read game events —
get_game_event,list_game_eventswith core/game/mod override resolution.Query cs_script API —
get_script_api,list_script_typesfor the point_script JS API and inherited members.Search Pulse bindings —
search_pulsefor visual-scripting node inputs/outputs/functions.Diff builds —
diff_buildsshows member-level changes between two indexed builds (convars, entities, schema, events, script, pulse, Hammer classes).Use examples and community docs —
list_examples/get_examplefor Valve sample scripts;list_docs/get_docfor source2.wiki pages with attribution.Validate code and configs —
validate_cs_scripttype-checks JS against officialpoint_script.d.ts;validate_cfgchecks every cfg line against indexed convars.Author addons —
scaffold_addon,list_addons,get_addon,read_addon_file,write_addon_file, andbuild_modelto write .vmdl documents.Orchestrate multi-step projects —
list_workflows,start_project,next_step,update_step,get_project,list_projects,get_prompt_text, with server-enforced quality gates.Manage test/build hosts — register, probe, and lease hosts via
claim_host/heartbeat_host/release_hostwith STUCK detection.Run remote builds —
build_start/build_status/build_log/build_wait/build_cancel/build_list, including UTF-16 log decoding and material-guard warnings.Deploy and verify —
deploy_push(MD5-verified copy over SSH),deploy_verify,deploy_list.Record evidence —
add_evidence,list_evidenceto satisfy project-step evidence gates.Track live test sessions —
test_start/test_step/test_finish/test_clear/test_status/list_test_runsmirroring the in-game TEST STATUS HUD.Run a job ledger — enqueue, queue/assign, drive, attach screenshots, record human verdicts, and close out jobs with mandatory summaries (
job_*tools).
Provides verified reference data for Counter-Strike 2 (and its cs_script API), letting agents look up and search console variables/commands, Hammer entity keyvalues, inputs and outputs, schema classes and enums, game events, Pulse bindings, official Valve declarations and sample scripts so they can write cs_script, Hammer logic, configs and mods using real names.
Serves searchable documentation extracted from Valve's Source 2 game builds, exposing Valve-authored assets (point_script.d.ts, Hammer FGD classes, sample scripts, game events) as MCP tools, prompts and resources for building game logic, configs and mods for Valve's Source 2 titles.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@s2docs MCP Server list the Hammer inputs for func_button in CS2"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
s2docs — Source 2 API/CLI docs + MCP
One searchable place for the undocumented surface of Valve's Source 2 games (Counter-Strike 2, Deadlock). Everything is extracted from each game build, then served three ways:
Web UI: a light vanilla-JS SPA for browsing and searching, plus operations pages (Hosts, Activity) for watching tests on remote hosts without SSHing anywhere, and an Orchestration page for the server's multi-step plans.
JSON API: FastAPI, with OpenAPI docs at
/docs.MCP server: tools, prompts and resources so coding agents can write cs_script, Hammer logic, configs and mods using verified names.
Data | CS2 | Deadlock | Source |
Console variables / commands | 4,327 / 1,246 | 4,859 / 1,421 |
|
Hammer entities (keyvalues, inputs, outputs) | 857 | 2,181 |
|
Schema classes / enums (fields, metadata, inheritance) | 3,753 / 634 | 9,264 / 880 |
|
Game events | 273 | 196 |
|
Pulse bindings | 2,488 | 2,646 |
|
cs_script API symbols | 517 | — |
|
Valve Hammer FGD classes (labels, help, defaults, choices) | 430 (63 editor-only) | — |
|
Valve sample scripts | 7 | — |
|
Community docs | 185 pages (all games) | source2.wiki (CC BY-SA 4.0) |
Where the data comes from
Source | Authored by | How we get it |
| Valve, copied verbatim from the game install | SteamTracking GameTracking mirrors the files on every patch |
Convars/commands, runtime entity lists, schema, Pulse bindings | Valve's binaries, extracted with DumpSource2 | Same GameTracking repos ( |
Wiki pages | source2.wiki community, CC BY-SA 4.0 | Shallow, sparse git clone of Source2Wiki/Source2Wiki |
No game install is needed. Entity lookups merge both entity sources. Every member is tagged runtime (registered by the binary), valve_fgd (an editor-only key from Valve's FGD) or both. The Valve Developer Community wiki is not ingested: it sits behind an anti-bot proof-of-work wall (Anubis) that deliberately blocks scripted clients. See RESEARCH.md for the full source survey.
Quick start
uv sync
uv run s2docs sync # sparse-clone GameTracking (~25 MB/game) and build data/s2docs.db (~5 s)
uv run s2docs serve # http://127.0.0.1:8000 (UI) · /docs (OpenAPI) · /mcp (MCP over HTTP)Other commands:
uv run s2docs sync --game cs2 # one game
uv run s2docs sync --no-fetch # re-index the existing checkout (e.g. after parser changes)
uv run s2docs sync --docs-only # only refresh source2.wiki (--skip-docs to skip it)
uv run s2docs sync --check # is the index older than the checkout? (read-only; --fail-if-stale exits 6)
uv run s2docs load cs2 /path/to/GameTracking-CS2 # index a full local clone
uv run s2docs stats
uv run s2docs prompt cs2_script task="teleport players who touch the lava"
uv run s2docs mcp # MCP over stdioEnvironment variables
Variable | Default | Purpose |
| the checkout's | Directory holding the index, workspace and captured logs. Never relative to the cwd (see below). |
|
| The SQLite index file. |
|
| Local addon sources written by the build tools. |
| unset (auth off) | When set, mutating |
|
| Phase age (seconds) after which a fresh host lock reads STUCK. |
| built-in per-phase defaults | JSON object of per-phase stuck thresholds, merged over the built-ins. |
|
| Seconds between orphan-run reaper passes ( |
The data dir never depends on the working directory. With S2DOCS_DATA_DIR unset the default is a fixed path: a source checkout uses its own ./data, and anything else uses a per-user directory. This closes the split-database incident — a server and an MCP client that were launched from different working directories (an MCP config that predates the env fix spawns s2docs mcp with cwd set to the client's profile dir) used to read and write two different stores, leaving a shadow DB next to an unrelated cwd. serve and mcp still print a loud stderr warning when the variable is unset, now naming the resolved path. S2DOCS_DB and S2DOCS_WORKSPACE overrides are unchanged.
Auth is opt-in. Set S2DOCS_AUTH_TOKEN and the server requires Authorization: Bearer <token> or X-S2docs-Token: <token> on every non-GET/HEAD/OPTIONS request under /api and on every request to the HTTP MCP endpoint /mcp; a missing or wrong token is 401 (compared in constant time). GET routes and the static web UI stay open, and stdio MCP is unaffected (it is local). With the variable unset the behaviour is unchanged — no auth — and GET /api/health and the MCP server_info tool report auth_required: false (they report only the boolean, never the token).
deploy_push takes the host lease. A deploy now arbitrates the host through the same lock the build and test services use, so it can never copy onto a host another owner holds. Pass owner if you already hold the lease — the push reuses it and leaves it locked; otherwise a free host is claimed as deploy:<id> for the copy+verify window and released afterwards (owner-conditional, even on failure), and a host held by a different owner is a 409 (HostLocked). The effective owner is recorded on the deployment row. The deploy_verified orchestration gate no longer falls back to the host's latest deployment: the step must name the deployment id from deploy_push as an artifact, and it must belong to the project's host and be verified.
Related MCP server: open77-devkit
Web UI
s2docs serve mounts a small vanilla-JS SPA at / (hash routes, no framework, no build step). Besides the browse/search sections (#/<game>/…) it has install-wide operations pages for watching work on remote hosts, and a page for the server's orchestration plans:
Overview (
#/overview) — a mission-control wallboard, not a copy of the four pages: a hero card for the one thing happening right now (running test leads, then builds, then the queue), a machine dock (one LED chip per host — pulsing gold = busy, red = STUCK, gray = idle), and three lanes: the queue rail, the verdict wall (one colour-coded tile per job, newest first, like a CI status board) and a compact feed of the newest runs. Read-only; every tile/line links to its record. Same auto-refresh cadence as Activity.Hosts (
#/hosts) — the registry plus live lock state: shell, GPU (from the last probe), how long ago it was probed, and the current lock owner with fresh/stale + heartbeat age, its phase (what the owner is doing) and a STUCK badge. A lock is STUCK when its heartbeat is fresh but its phase has not moved for longer than the server's threshold — liveness is not progress, so a worker that heartbeats faithfully while its compile hangs still shows up. Clicking a host opens#/hosts/<id>with its facts JSON, lock history (claim / takeover / release / phase-change events) and its recent builds, deployments and test runs. Read-only; auto-refreshes every 5 s while open.Activity (
#/activity) — the page to watch instead of tailing logs: a Now section (the running test run — the samename / step / state / elapsedfields as the in-game TEST STATUS HUD strip — and active/queued build runs, freshest first), a By host section grouping every recent build run, test run and deployment under the host it ran on, and a collapsed full history. Auto-refreshes every 5 s while a run is active, 15 s when idle.Orchestration (
#/<game>/projects) — the multi-step orchestration DAGs (distinct from runs): each project opens to a step-level view with a dependency graph, and per step its state, gate, evidence counts, updated time and notes. For the build/test/deploy runs those steps drive, use Activity.Jobs (
#/jobs) — the job ledger + queue: one record per requestedtest/demo/finalrun (intake, timeline, screenshots/logs, links, and a mandatory close-out summary). A per-host queue board (running on top, then queued by position, error/crash counts in red);#/jobs/<id>shows the intake, the screenshot gallery, linked records and the summary card (first on a finished job). Jobs never claim the host lease — the build/test/deploy tools still do; a job links to them. Seedocs/TEST_JOBS.md.
Activity is service-fed. A row appears on that page only when work goes through the s2docs build / test / deploy services (build_start / test_start / deploy_push over MCP, or the equivalent s2docs CLI) — it is not a view of any CS2 that happens to be running anywhere, so an empty page means nothing has been recorded yet, not that the page is broken; the empty state says so and points at the commands to start something. All these pages read the existing JSON API (/api/hosts, /api/hosts/{id}/locks, /api/builds, /api/deploys, /api/test-runs, /api/projects, /api/projects/{id}, /api/evidence); nothing there can mutate a host or a run.
MCP
Hermes Agent (native tools as mcp_s2docs_*; stdio, no server process needed)
hermes config set mcp_servers.s2docs.command /Users/you/source/s2docs/.venv/bin/s2docs
hermes config set mcp_servers.s2docs.args '["mcp"]'Claude Code
claude mcp add s2docs -- uv --directory /Users/you/source/s2docs run s2docs mcp
# or, with `s2docs serve` running:
claude mcp add --transport http s2docs http://127.0.0.1:8000/mcpClaude Desktop / other stdio clients
{
"mcpServers": {
"s2docs": {
"command": "uv",
"args": ["--directory", "/Users/you/source/s2docs", "run", "s2docs", "mcp"]
}
}
}Docs tools (read-only) | |
| Full-text search over everything. Each hit's |
| Default, min/max, flags ( |
| Keyvalues (with choices), inputs and outputs including inherited ones, with |
| Fields, types, defaults, metadata, inheritance chain and subclasses (VData, netvars) |
| The effective definition after core → game → mod overrides |
| The cs_script API ( |
| Pulse visual-scripting node bindings |
| What changed between two indexed builds, at member granularity: one row per added/removed/changed schema field, entity member, game-event field, script symbol, convar, Pulse binding or Hammer class, grouped under its parent class/entity/event/library. Sections: convars, entities, schema, events, script_api, pulse, hammer_classes. |
| Valve's cs_script sample scripts, each with the API it uses |
| source2.wiki pages as Markdown, with URL and license for citation |
Build tools | |
| Type-checks a script with |
| Checks every cfg line against the indexed convars: unknown names (with suggestions), out-of-range values, cheat flags, client vs server |
| Local addon workspace ( |
Host tools | |
| Registry of test/build hosts (ssh target, shell flavor, probed facts: GPU/RAM/processes/CS2 build) |
| SSH-probe a host and store its facts ( |
| Server-side host locks with owner + TTL + heartbeat + phase. A fresh lock blocks other owners; a stale lock (crashed worker) is taken over automatically. Report a |
Stuck detection | The server, never the client, decides stuck-ness: a lock is |
The same operations exist on the CLI (s2docs host list|show|register|probe|lock|heartbeat|unlock, JSON out, exit 2 = locked; lock/heartbeat take --phase/--note, and host list/show print a STUCK: line on stderr) and the HTTP API (/api/hosts, /api/hosts/{id}/lock, …; 409 = locked; every host read carries lock.phase, lock.phase_age_sec and lock.stuck; /api/hosts/{id}/locks returns the claim/takeover/release/phase-change audit trail the Hosts page shows).
Build tools | |
| Start a server-owned run on a host: acquires the host lease ( |
| A run's record (status, exit code, parsed summary counts, material guard, artifacts) and recent runs |
| Tail the run's log — the log is UTF-16 (resourcecompiler), so this decodes it and reports the detected |
| Block server-side until the run finishes (bounded), or fail on timeout. |
Material guard | Scans the decoded log for |
| Mark the run cancelled and release its host lease |
Deploy tools | |
| Copy files to a host per a manifest ( |
| Re-check a recorded deployment's MD5s and update its status |
| Recent deployments, optionally per host |
CLI: s2docs build start|status|log|wait|cancel|list and s2docs deploy push|verify|list (global --json → one {"schema": "s2docs/v1", …} object on stdout, logs to stderr; exit codes 0 ok, 1 error, 2 locked, 3 not found, 6 partial, 7 timeout/cancelled, 8 material warnings under --strict-materials). HTTP: /api/hosts/{id}/builds, /api/builds/…, /api/deploys.
Evidence tools | |
| Store a structured evidence row for a project step: |
| Evidence rows (newest first), filtered by project, step and/or kind |
Test-run tools | |
| Open a server-side test session ( |
| Set the current step, finish |
| One run (or the current active run) and recent runs, optionally by state |
CLI: s2docs evidence add|list|show and s2docs test start|step|finish|clear|status|list. HTTP: /api/evidence, /api/evidence/{id}, /api/test-runs (+ /current, /{id}/step|finish|clear).
Job tools | |
| Enqueue a job (validated intake) — the |
| Jobs (newest first, with error/crash counts), one job with its timeline/links/artifacts, and the head of a host's queue when its lease is free |
| Set host/queue order, queued → running, and append timeline events (a |
| Copy a local file into the job's artifacts (append-only), or ask the runner for a screenshot |
| Attach a test run/build/deploy/evidence/project, close out (all four summary fields required; |
CLI: s2docs job enqueue|list|show|next|start|event|attach|shot-request|finish|restatus|cancel|watch (the global exit codes: 3 not found, 4 conflict, 9 validation). HTTP: /api/jobs (+ /next, /{id}, /{id}/assign|start|event|link|screenshot-request|finish|restatus|cancel, /{id}/artifacts/{name}), GET /api/artifacts/{job}/{name}. Every job event also appends <data_dir>/job_events.jsonl (/api/health → job_events_jsonl). Full notes: docs/TEST_JOBS.md.
Orchestration tools | |
| Multi-step recipes: |
| Creates a persistent project (stored in SQLite) and returns the first ready step(s) |
| Ready steps, each with instructions, the tools to use, rendered prompt text, and its gate |
|
|
| State, history, and prompt text for clients that can't fetch prompts mid-task |
Orchestration
The server holds the plan and the quality gates. The client's model does the work. Start with the orchestrate prompt ("build an isometric Diablo-style viewer for CS2"), or call start_project directly:
start_project ─▶ brief (ready steps + tools + rendered prompt) ─▶ agent works ─▶ update_step(done)
▲ │ gate check
└──────────────── next ready steps (dependency DAG) ◀──────┘Gates are checked on the server, not taken on trust:
scripts_typecheckre-runstscon every.jsfile in the addon,cfgs_validvalidates every.cfg,addon_exists, andevidence— which requires at least one stored non-note evidence row for the step (viaadd_evidence); notes and artifacts alone no longer pass.cs2_live_testaddshost_claimed(the project really holds the host lease, ownerproject:<id>) anddeploy_verified— the deployment id fromdeploy_push, recorded as an artifact of the step, must belong to the project's host and beverifiedin the deploy registry (there is no latest-for-host fallback, so a stale or unrelated deployment can never satisfy it). A failed gate returns the exact errors (HTTP 409 / MCP tool error).Steps form a DAG. Independent steps (e.g. camera script and test cfg) become ready together, so a client can run them in parallel or hand them to subagents.
Projects persist. Any session can pick one up with
next_step <id>, and the web UI's Orchestration page shows the step-level DAG, gates and evidence, and can start projects or move steps.Steps are snapshotted when a project starts, so editing
orchestration/workflows.pynever changes a project in flight.
Prompts: orchestrate, cs2_script, hammer_io_logic, server_config, deadlock_mod, cs2_map_rebuild, explain_symbol. Each is a Markdown template in src/s2docs/prompts/templates/, filled at render time with live data from the database (e.g. the current Instance method index). Edit the .md files to tune them. The same prompts are available at GET /api/prompts and POST /api/prompts/{name}/render.
Resources: s2docs://cs2/cs_script/point_script.d.ts and tsconfig.json (put them next to your scripts for editor IntelliSense), plus s2docs://cs2/cs_script/examples/{name}.
Architecture
src/s2docs/
config.py game registry (repo, mod dir, sparse patterns), settings
ingest/
sources.py shallow + blob-filtered + sparse git checkout
parsers/ pure parsers: convars, fgd, schema (.h), gameevents (KV1), pulse, d.ts, steam.inf
loader.py one transaction per build → normalized tables + FTS rows
db/
migrations/*.sql versioned schema (PRAGMA user_version)
connection.py read-only URI connections for readers, WAL for the writer
fts.py identifier splitting + injection-safe FTS5 query builder
repository.py every read query (parameterized, recursive CTEs for inheritance)
models.py Pydantic models shared by the API and MCP
prompts/ prompt registry + Markdown templates
api/ FastAPI app + routes (MCP mounted at /mcp, SPA at /)
build/ validate_script (tsc), validate_cfg, workspace (addon files)
orchestration/ workflows.py (registry) + engine.py (projects, DAG, gates)
hosts.py host registry + server-side leases (claim/heartbeat/release, TTL expiry) + lock-event audit (host_lock_events) for the Hosts page
builds.py server-owned build runs (SSH launch, UTF-16 log tail, host lease)
deploys.py deployment registry (scp push + Get-FileHash MD5 verification)
evidence.py structured evidence store — what the `evidence` gate checks (kind/ref/data)
test_runs.py in-game test sessions mirroring the TEST STATUS HUD strip (start/step/finish/off)
mcp_server.py MCP tools/prompts/resources (mcp SDK 2.x MCPServer)
web/ index.html, app.js, style.css (no build step)SQLite design
Every data table hangs off
builds(id)withON DELETE CASCADE. A whole build is written in one transaction, so readers see either the old build or the complete new one. Re-indexing the same build number replaces it. The last N builds are kept (--keep, default 5) fordiff_builds.search_ftsis an FTS5 index over the current build of each game.namekeeps identifiers whole (_is a token character, somp_round*prefix-matches).name_partsand the body hold split forms (m_iHealth→m i health), so word searches find identifiers. Results are ranked by bm25 with column weights, with exact name matches first.User input never becomes FTS syntax: it is reduced to quoted tokens and bound as a parameter.
LIKEfilters escape%and_. The only dynamic SQL is placeholder lists and fixed column names.Entity and schema inheritance are resolved with recursive CTEs.
row_number()keeps the nearest definition of an overridden member.The API and MCP read docs data read-only (
mode=ro), one connection per request or tool call. Only orchestration (projects,project_steps,project_events) writes, through the WAL writer connection.
Development
uv run pytest -q # 115 tests: parsers, repository, API, MCP, build tools, orchestration, hosts, builds/deploys (incl. material guard), evidence, test runs, web-UI visibility
uv run ruff check src tests && uv run ruff format src tests
uv run python scripts/check_frontend.py # renders every SPA route in headless Chrome (server on :8765; extra routes as args)Roadmap
Human docs: load the Valve Developer Community (MediaWiki API), source2.wiki (git), and the CounterStrikeSharp and Deadworks docs into the existing
docstable, with license and source URL per page.Scheduled sync: cron or CI running
s2docs sync. It's cheap because the checkouts are incremental.Deeper Deadlock data: hero, ability and item VData values (KV3) from the VPKs via the Source 2 Viewer CLI.
Semantic search: embeddings alongside FTS5 for natural-language questions.
Available Tools
73 toolsadd_evidenceA
Record structured evidence for a step. This is what the evidence gate checks — a step with an
evidence gate CANNOT be marked done until at least one non-note row exists for it. Use kind='note'
only for a supplementary remark (it does not satisfy the gate).
Pass `step_id` to attach the row to a project step, or omit it to record evidence at project
level (a measurement, a console transcript, a negative result). A step-less row satisfies no
step gate, by design.Inputs: kind (required), project_id (required), ref, data, +1 more. Returns: id, ref, data, kind, +4 more
build_cancelADestructiveIdempotent
Cancel a run: mark it cancelled and release its host lease (terminates the process when this server owns it).
Inputs: run_id (required). Returns: id, kind, lock, notes, +13 more
build_listARead-onlyIdempotent
Recent build runs (newest first), optionally one host and/or active only.
Inputs: active, host_id. Returns: result
build_logARead-onlyIdempotent
Tail a build run's log. The log is UTF-16 (resourcecompiler) — this decodes it for you,
returning the detected encoding, whether the tail was truncated, and any MATERIAL GUARD
warnings found in the full log (see build_status).
Inputs: run_id (required), lines. Returns: size, lines, run_id, encoding, +2 more
build_modelA
Write a Source 2 model document (.vmdl) for a mesh that is already inside the addon.
`mesh` is addon-relative (e.g. models/probe/probe.dmx). The document copies the shape the
game's own compiler accepts — compile it on a host with resourcecompiler afterwards
(build_start). Returns the written path. Use overwrite=true to replace an existing .vmdl.Inputs: mesh (required), name (required), addon (required), game, +2 more. Returns: result
build_startADestructive
Start a server-owned build run on a host: acquires the host lease, runs the command over SSH and
returns a run id immediately. Do NOT poll with sleeps — use build_wait(run_id) or build_status. The
host lease is released automatically when the run reaches a terminal status. The lease's phase is
the run kind (e.g. 'compile'), so a run whose compile hangs while its heartbeat stays fresh reads
STUCK on list_hosts — the server, not the client, decides that.
Inputs: command (required), host_id (required), kind, notes, +1 more. Returns: id, kind, lock, notes, +13 more
build_statusARead-onlyIdempotent
One build run's record: status, exit code, parsed summary counts, material guard and artifacts.
`warnings` carries the MATERIAL GUARD findings when the log referenced missing/reserved materials
(Valve's `_vrf/` subtree, or another map's `maps/<other>/` subtree): counts per kind plus the first
few offending paths. Such a run can still be 'succeeded' — the resourcecompiler exits 0 and the map
then error-renders as a red/black checkerboard, so treat a non-None `warnings` as NOT clean.Inputs: run_id (required). Returns: id, kind, lock, notes, +13 more
build_waitARead-onlyIdempotent
Block (server-side, no client sleep loop) until the run finishes, then return its record. Raises if it is still running after timeout_sec. With strict_materials, a run whose log has material-guard warnings is an error even though its status is 'succeeded'.
Inputs: run_id (required), timeout_sec, strict_materials. Returns: id, kind, lock, notes, +13 more
claim_hostAIdempotent
Claim exclusive use of a host. Fails if another owner holds a fresh lock — a stale lock
(heartbeat older than its TTL, e.g. from a crashed worker) is taken over automatically.
Always report a phase (what you are doing); the server flags a lock STUCK when its heartbeat
is fresh but the phase has not moved for too long, so keep the phase current (pass a new phase
to heartbeat_host) as you progress. Heartbeat while you work; release with release_host when done.
Inputs: owner (required), host_id (required), note, force, +2 more. Returns: note, fresh, owner, phase, +9 more
deploy_listBRead-onlyIdempotent
Recent deployments (newest first), optionally one host.
Inputs: host_id. Returns: result
deploy_pushADestructive
Copy files to a host and verify what landed (Get-FileHash over SSH). Returns a deploy id whose entries carry the server-checked md5 + verified flags. Call deploy_verify later to re-check.
The push takes the host lease for the copy+verify window: a host held by another owner is a
conflict (pass that owner if it is you), a free host is claimed as 'deploy:<id>' and released
when the copy finishes. Inside an orchestration project the project already holds the lease as
'project:<project_id>', so pass that as `owner` (the `cs2_live_test` deploy step does).Inputs: files (required), host_id (required), dest_root (required), addon, +4 more. Returns: id, addon, notes, owner, +7 more
deploy_verifyBIdempotent
Re-check a recorded deployment's MD5s on the host and update its status.
Inputs: deploy_id (required). Returns: id, addon, notes, owner, +7 more
diff_buildsARead-onlyIdempotent
Show what changed between two ingested builds, at member granularity: one row per added/
removed/changed schema field, entity member, game-event field, script symbol, convar, Pulse
binding or Hammer class. Each entry carries group (the parent class/entity/event/library) so
members cluster under it; before/after hold only that member's own value. counts gives the
exact per-section added/removed/changed breakdown ({section: {added, removed, changed}}), taken
BEFORE the per-section limit cap, so it stays true even when sections is truncated to the
first limit entries; totals[section] is the sum of that section's three counts. Use it after
a game patch to find code or configs that might break.
Inputs: game (required), to_build, from_build. Returns: game, limit, counts, totals, +3 more
find_entities_with_ioARead-onlyIdempotent
Find entity classes (including base classes) that declare an input or output with this name. Subclasses inherit it too, so check candidates with get_entity.
Inputs: game (required), name (required), side, limit. Returns: items, limit, total, offset
get_addonBRead-onlyIdempotent
Show an addon's local root and file list.
Inputs: name (required), game. Returns: game, name, root, files
get_convarARead-onlyIdempotent
Get a console variable or command: default, min/max, flags (cheat, replicated, ...) and help text.
Inputs: game (required), name (required). Returns: kind, name, flags, max_value, +3 more
get_docARead-onlyIdempotent
Get a community documentation page (source2.wiki) as Markdown, with its URL and license for citation.
Inputs: key (required). Returns: key, url, body, game, +4 more
get_entityARead-onlyIdempotent
Get a Hammer entity: keyvalues (with choices), inputs and outputs, including inherited ones.
defined_in shows which base declares each member.
Inputs: game (required), classname (required), side, include_inherited. Returns: side, bases, origin, members, +10 more
get_exampleARead-onlyIdempotent
Get the full source of an example script. Prefer adapting these Valve-written patterns.
Inputs: name (required), game. Returns: api, name, path, origin, +2 more
get_game_eventARead-onlyIdempotent
Get a game event's fields and types (the effective definition after core/game/mod overrides).
Inputs: game (required), name (required). Returns: name, fields, overrides, description, +1 more
get_hostBRead-onlyIdempotent
One host's registry entry: ssh target, shell flavor, probed facts, and lock.
Inputs: host_id (required). Returns: id, os, lock, facts, +6 more
get_projectBRead-onlyIdempotent
Full project state: every step's status, notes, artifacts and recent events.
Inputs: project_id (required). Returns: id, game, goal, addon, +9 more
get_prompt_textARead-onlyIdempotent
Render one of this server's prompts as text, for clients that can't fetch MCP prompts mid-task.
Inputs: name (required), arguments. Returns: result
get_schema_classARead-onlyIdempotent
Get an engine schema class or enum: fields with types, defaults and metadata, the inheritance chain and direct subclasses. Use it for networked fields, VData keys (Deadlock abilities) and plugin offsets.
Inputs: game (required), name (required), module, include_inherited. Returns: kind, name, bases, fields, +6 more
get_script_apiARead-onlyIdempotent
Look up the CS2 cs_script (point_script) JavaScript API. A type name returns the type with all
members, including inherited ones. Anything else returns the matching members with signature and docs.
Instance is the Domain type.
Inputs: symbol (required), game. Returns: result
heartbeat_hostAIdempotent
Refresh your lock's heartbeat so it doesn't go stale mid-task. Heartbeating the SAME phase
does not clear stuck-ness (that is the point); report a new phase as your work moves on.
Inputs: owner (required), host_id (required), note, phase. Returns: note, fresh, owner, phase, +9 more
job_assignBIdempotent
Set (or move) a queued job's host and queue order.
Inputs: job_id (required), host_id (required), position. Returns: id, kind, links, phase, +21 more
job_attachA
Copy a local file into the job's artifacts (MCP runs on the Mac). Append-only: no overwrite.
Images also record a screenshot event automatically.
Inputs: path (required), job_id (required), name. Returns: md5, url, name, size
job_cancelA
queued/running -> cancelled; the reason is recorded as a timeline event.
Inputs: job_id (required), reason. Returns: id, kind, links, phase, +21 more
job_enqueueA
Enqueue a job (validated intake -> queued). The intake is the gate: every missing required field
comes back in one error with a field list. Set host_choice='ask' to queue unassigned.
Inputs: kind (required), title (required), target (required), purpose (required), +14 more. Returns: id, kind, links, phase, +21 more
job_eventA
Append one timeline event. phase updates the job's current phase; error/crash require a
message and SHOULD carry data.log_tail so the job page can expand the failure in a .
Inputs: level (required), job_id (required), message (required), data, +1 more. Returns: id, data, level, phase, +3 more
job_finishA
Close out a running/queued job. Gate: summary must carry what, why, learned and next, each
non-empty and <=600 chars; otherwise the call is rejected with the offending field names.
status='inconclusive' is the honest close-out for a run that ended without a pass/fail verdict
(a step-gated demo the user stopped answering) — it is NOT a pass.
Inputs: job_id (required), status (required), summary (required). Returns: id, kind, links, phase, +21 more
job_getBRead-onlyIdempotent
One job with its full timeline (events), links to test runs/builds/deploys/evidence and artifacts.
Inputs: job_id (required). Returns: id, kind, links, phase, +21 more
job_linkBIdempotent
Attach a canonical record the job is about (a test run, build, deploy, evidence row or project).
Inputs: kind (required), job_id (required), ref_id (required). Returns: id, kind, links, phase, +21 more
job_listBRead-onlyIdempotent
Jobs newest first. status='active' means queued + running. Each row carries error/crash counts.
Inputs: limit, status, host_id. Returns: result
job_nextARead-onlyIdempotent
The head queued job for a host, only when that host's lease is free. Errors (with the holder) when a fresh lock blocks it, and when the host's queue is empty.
Inputs: host_id (required). Returns: id, kind, links, phase, +21 more
job_request_screenshotA
Ask the runner for a screenshot: an info event plus params.pending_shot=true. The runner
polls the job and answers by uploading an artifact, which clears the flag.
Inputs: job_id (required), note. Returns: id, kind, links, phase, +21 more
job_restatusA
Correct a FINISHED job's terminal status (terminal -> terminal), for a mis-recorded close-out
— e.g. a run that finished 'passed' but never got a verdict. reason is mandatory and lands as a
milestone timeline event. The job must already be terminal (a queued/running job finishes or is
cancelled) and the target must differ from the current status, else the call is a conflict.
Inputs: job_id (required), reason (required), status (required). Returns: id, kind, links, phase, +21 more
job_screenshotsARead-onlyIdempotent
A job's screenshots tied to their step and verdict (seq, step, reason, url, web_url, verdict,
dimensions) — the image context a reviewer pulls after a run. Filter by step/reason.
Inputs: job_id (required), step, reason. Returns: result
job_startB
queued -> running, recording the worker and started_at. A second call is a conflict.
Inputs: job_id (required), worker (required). Returns: id, kind, links, phase, +21 more
job_verdictA
Answer the running job's current step with a human verdict (pass | fail | skip). Writes a
human-confirm evidence row through the same path as POST /api/evidence (so the confirm push
files fire) and appends a milestone event. The job must be running and link a test run, else
the call is a conflict.
Inputs: job_id (required), verdict (required), step, notes, +1 more. Returns: id, kind, links, phase, +21 more
list_addonsBRead-onlyIdempotent
List addons in the local workspace.
Inputs: game. Returns: result
list_convarsBRead-onlyIdempotent
List console variables/commands, filtered by substring and/or flag.
Inputs: game (required), flag, kind, limit, +2 more. Returns: items, limit, total, offset
list_docsARead-onlyIdempotent
Browse community documentation pages (titles, URLs) without bodies.
Inputs: game, limit, query. Returns: items, limit, total, offset
list_evidenceARead-onlyIdempotent
List evidence rows (newest first), optionally filtered by project, step and/or kind.
Inputs: kind, step_id, project_id. Returns: result
list_examplesARead-onlyIdempotent
List example scripts (Valve's cs_script_demo samples) with the API each one uses.
Inputs: game. Returns: result
list_game_eventsBRead-onlyIdempotent
List game events, optionally filtered by a substring of the name or description.
Inputs: game (required), limit, query, offset. Returns: items, limit, total, offset
list_gamesARead-onlyIdempotent
List supported games with their currently indexed build and patch version.
Inputs: no named parameters. Returns: result
list_hostsARead-onlyIdempotent
List registered test/build hosts with their facts (shell, GPU, paths) and current lock state.
CHECK THIS before driving any host: a fresh lock means another session/workstream owns it.
Each lock carries phase (what the owner is doing), phase_age_sec and stuck — the SERVER
marks a lock STUCK when its heartbeat is fresh but the phase has not moved past the configured
threshold (liveness is not progress). Treat lock.stuck=true as "the owner may be hung".
Inputs: no named parameters. Returns: result
list_projectsBRead-onlyIdempotent
List orchestration projects (most recently updated first).
Inputs: status. Returns: result
list_schema_classesBRead-onlyIdempotent
List schema classes/enums by name substring, module, kind or declared field.
Inputs: game (required), kind, limit, query, +3 more. Returns: items, limit, total, offset
list_script_typesARead-onlyIdempotent
List every top-level cs_script type: the Instance const, classes, interfaces, enums and type aliases.
Inputs: game. Returns: result
list_test_runsBRead-onlyIdempotent
Recent test sessions (newest first), optionally filtered by state.
Inputs: state. Returns: result
list_workflowsARead-onlyIdempotent
List orchestration workflows: multi-step recipes (research → design → build → validate → test → document) that chain this server's tools and prompts. Start one with start_project.
Inputs: game. Returns: result
next_stepARead-onlyIdempotent
Get the ready step(s) of a project: instructions, tools to use, rendered prompt text and the gate the server will check when the step is marked done.
Inputs: project_id (required). Returns: game, goal, addon, title, +6 more
probe_hostAIdempotent
SSH to the host, run the probe script (GPU, RAM, processes, CS2 path/build) and store the facts. Use before planning work on a host — e.g. to see if a game or a heavy GPU process is running.
Inputs: host_id (required). Returns: id, os, lock, facts, +6 more
read_addon_fileBRead-onlyIdempotent
Read a text file from an addon in the workspace.
Inputs: name (required), path (required), game. Returns: result
register_hostAIdempotent
Register (or update) a test/build host. Upsert: omitted fields keep their previous values.
Inputs: host_id (required), ssh_target (required), os, notes, +3 more. Returns: id, os, lock, facts, +6 more
release_hostAIdempotent
Release a host lock. Always call this when finished with a host.
Inputs: owner (required), host_id (required), force. Returns: result
scaffold_addonA
Create a new addon in the local workspace (content/csgo_addons// with scripts/, maps/, materials/, ...), a starter cs_script, Valve's point_script.d.ts + tsconfig.json, and a README with the Hammer setup steps. Fails if the addon already exists.
Inputs: name (required), game, with_script. Returns: game, name, root, files
search_docsARead-onlyIdempotent
Full-text search across everything. Each hit's key feeds the detail tool for its kind:
convar/command→get_convar(name), entity→get_entity('side:classname'), schema_class/schema_enum→
get_schema_class('module:Name'), event→get_game_event, script→get_script_api, pulse→search_pulse,
example→get_example(key), doc→get_doc(key). With game set, general docs are included too.
Inputs: query (required), game, kinds, limit. Returns: items, limit, total, offset
search_pulseCRead-onlyIdempotent
Search Pulse (Valve's visual scripting) node bindings: entity inputs/outputs and functions.
Inputs: game (required), kind, limit, query, +1 more. Returns: items, limit, total, offset
server_infoARead-onlyIdempotent
This server's DB identity: db_path, data_dir, pid, started_at and version, plus status.
Compare it against the other server's `GET /api/health` before writing so two servers pointed
at different data dirs (a split store) are caught instead of silently diverging.Inputs: no named parameters. Returns: DB identity of a running server (REST /api/health and MCP server_info return th…
start_projectA
Start a persistent orchestration project and get the first ready step(s). Progress is stored on the server, so any later session can continue with next_step(project_id).
Inputs: goal (required), workflow (required), game, addon, +3 more. Returns: game, goal, addon, title, +6 more
test_clearBIdempotent
Dismiss a session (iso_test off): state=idle, releasing a still-held host lease.
Inputs: run_id (required). Returns: A server-side in-game test session, mirroring the iso-spectate TEST STATUS HUD…
test_finishAIdempotent
Finish a running session pass/fail (the HUD's iso_test pass|fail): releases the host lease and
records a console-transcript evidence row on the linked project when there is one.
Inputs: result (required), run_id (required), note. Returns: A server-side in-game test session, mirroring the iso-spectate TEST STATUS HUD…
test_startA
Start a server-side test session (state=running). Claims the host lease when a host is given.
Mirror the state onto the in-game TEST STATUS HUD yourself via iso_test start over netcon.
Inputs: name (required), host_id, project_id. Returns: A server-side in-game test session, mirroring the iso-spectate TEST STATUS HUD…
test_statusARead-onlyIdempotent
One test run's record, or the current active run when no id is given.
Inputs: run_id. Returns: A server-side in-game test session, mirroring the iso-spectate TEST STATUS HUD…
test_stepAIdempotent
Set the running session's current step (the HUD strip's iso_test step). Running runs only.
Inputs: text (required), run_id (required). Returns: A server-side in-game test session, mirroring the iso-spectate TEST STATUS HUD…
update_stepA
Record progress on a step and get the next brief. Marking 'done' runs the step's gate (e.g. all addon scripts must type-check); if it fails you get the problems back — fix them and retry.
Inputs: status (required), step_id (required), project_id (required), notes, +1 more. Returns: game, goal, addon, title, +6 more
validate_cfgARead-onlyIdempotent
Check every command in a .cfg against the indexed convars/commands: unknown names (with suggestions), out-of-range values, cheat/devonly flags, and client-vs-server context.
Inputs: game (required), text (required), context. Returns: ok, errors, commands, warnings, +1 more
validate_cs_scriptARead-onlyIdempotent
Type-check a cs_script with the TypeScript compiler against this build's Valve point_script.d.ts.
Errors with category unknown_api mean the method/property does not exist — fix before shipping.
needs_type_narrowing gives the subclass to cast to. ALWAYS run this before handing a script to the user.
Inputs: code, game, path, addon. Returns: ok, errors, checker, warnings, +2 more
write_addon_fileA
Write a text source file (.js, .cfg, .vmat, .vdata, panorama .xml/.css, ...) into an addon. Binary/compiled assets are produced by Valve's tools, not written here. Use overwrite=true to replace.
Inputs: name (required), path (required), content (required), game, +1 more. Returns: path, size
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 73 tools
Most tools target distinct resources and actions, and the long descriptions clarify boundaries between docs, builds, jobs, hosts, tests, and projects. However, with 73 tools there is meaningful cross-domain overlap and potential confusion among job/test/project/evidence lifecycle tools and among related build/deploy/test list/status/finish operations.
All names use snake_case, which helps readability, and many tools are grouped by domain prefixes such as job_, build_, deploy_, and test_. But the convention mixes noun_verb domain-prefixed names (job_list, build_start) with generic verb_noun names (list_projects, get_host), so it is readable but not fully consistent.
The server exposes 73 tools, which is far beyond a well-scoped set for a single MCP server. Even though the surface spans several subdomains, the count is extreme and creates a heavy selection and maintenance burden.
The surface is broad and covers most major workflows: documentation lookup, schema/entity/script queries, addon creation and editing, build/deploy/test lifecycle, host management, jobs, evidence, and projects. Minor gaps remain, such as addon/file deletion or project cleanup operations, but core workflows are well covered.
Maintenance
Related MCP Connectors
Versioned documentation registry and semantic search for AI tools and coding assistants.
Serves your design system and coding standards to coding agents, so they stop guessing.
Gemmein docs, API reference, error explainers and a live integration check for coding agents.
Provide your AI coding tools with token-efficient access to up-to-date technical documentation for…
Related MCP Servers
- FlicenseNot gradedqualityBmaintenanceEnables AI clients to create, inspect, edit, and deploy Counter-Strike 2 maps and addons by working with Hammer map files, addon source files, Valve's resource compiler, and Workshop Tools locally.1-
- AlicenseAqualityBmaintenanceEnables coding agents to develop Open77 server resources with the full Lua API, permissions, events, game data, guides, and schemas pinned to a specific server build, plus validation and live server tools.2762 npm1MIT
- AlicenseAqualityBmaintenanceProvides AI assistants offline access to Counter-Strike 2 Workshop Tools documentation, enabling full-text search and page retrieval through MCP.3MIT
- FlicenseNot gradedqualityAmaintenanceEnables AI assistants to discover and invoke tools for saving and retrieving code snippets, generating style guides and wiki documentation, and orchestrating multi-agent documentation workflows with semantic search.-