Command Palette

Search for a command to run...

UnylyUnyly
Browse all

GMod

FreeNot checked

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

GitHubEmbed

About

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

README

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.
  5. Verify: the host and game tools (see 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 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).
engine_log Read the tail of GMod's engine console log (console.log) — the raw, unfiltered console: engine-native C++ output (Bad SetLocalOrigin, Crazy origin, asset/mount spew, engine errors) plus both realms' Lua output, interleaved.

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_<id> 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 ConVar Default Description
player_control mcp_allow_player_control off Player control: let MCP drive and reposition the local player — teleport/pose/health/loadout (player_set) and movement/aim (player_walk).
unsafe mcp_allow_unsafe off Unsafe: arbitrary code execution via MCP — Lua source (lua_run) and raw console commands (console_cmd).
world_control mcp_allow_world_control off World control: let MCP mutate world state via structured tools — spawn/remove/modify entities (entity_create/remove/set), curated game knobs (game_set), and test bots (bot_spawn/remove).

Console & error capture

Output and errors that happen during a tool call are already returned on that call's response (console / warnings). Anything that fires outside a tool call — a hook the assistant registered firing later, a timer, an autorefresh re-run, another addon erroring — would otherwise be invisible. Passive capture records these into a small per-realm ring buffer and surfaces them to the model two ways, both over the existing bridge (MCP has no way to push unsolicited messages into the model's context, so there's no notification channel — it rides tool results):

  • Attached to the next tool response as an events array (per-session cursor, so it isn't re-sent).
  • On demand via the console_read_sv / console_read_cl tools (pass back the returned cursor as since to get only newer events).

It's realm-local — _sv shows server-side errors/prints, _cl shows client-side. Controlled by mcp_capture (only active while mcp_enable is 1):

mcp_capture 2   # default: Lua errors + console (print/Msg)
mcp_capture 1   # Lua errors only
mcp_capture 0   # off

Extending

Drop a Lua file in lua/mcp/functions/ with the conventional sh_/cl_/sv_ prefix; the realm is implicit from the prefix and the framework appends _sv/_cl to the tool name automatically. Saving the file is enough — GMod's autorefresh re-runs it, the registry updates in place, and a debounced manifest write propagates the change to the .NET host (which emits notifications/tools/list_changed). mcp_reload forces a rebuild when a tool file is deleted (autorefresh has nothing to fire on for removals).

Adding a tool

MCP:AddFunction({
    id = "list_players",
    description = "List all connected players.",
    schema = { type = "object", properties = {}, required = {} },
    handler = function(args, ctx)
        local names = {}
        for _, ply in ipairs(player.GetAll()) do
            names[#names + 1] = ply:Nick()
        end
        return { ok = true, result = names }
    end,
})

Adding a capability

Gate a sensitive tool behind a capability. MCP:AddCapability auto-derives a mcp_allow_<id> convar (default off), and the bridge refuses to dispatch a tool whose required capability is off — so a buggy or compromised handler can't bypass the gate.

MCP:AddCapability({
    id = "dangerous",
    description = "What this unlocks, and why it's risky.",
    default = false,
})

MCP:AddFunction({
    id = "my_tool",
    requires = { "dangerous" },
    -- ...
})

License

MIT — see LICENSE.

from github.com/AmyJeanes/GMod-MCP-Server

Installing GMod

This server has no published package — it is built from source. Open the repository and follow its README.

▸ github.com/AmyJeanes/GMod-MCP-Server

FAQ

Is GMod MCP free?

Yes, GMod MCP is free — one-click install via Unyly at no cost.

Does GMod need an API key?

No, GMod runs without API keys or environment variables.

Is GMod hosted or self-hosted?

Self-hosted: the server runs locally on your machine via the install command above.

How do I install GMod in Claude Desktop, Claude Code or Cursor?

Open GMod on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.

Related MCPs

Compare GMod with

Not sure what to pick?

Find your stack in 60 seconds

Author?

Embed badge for your README

Browse similar

All ai MCPs