Skip to main content
Glama

LovePilot — LÖVE2D MCP Server

GitHub License: MIT

LovePilot is an advanced Model Context Protocol (MCP) server designed specifically for the LÖVE / LÖVE2D game engine — the most complete MCP server for playing a live LÖVE2D game: introspect real-time state, simulate keyboard/mouse input so the AI can actually play, capture screenshots, execute Lua code, hot-reload modules, and receive push notifications the moment the game state changes.

Fork/extension of shayarnett/love2d-mcp with real-time play capabilities and a more robust TCP client.

What's different from the original

This fork adds the real-time play capabilities and reliability fixes that make an AI genuinely playable against a live LÖVE2D game, on top of the original shayarnett/love2d-mcp:

  • Real-time playsend_input lets the AI control the game through the bridge (keyboard + mouse), get_screenshot captures the window as a viewable PNG, and watch_game_state pushes state_changed events the moment anything changes (no polling).

  • Request tracking (_reqId) — every command carries a request id; a late reply from the game can never be misattributed to the wrong command after a timeout. Screenshot capture is also wrapped in pcall so a failing encode returns an error instead of crashing the game.

  • Command timeout — configurable per-command timeout (15s default) so a hung game reports an error to the AI instead of waiting forever.

  • Hot-reload that survives referencesreload_code mutates module tables in place (same table identity, updated contents), so existing require() references keep working. game/handle.lua wraps non-table engine objects (physics bodies, audio sources, canvases) so even those can be re-bound safely after a reload.

  • Loaded tool setget_objects (list or by id), run_lua, list_lua_files, reload_code, send_input, get_screenshot, watch_game_state / unwatch_game_state.

For the full technical walkthrough (development notes, in Spanish), see CAMBIOS_TIEMPO_REAL.es.md.

Related MCP server: game-mcp

Features

  • Real-time introspection — query game objects, positions, properties, and state

  • AI-driven input — simulate keyboard and mouse so the AI can move, attack, and play the game

  • Screenshots — capture the running window as a PNG the AI can see

  • Dynamic code execution — run Lua code inside the live game context

  • Push notifications — the game pushes state_changed events when anything changes; no polling needed

  • Hot-reload — reload a Lua file from disk into the running game without restarting it

  • Robust transport — FIFO command queue, ordered 1:1 responses, auto-reconnect, no listener leaks

  • Proper error reporting — every tool returns isError: true with a descriptive message on failure

Architecture

┌─────────────┐ stdio  ┌─────────────┐   TCP    ┌─────────────┐
│ MCP Client  │◄──────►│  MCP Server │◄────────►│  LÖVE2D     │
│ (Claude,    │        │ (Node/TS)   │  JSON-L   │  Game (Lua) │
│  Cursor,    │        │ build/      │  per line │  + bridge   │
│  OpenCode)  │        └─────────────┘           └─────────────┘
└─────────────┘        stdio uses MCP      TCP uses JSON each line,
                        (newline-delimited)        1 response per command
  • MCP Server: Node.js/TypeScript server speaking the MCP protocol over stdio

  • LÖVE2D Bridge: small Lua TCP server embedded in the game (game/mcp_bridge.lua)

  • Communication: JSON per line over TCP; the game replies in strict FIFO order

  • Real-time push: subscribed clients receive state_changed events automatically

Requirements

Setup

git clone https://github.com/InfiniteLoopRD/lovepilot.git
cd lovepilot
npm install
npm run build

The compiled server lives at build/index.js. Run it directly (node build/index.js) or via npm start.

Quick Start

1. Start the example game

love game/

A window opens and the game starts a TCP bridge on port 12345. Use port 12345 in your own game too, or change LOVE2D_PORT at the top of src/index.ts.

2. Connect a client

With the MCP Inspector:

npx @modelcontextprotocol/inspector node build/index.js

In a config file for Claude/Cursor/OpenCode-style clients:

{
  "mcpServers": {
    "love2d": {
      "command": "node",
      "args": ["/path/to/lovepilot/build/index.js"]
    }
  }
}

Available MCP Tools

get_objects

Lists all objects in the current game scene, or gets one specific object if you pass an id. Replaces the old separate list_objects / get_object tools — same underlying query, with or without a filter.

Arguments:

  • id (string, optional): the object ID, e.g. "p1". Omit to list every object.

Returns: if id is omitted, an array of {id, type, x, y} for every game object. If id is given, the complete object data including every property (state, health, x, y, velocity, stocks…).

run_lua

Execute arbitrary Lua code in the game context.

Arguments:

  • code (string): Lua code to execute

Returns: the result (string, or table encoded as JSON).

Available in the code context: objects (all game objects), love (full LÖVE2D API), plus standard Lua libs (math, string, table, pairs, ipairs, …).

return objects.p1.x

get_screenshot

Capture a screenshot of the currently running game window.

Arguments: none

Returns: a PNG image block (base64) that the AI assistant can view. Requires love.graphics.captureScreenshot — call mcp_bridge.captureIfPending() at the end of the bridge's love.draw().

send_input

Simulate keyboard or mouse input so the AI can control the game.

Arguments:

  • type (string, required): key_down, key_up, mouse_move, mouse_down, mouse_up

  • key (string): LÖVE KeyConstant, e.g. "left", "space", "a" (for key events)

  • duration (number): optional, auto-release the key after N seconds for key_down

  • x, y (number): target position for mouse_move

  • button (number): 1 = left, 2 = right, 3 = middle (for mouse buttons)

Returns: {ok: true} on success.

Important: the game must read input through the bridge — mcp_bridge.isDown("left") instead of love.keyboard.isDown("left") — so real keyboard input and AI input coexist (see the example in game/main.lua).

watch_game_state

Subscribe to real-time updates. Every time something changes (position, health, state, …), the game pushes a state_changed notification to the AI automatically — no polling.

Arguments: none

Returns: {ok: true, message: "subscribed to state_changed events"}.

unwatch_game_state

Stop receiving state-change notifications.

Arguments: none

Returns: {ok: true, message: "unsubscribed"}.

list_lua_files

List every .lua file in the game project. Real games are usually split across several files (main.lua plus modules), so check this before deciding what to edit and pass to reload_code — don't assume everything lives in main.lua.

Arguments:

  • dir (string, optional): subfolder to scan, relative to the game's source folder. Defaults to the project root.

Returns: the list of .lua files found under the scanned directory.

reload_code

Hot-reload a Lua file from disk into the running game. LÖVE does not do this on its own — editing a .lua file while the game is running has zero effect until you restart the process, unless you call this tool.

Arguments:

  • file (string, optional): path relative to the game's source folder. Defaults to "main.lua".

Returns: {ok: true, message: "reloaded main.lua"} on success.

How it works: re-reads and re-executes the file, then calls the freshly-defined love.load() again — this behaves like restarting the level with the new code, not a state-preserving patch. Most game state lives in local variables that get re-created when the file's top-level code runs again, so expect a reset (e.g. entities repositioned to their initial values), not a seamless in-place edit. mcp_bridge.init() is safe to call twice — it detects the server is already listening and skips re-binding the port instead of erroring.

Real-Time State Watching

Instead of the AI repeatedly asking "what changed?", the game pushes updates itself:

  • The bridge compares the current state snapshot against the previous one each frame (mcp_bridge.checkAndPushStateChanges()).

  • When something differs, it sends {"event":"state_changed", "data":{...}} to all subscribed clients.

  • The MCP server forwards every push as a standard notifications/message MCP notification, so the AI receives it the moment it happens — regardless of whether a command is pending.

  • Pushes never corrupt command responses: the FIFO command queue guarantees each response is matched to the right request 1:1, even under heavy push traffic (tested with 2000+ pushes landing while commands were in flight).

Integrating with Your Own Game

  1. Copy game/mcp_bridge.lua to your game directory.

  2. In main.lua:

local mcp_bridge = require("mcp_bridge")

function love.load()
    mcp_bridge.init(12345)
    mcp_bridge.setObjectGetter(function() return objects end)
end

function love.update(dt)
    mcp_bridge.update()          -- handles commands + pushes state changes
    -- your game logic; read AI+real input via:
    --   mcp_bridge.isDown("left"), mcp_bridge.mouseIsDown(1)
end

function love.draw()
    -- draw your game...
    mcp_bridge.captureIfPending() -- end of draw, so AI screenshots see the frame
end

function love.quit()
    mcp_bridge.shutdown()
end
  1. Fill the object table with the properties the AI should see (state, health, facing, stocks…). The bridge's simple JSON encoder handles nested tables of strings/numbers/booleans.

Development

npm run dev     # tsc --watch, auto-rebuild
npm run build   # compile TypeScript once
npm start       # run the compiled server

Project structure

lovepilot/
├── src/
│   └── index.ts            # MCP server implementation + TCP client
├── build/
│   └── index.js            # compiled output (what you run)
├── game/
│   ├── main.lua            # example: bouncing balls + an AI/keyboard-controllable
│   │                       # square (idle/walking/attacking states, no real combat)
│   ├── handle.lua          # `Handle` wrapper so non-table engine objects
│   │                       # (physics, audio, canvases) survive code reloads
│   └── mcp_bridge.lua      # Lua TCP bridge module
├── CAMBIOS_TIEMPO_REAL.es.md # (Spanish) real-time features walkthrough
├── package.json
├── tsconfig.json
└── README.md

Troubleshooting

Game won't start

  • Verify LÖVE2D is installed: love --version

  • Check for syntax errors in the Lua files

MCP connection fails

  • Make sure the game is running before starting the MCP server (the server connects to port 12345 lazily on first command)

  • Check port 12345 isn't already in use; look for "MCP Bridge listening" in the game console

AI tools error with "argument 'x' (string|number) is required"

  • That tool requires at least one argument; pass it via the schema shown in tools/list

Screenshot not working

  • Call mcp_bridge.captureIfPending() at the very end of love.draw()

License

MIT — see LICENSE.

Acknowledgments

Available Tools

8 tools
get_objectsA

Get objects from the current game scene. Omit 'id' to list every object (id, type, x, y for each). Pass 'id' to get full detail on just that one object instead. Replaces what used to be two separate tools (list_objects / get_object) since they were the same underlying query with or without a filter.

ParametersJSON Schema
NameRequiredDescriptionDefault
idNoOptional. The ID of a specific object to retrieve. Omit to list all objects.

TDQS

A3.9/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full behavioral burden. It helpfully discloses the return shape for each mode ('id, type, x, y for each' vs full detail), which implies a read-only query. It does not address permissions, rate limits, or scene-availability preconditions, leaving meaningful gaps for an unannotated tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tightly written sentences with the primary purpose front-loaded and the mode-selection rule immediately following. The final sentence about replacing list_objects/get_object is useful orientation but is marginally meta rather than invocation-critical.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, so the description correctly compensates by describing the return fields for both modes. With no annotations and one trivially simple parameter, the description covers nearly everything an agent needs, falling short only on preconditions and read-only confirmation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and the single parameter is fully documented, so the baseline is 3. The description restates the same omit/pass semantics as the schema, adding no syntax or format detail beyond it.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Get objects from the current game scene') and clearly delineates the two operating modes (list-all vs single-object detail). This is easily distinguished from siblings like run_lua, get_screenshot, and watch_game_state, which do entirely different things.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly tells the agent how to select each mode: omit 'id' to list every object, pass 'id' for full detail on one. It also explains the tool consolidates two former tools. It does not, however, state when to prefer this over siblings like run_lua for querying game state, so the routing guidance is incomplete.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_screenshotA

Capture a screenshot of the currently running game window as a base64 PNG image

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A3.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden. It does disclose the return format ('base64 PNG image') and implies the game must be running, but says nothing about permissions, potential side effects, performance cost, or whether the capture disrupts gameplay. These gaps are notable for a zero-annotation tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single, front-loaded sentence with no wasted words. It efficiently conveys the action, target, and output format.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter, read-only-style tool with no output schema, the description covers the essential facts: what is captured, the target window, and the return encoding. It lacks only minor context such as whether the game must be running or if the capture is async, but is largely sufficient for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, so the baseline of 4 applies. The description adds no parameter information, which is appropriate since none exist.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb 'Capture' and resource 'screenshot of the currently running game window', clearly indicating what the tool does. It does not explicitly differentiate from any sibling tool, though the purpose is distinct enough that an agent would not confuse it with send_input, run_lua, or get_objects.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no when-to-use guidance, no prerequisites (e.g., game must be running), and no alternatives. It merely states the action without indicating the context in which an agent should select this tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_lua_filesA

List every .lua file in the game project. Real games are usually split across several files (main.lua plus modules), so check this before deciding what to edit and pass to reload_code — don't assume everything lives in main.lua.

ParametersJSON Schema
NameRequiredDescriptionDefault
dirNoSubfolder to scan, relative to the game's source folder. Defaults to the project root.

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden. It discloses the scan scope (project .lua files) and default root behavior, but says nothing about recursion into subfolders, ordering, or the exact form of what is returned (paths vs names).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, front-loaded with the action, and the rationale about multi-file projects is brief and earns its place by explaining why the tool matters. No redundancy or padding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple no-annotation, single-optional-param tool with no output schema, the description covers purpose and usage well but leaves return-format details (recursion, path style) unspecified, which an agent would need to know before relying on the output.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the single optional dir parameter is already fully documented as relative to the source folder with root as default. The description adds no syntax or format nuance beyond that, so baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (list) and resource (.lua files in the game project) with clear scope. It distinguishes itself from siblings by tying its output to a downstream decision (what to edit and what to pass to reload_code), though it doesn't explicitly name what it is not.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives clear context for when to call it: before deciding what to edit and before invoking reload_code, and warns against assuming everything lives in main.lua. No explicit when-not or exclusion conditions, but the intended workflow is unmistakable.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

reload_codeA

Hot-reload a Lua file from disk into the running game (default 'main.lua'). LÖVE does NOT pick up file edits on its own — without this tool, changes you write to disk have zero effect until the game is restarted. This clears the require() cache for every game module first (so edits to files required by main.lua are picked up too, not just main.lua itself), then re-runs the file and calls love.load() again — it behaves like restarting the level with the new code rather than a state-preserving patch. Module tables themselves are reloaded via in-place mutation (same table identity, new contents), so any other system that already did local X = require(...) and kept that reference will see the updated code automatically — you do NOT need to manually re-point those. This does NOT apply to instances created from a module before the reload (e.g. an object made with Class.new()) — those keep their old field values and identity; if something else is holding a reference to a specific pre-reload instance, use run_lua to re-point it manually, or design around ids/lookup tables instead of raw instance references.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileNoPath relative to the game's source folder. Defaults to 'main.lua'.

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and delivers: it clears the require() cache, re-runs the file, calls love.load(), uses in-place table mutation, and explicitly scopes what it does NOT affect (pre-reload instances). This is exactly the side-effect disclosure a mutation tool needs.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core action, then layers genuinely useful caveats rather than filler. It is long, but nearly every sentence carries behavioral information that an agent needs before invoking.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter mutation tool with no annotations and no output schema, the description is complete: it covers purpose, side effects, reload semantics, and the exact boundary of what gets updated.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the single param is fully documented, so the baseline is 3. The description's '(default main.lua)' merely repeats the schema and adds no new syntax or format detail.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('hot-reload a Lua file from disk into the running game') with the default scope named. It is clearly distinguishable from siblings like run_lua and list_lua_files without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explains the trigger condition precisely ('LÖVE does NOT pick up file edits on its own'), so the agent knows when this tool is required. It routes a specific sub-case to run_lua, but does not give a broader when-not-to-use matrix.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

run_luaC

Execute arbitrary Lua code in the game context

ParametersJSON Schema
NameRequiredDescriptionDefault
codeYesThe Lua code to execute

TDQS

C2.9/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full behavioral burden for a tool that executes arbitrary code. It says nothing about sandboxing, permissions, side effects on game state, whether execution is synchronous, how errors or return values surface, or whether results are returned at all — a major gap for such a powerful operation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single front-loaded sentence with no wasted words; it is efficient and readable. It is arguably under-specified rather than over-long, but conciseness itself is good.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a code-execution tool with no annotations and no output schema, the description is far too thin. It omits what the code can access, what happens on error, and whether any value is returned, leaving the agent unable to predict the outcome of a call.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Only one parameter exists and schema description coverage is 100%, so the schema already documents 'code' fully. The description adds no syntax, format, or constraint details beyond what the schema provides, making the baseline 3 appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (Execute) and resource (arbitrary Lua code) with the execution scope ('in the game context'), which lets an agent distinguish it from siblings like list_lua_files and reload_code. However, it does not explicitly contrast itself with those siblings, so it falls short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no guidance on when to reach for run_lua versus list_lua_files, reload_code, or send_input, nor any prerequisites or exclusions. The agent must infer usage entirely from the name and one-line description.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

send_inputC

Simulate keyboard or mouse input in the game, letting the AI actually play (move, attack, click, etc). type must be one of: key_down, key_up, mouse_move, mouse_down, mouse_up.

ParametersJSON Schema
NameRequiredDescriptionDefault
xNo
yNo
keyNoLÖVE KeyConstant, e.g. 'left', 'space', 'a'
typeYes
buttonNo1 = left, 2 = right, 3 = middle
durationNoOptional: auto-release the key after N seconds

TDQS

C2.9/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It conveys that this mutates game state (simulated input), but says nothing about coordinate conventions, whether input must be paired (key_down/key_up), rate limits, or failure modes — significant gaps for a 6-param mutation tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two tight sentences, front-loaded with the purpose before the enum list. The enum restatement is slightly redundant given it already lives in the schema, but the text is otherwise waste-free.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With 6 parameters, no annotations, and no output schema, the description should do more. It omits coordinate semantics, the key/mouse-mode relationship, and any safety or sequencing guidance, leaving an agent to infer how to construct valid calls.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is only 50%, and the description's only parameter content is repeating the enum already present in the schema. It adds no meaning for x/y coordinate space, how key/button interact with type, or how duration behaves, so it does not compensate for the coverage gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific verb+resource ('Simulate keyboard or mouse input in the game') and gives concrete examples of its effect (move, attack, click). It is clearly distinguishable from read-only siblings like get_screenshot and get_objects, though it never names an alternative explicitly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by 'letting the AI actually play,' which signals this is the tool for acting on the game, but there is no explicit when-to-use versus when-not, and no mention of how it relates to run_lua, which could also drive behavior.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

unwatch_game_stateB

Stop receiving real-time game state update notifications.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

B3.2/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full behavioral burden and does not meet it. It does not say whether the call is idempotent, whether it errors when no watch is active, whether it only affects the calling session or all subscribers, or whether the stop takes effect immediately. It essentially restates the tool name with slightly more detail about what is being stopped.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single front-loaded sentence with no filler. Every word earns its place and the action is stated immediately.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter toggle with no output schema, the description is minimally sufficient to invoke correctly. However, it omits the relationship to watch_game_state, error/idempotency behavior, and scope of effect, which are the only remaining things an agent would need for this operation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so the baseline of 4 applies. Nothing in the description is needed to explain parameter usage, and there is no ambiguity about what inputs to supply.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific action (stop receiving) on a specific resource (real-time game state update notifications), so the inverse relationship with the sibling watch_game_state is obvious. It does not explicitly name that sibling, but the verb and object are unambiguous enough that no agent would confuse it with send_input, run_lua, or get_screenshot.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no explicit when-to-use guidance, no mention of the required counterpart call (watch_game_state) that presumably must precede it, and no statement about what happens if invoked without an active watch. Usage is only weakly implied by the phrase 'stop receiving'.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

watch_game_stateA

Subscribe to real-time game state updates. The game will push a notification every time something changes (position, health, animation state, etc), instead of you having to repeatedly call get_objects. Call unwatch_game_state to stop.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so the description carries the behavioral burden. It discloses push-on-change semantics (event-driven, not polled) which is a meaningful behavioral trait beyond the empty schema, and names a stop mechanism. Does not disclose notification payload format or delivery guarantees.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, front-loaded with the action, then the mechanism, then the teardown. No filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Covers the what, the when-alternative, and the teardown for a zero-param subscription tool. No output schema, but the push-notification payload shape is not described; minor gap since notification content (position, health, animation state) is at least hinted.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Zero parameters, so baseline is 4. Description correctly implies no parameters are needed to start the subscription.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Names the specific action (subscribe to real-time game state updates) and the resource (game state). Distinguishes from the polling alternative get_objects and pairs with the sibling unwatch_game_state.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly frames when to use: instead of repeatedly calling get_objects. Also states how to stop by tying to unwatch_game_state. No explicit when-not-to-use, but the alternative is named clearly.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 8 tool updatesv1.0.0
    • First observedget_objects
    • First observedget_screenshot
    • First observedlist_lua_files
    • First observedreload_code
    • First observedrun_lua
    • First observedsend_input
    • First observedunwatch_game_state
    • First observedwatch_game_state

TDQS

A3.7/5.0

Scored across 8 tools

Disambiguation5/5

Each tool targets a distinctly different capability: input simulation, Lua execution, screenshots, live state subscription, file listing, code reload, and object inspection. The watch/unwatch pair is a natural complementary set rather than an overlap. No two tools could plausibly be confused for one another.

Naming Consistency5/5

All eight tools follow a consistent verb_noun snake_case pattern (send_input, run_lua, get_screenshot, watch_game_state, unwatch_game_state, list_lua_files, reload_code, get_objects). The watch/unwatch pairing is symmetric and predictable.

Tool Count5/5

Eight tools is well-scoped for a game-automation server, covering input, observation, code editing, and hot-reload without bloat. The note that list_objects/get_object were merged into a single get_objects shows active curation against redundancy.

Completeness4/5

The surface covers the full loop an agent needs: inspect the project, execute or reload code, simulate input, and observe results via screenshots, object queries, or live subscriptions. Minor gap: no tool to write/edit .lua files from the server, so file changes must be made externally before reload_code.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers