# GMod MCP Server

> Model Context Protocol bridge for Garry's Mod — lets an AI assistant run Lua and inspect state inside a live game session.

- **Type:** MCP server
- **Install:** `agentstack add mcp-amyjeanes-gmod-mcp-server`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [AmyJeanes](https://agentstack.voostack.com/s/amyjeanes)
- **Installs:** 0
- **Category:** [Integrations](https://agentstack.voostack.com/c/integrations)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [AmyJeanes](https://github.com/AmyJeanes)
- **Source:** https://github.com/AmyJeanes/GMod-MCP-Server

## Install

```sh
agentstack add mcp-amyjeanes-gmod-mcp-server
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# GMod MCP Server

A Model Context Protocol (MCP) bridge between an AI coding assistant and a running Garry's Mod session. Lets the assistant invoke tools (e.g. run Lua, inspect state) inside the live game, instead of relying on static analysis and copy-pasted console output.

The repo is the GMod addon: clone it directly into `garrysmod/addons/`. The .NET MCP server lives in `server/` and is ignored by the GMod engine.

## Quick start

1. **Install the addon**
   ```
   cd garrysmod/addons
   git clone https://github.com/AmyJeanes/GMod-MCP-Server.git
   ```
2. **Build the MCP server**
   ```
   cd GMod-MCP-Server/server/GModMcpServer
   dotnet build
   ```
3. **Register the MCP server with your client** (e.g. Claude Code):
   ```
   claude mcp add gmod -- dotnet run --project /absolute/path/to/server/GModMcpServer
   ```
4. **Launch GMod** via the `host_launch` tool, or start it yourself. The bridge runs regardless, but tool dispatch requires opting in: in the GMod developer console, run `mcp_enable 1`. Sensitive tools additionally need their capability granted — see [Capabilities](#capabilities).
5. **Verify**: the host and game tools (see [Tools](#tools)) appear in your assistant's tool list, and `host_status` reports `bridge.reachable: true` once GMod is running and responsive.

## How it works

GMod cannot run a listening socket from pure Lua, and `http.Fetch`/`HTTP()` block private-IP destinations on listen and singleplayer servers. This addon uses **file-based IPC** via `garrysmod/data/mcp/` — the server-realm and client-realm bridges run independent poll loops, the .NET host polls the response files, and big payloads like screenshots never traverse `net.WriteString`. No binary modules required.

Each .NET MCP host generates a per-process session GUID and prefixes its request IDs with it, so multiple MCP clients (Claude Code + MCP Inspector + …) can share the same GMod instance without stealing each other's responses.

See `docs/protocol.md` for the wire format.

## Tools

These are the **built-in** tools, grouped by where they run. This is **not** an exhaustive list of what a live game exposes: any addon can register its own tools (`MCP:AddFunction`) and capabilities (`MCP:AddCapability`), which then appear alongside the built-ins — [the TARDIS addon does exactly this](https://github.com/AmyJeanes/TARDIS/tree/dev/lua/mcp/functions) to add its `tardis_*` tools.

### Host tools

Implemented by the .NET MCP server itself — available even when GMod isn't running.

| Tool | Description |
| --- | --- |
| `host_launch` | Launch Garry's Mod and wait until the MCP bridge is fully ready before returning. |
| `host_close` | Close the running GMod process (located by name, regardless of who launched it). |
| `host_status` | Report whether GMod is running, whether the MCP bridge is reachable (a live ping is sent when GMod is detected), and the current tool count and capability state. |
| `host_changelevel` | Change the map of the already-running GMod server and block until the new map is ready before returning (the in-game sibling of host_launch's readiness wait). |
| `mcp_reload` | Reload the in-game MCP addon (re-run its Lua and restart the bridge) and block until the bridge is back and ready before returning — the host-managed equivalent of running `mcp_reload` in the GMod console, but without the timeout a bare reload causes (the reload tears the bridge down mid-call). |

### Game tools

Dispatched into the running game over the file bridge. The framework appends `_sv` (server realm) or `_cl` (client realm) to each name, so the realm is always visible.

| Tool | Realm | Requires | Description |
| --- | --- | --- | --- |
| `bot_remove_sv` | server | `world_control` | Remove (kick) bots from the server. |
| `bot_spawn_sv` | server | `world_control` | Spawn one or more bots on the server (needs a listen server -- maxplayers>1). |
| `console_cmd_cl` | client | `unsafe` | Run a raw console command in this realm (server: game.ConsoleCommand; client: the local console). |
| `console_cmd_sv` | server | `unsafe` | Run a raw console command in this realm (server: game.ConsoleCommand; client: the local console). |
| `console_read_cl` | client | — | Read recently captured console output and Lua errors that fired outside a tool call (background hooks, timers, autorefresh, other addons) in this realm. |
| `console_read_sv` | server | — | Read recently captured console output and Lua errors that fired outside a tool call (background hooks, timers, autorefresh, other addons) in this realm. |
| `constraint_find_sv` | server | — | List an entity's physics constraints and its whole constraint network -- the structured answer to "what is this welded/no-collided/roped to?" and "why does this prop fall through or not collide?". |
| `cvar_set_cl` | client | `unsafe` | Set a console variable, wait for it to settle, and report the actual stabilized value. |
| `cvar_set_sv` | server | `unsafe` | Set a console variable, wait for it to settle, and report the actual stabilized value. |
| `cvar_state_cl` | client | — | Structured snapshot of one or more console variables -- current value (as string/int/float/bool), default, decoded FCVAR_* flags, help text and min/max bounds, in a single read. |
| `cvar_state_sv` | server | — | Structured snapshot of one or more console variables -- current value (as string/int/float/bool), default, decoded FCVAR_* flags, help text and min/max bounds, in a single read. |
| `debug_clear_cl` | client | — | Remove every hook the debug_* tools installed in this realm -- debug_record sampling hooks and (later) debug_draw render hooks, all under the `mcp_debug_` id namespace. |
| `debug_clear_sv` | server | — | Remove every hook the debug_* tools installed in this realm -- debug_record sampling hooks and (later) debug_draw render hooks, all under the `mcp_debug_` id namespace. |
| `debug_draw_cl` | client | `unsafe` | Install a persistent client-side render hook that runs your draw Lua every frame, for visual debugging you can then screenshot -- mark an entity, outline a volume, draw a path, label something on the HUD. |
| `debug_hooks_cl` | client | — | Inspect the hook registry -- answer "did my hook register, and where is it defined?" without hand-dumping hook.GetTable(). |
| `debug_hooks_sv` | server | — | Inspect the hook registry -- answer "did my hook register, and where is it defined?" without hand-dumping hook.GetTable(). |
| `debug_record_cl` | client | `unsafe` | Record a value each time a hook fires, for a bounded window, then return the time series -- a managed sampling probe that owns the hook lifecycle (unique namespaced hook, duration cap, auto-remove on end/stop/error) so you never hand-roll hook.Add/poll/hook.Remove. |
| `debug_record_interactive_cl` | client | `unsafe` | ARM an on-screen, player-driven recorder for a repro the user must physically perform (walk a path, time a portal crossing, mash a seam) -- the interactive sibling of debug_record. |
| `debug_record_interactive_sv` | server | `unsafe` | Arm the paired SERVER-realm capture for an interactive recording -- the server half of debug_record_interactive_cl (which owns the on-screen UI and human timing). |
| `debug_record_read_cl` | client | — | Collect the series from a debug_record_interactive recorder. |
| `debug_record_read_sv` | server | — | Collect the SERVER-realm series from a debug_record_interactive (server) recorder, by `link_id`. |
| `debug_record_sv` | server | `unsafe` | Record a value each time a hook fires, for a bounded window, then return the time series -- a managed sampling probe that owns the hook lifecycle (unique namespaced hook, duration cap, auto-remove on end/stop/error) so you never hand-roll hook.Add/poll/hook.Remove. |
| `entity_create_sv` | server | `world_control` | Spawn one entity server-side -- Create, SetModel, SetPos, Spawn, Activate -- optionally frozen and coloured, and tagged for later cleanup by entity_remove. |
| `entity_find_cl` | client | — | Find entities and return compact rows -- index, class, model, pos and distance -- instead of a raw dump. |
| `entity_find_sv` | server | — | Find entities and return compact rows -- index, class, model, pos and distance -- instead of a raw dump. |
| `entity_remove_sv` | server | `world_control` | Remove entities server-side and wait until they are actually gone before reporting (:Remove is deferred, so a same-call count would be stale). |
| `entity_set_sv` | server | `world_control` | Mutate one entity's transform, render and physics state by index, then confirm. |
| `entity_state_cl` | client | — | Nil-safe structured snapshot of one entity by index -- identity, transform, render (incl. |
| `entity_state_sv` | server | — | Nil-safe structured snapshot of one entity by index -- identity, transform, render (incl. |
| `file_exists_cl` | client | — | Check whether a single path exists in the GMod virtual filesystem, and report whether it's a file or folder plus its size and modified time -- file.Exists/IsDir/Size/Time. |
| `file_exists_sv` | server | — | Check whether a single path exists in the GMod virtual filesystem, and report whether it's a file or folder plus its size and modified time -- file.Exists/IsDir/Size/Time. |
| `file_find_cl` | client | — | List files and folders in the GMod virtual filesystem matching a glob -- file.Find(pattern, path). |
| `file_find_sv` | server | — | List files and folders in the GMod virtual filesystem matching a glob -- file.Find(pattern, path). |
| `game_set_cl` | client | `world_control` | Set curated client-only game knobs, then confirm. |
| `game_set_sv` | server | `world_control` | Set one or more curated, safe server-tuning knobs, wait for them to settle, and report the actual values. |
| `game_state_sv` | server | — | Structured snapshot of server-wide game state in one read -- current map, gamemode, hostname, singleplayer/dedicated flags, max player slots, player/bot/human counts, a lean roster of every player (name/userid/entindex/is_bot/is_host/team -- drill into one with player_state or entity_state), a `tuning` block with the live values of game_set's knobs (gravity, timescale, phys_timescale, fakelag), and `cheats_enabled` (sv_cheats) -- which gates whether game_set's timescale/fakelag will take. |
| `hook_call_cl` | client | `unsafe` | Fire a GMod hook and report the result -- the dispatch companion to debug_hooks (which only reads the registry). |
| `hook_call_sv` | server | `unsafe` | Fire a GMod hook and report the result -- the dispatch companion to debug_hooks (which only reads the registry). |
| `job_cancel_cl` | client | — | Abort a background job started with async=true, tearing down its hooks and side effects immediately (e.g. |
| `job_cancel_sv` | server | — | Abort a background job started with async=true, tearing down its hooks and side effects immediately (e.g. |
| `job_collect_cl` | client | — | Fetch the result of a background job started by calling a tool with async=true, identified by the `job_id` the arm returned. |
| `job_collect_sv` | server | — | Fetch the result of a background job started by calling a tool with async=true, identified by the `job_id` the arm returned. |
| `job_list_cl` | client | — | List background jobs (from async=true arms) in this realm: job_id, the tool, status (armed / finished / cancelled), seconds elapsed since arming, and whether it's collectable now. |
| `job_list_sv` | server | — | List background jobs (from async=true arms) in this realm: job_id, the tool, status (armed / finished / cancelled), seconds elapsed since arming, and whether it's collectable now. |
| `light_projected_cl` | client | — | Create, update, or remove a clientside ProjectedTexture -- a spotlight test-light rig, the projected-light sibling of debug_draw. |
| `lua_run_cl` | client | `unsafe` | Compile and execute Lua source in this realm. |
| `lua_run_sv` | server | `unsafe` | Compile and execute Lua source in this realm. |
| `model_info_cl` | client | — | Structured info about a model ASSET without spawning a prop -- read straight from the model file via util.GetModelInfo (no entity, no spawn), so it is synchronous and realm-identical. |
| `model_info_sv` | server | — | Structured info about a model ASSET without spawning a prop -- read straight from the model file via util.GetModelInfo (no entity, no spawn), so it is synchronous and realm-identical. |
| `player_lua_run_sv` | server | `unsafe` | Compile and execute Lua source on a target player's client realm and return the result. |
| `player_set_sv` | server | `player_control` | Set a player or bot's pose and state, then wait for it to settle and confirm it stuck. |
| `player_state_cl` | client | — | Structured snapshot of a player (or all players) -- identity, vitals, eye position/aim, velocity, movement state (movetype, on_ground, crouching, ducking, frozen, godmode, water_level), view offset and collision hull, model/animation sequence, playermodel and weapon colours, and active weapon, in one read. |
| `player_state_sv` | server | — | Structured snapshot of a player (or all players) -- identity, vitals, eye position/aim, velocity, movement state (movetype, on_ground, crouching, ducking, frozen, godmode, water_level), view offset and collision hull, model/animation sequence, playermodel and weapon colours, and active weapon, in one read. |
| `player_trace_cl` | client | — | Raycast from a player's eyes along their view and report what they're looking at -- the hit entity (index and class; drill in with entity_state), hit position, distance from the eye, surface normal, and surface material/texture. |
| `player_trace_sv` | server | — | Raycast from a player's eyes along their view and report what they're looking at -- the hit entity (index and class; drill in with entity_state), hit position, distance from the eye, surface normal, and surface material/texture. |
| `player_walk_cl` | client | `player_control` | Walk the local (host) player naturally by driving the real movement code (CUserCmd each tick) via CreateMove, so grounded-locomotion bugs reproduce -- unlike teleport or `+forward`. |
| `player_walk_sv` | server | `player_control` | Walk a target player or bot naturally by driving its CUserCmd each tick via StartCommand -- the canonical way to control bots. |
| `reload_file_cl` | client | — | Hot-reload one on-disk Lua source file by re-running it in this realm -- a targeted alternative to mcp_reload (which rebuilds the whole MCP addon) and the engine's autorefresh (which only fires for some edits), for iterating on a single file in any addon. |
| `reload_file_sv` | server | — | Hot-reload one on-disk Lua source file by re-running it in this realm -- a targeted alternative to mcp_reload (which rebuilds the whole MCP addon) and the engine's autorefresh (which only fires for some edits), for iterating on a single file in any addon. |
| `screenshot_cl` | client | — | Capture a JPEG of what the player actually sees on screen - the genuine rendered frame (HUD, portals, post-processing all as-live), not a re-render, so the image matches the game exactly. |
| `world_trace_cl` | client | — | Raycast from an arbitrary origin and report what the ray hits -- the hit entity (index and class; drill in with entity_state), hit position, distance, surface normal and material, plus the origin's point-contents and the trace's solid flags. |
| `world_trace_sv` | server | — | Raycast from an arbitrary origin and report what the ray hits -- the hit entity (index and class; drill in with entity_state), hit position, distance, surface normal and material, plus the origin's point-contents and the trace's solid flags. |

### Capabilities

Security gates a tool can require (`requires = { ... }`). Each derives an archived `mcp_allow_` convar; grant it in the GMod developer console (e.g. `mcp_allow_unsafe 1`) to enable the tools that depend on it. Only the built-in `unsafe` is listed here — addons can declare their own.

| Capability |

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [AmyJeanes](https://github.com/AmyJeanes)
- **Source:** [AmyJeanes/GMod-MCP-Server](https://github.com/AmyJeanes/GMod-MCP-Server)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-amyjeanes-gmod-mcp-server
- Seller: https://agentstack.voostack.com/s/amyjeanes
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
