Windows 11 MCP server for local game control + capture (WGC + DXGI)
This MCP server provides 15 tools for Windows game control with low-to-moderate definition quality. Naming is clear and verb-based (set_allowlist, focus_window, key_tap, etc.), but descriptions are inconsistent in quality and depth. Most tools have 1-2 sentence descriptions (30-100 chars), which is below the ideal 50-200 char range for LLM optimization. Input schemas are present and properly typed for all tools, but parameter descriptions are minimal or generic. Output schemas are implicit (returning Dict[str, Any]) rather than formally documented. Error handling exists ("ok": False patterns) but lacks actionable recovery guidance. Security implementations include allowlist checking and rate limiting, which is good for a game-control tool. However, the server lacks idempotency documentation, parameter constraint documentation, and composition clarity.
Focus a window by title substring.
Create/connect a virtual Xbox controller (ViGEm).
Disconnect the virtual gamepad.
Press a gamepad button (A,B,X,Y,LB,RB,START,BACK,LS,RS).
Release a gamepad button.
Set left/right sticks. Range -32768..32767.
Return the active window title.
Descriptions are too brief (30-100 chars) and lack LLM-optimized context. Most are single-sentence fragments without explaining WHEN to use the tool or what prerequisites exist (e.g., 'key_down' should note it requires 'key_up' to release).
Output schemas are not formally documented. All tools return Dict[str, Any] with implicit fields like 'ok', 'reason', 'title', 'x', 'y', 'w', 'h'. LLMs cannot parse unstructured output reliably. Need explicit schema documentation for each tool's return type.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | F | 47 | 2026-07-28+ | v2 |
| 2026-03-09 | F | 0 | - | v1 |
Return x,y,w,h for the first window whose title contains substring.
Hold a key down until key_up.
Tap a key by name (e.g., 'w','space','esc').
Release a key previously held with key_down.
Click mouse: button left|right|middle, count.
Move mouse by relative dx, dy.
Vertical scroll (positive=up/away, negative=down/towards).
Set allowed window title substrings; input only fires when a matching window is focused.
Error messages lack actionable recovery guidance. Example: 'No matching window' does not tell the LLM what to do next (e.g., 'Try get_active_window() to see the current window title'). Similarly, 'Unknown key' does not list valid key names.
Parameter descriptions are minimal or missing context. Example: 'key_tap' accepts a 'key' parameter described only as "Key name to tap" but does not list valid key names (w, space, esc, etc.) or constraints. LLMs cannot reliably infer valid values without an enum or explicit constraint list.
Gamepad functions lack validation and error handling. 'gamepad_set_axes' accepts lx, ly, rx, ry (-32768 to 32767) but does not validate bounds or explain what happens if vgamepad is not installed. If vgamepad import fails, _pad remains None and subsequent calls will silently fail or crash.
Allowlist-based access control is enforced via '_is_allowed()' in control tools, but 'set_allowlist' itself is not gated. Any LLM can call 'set_allowlist' with an empty list, disabling all subsequent control tools. Should require pre-configuration or explicit authorization.
Rate limiting via '_rate_limit()' is implemented (MAX_EVENTS_PER_SEC=120) but not documented in tool descriptions or error responses. If an LLM hits the rate limit, it receives no feedback, calls simply block silently for up to 8.3ms. Should return explicit rate-limit-exceeded errors with retry guidance.
Keyboard input via SCAN dict is hardcoded and incomplete. Only ~15 keys are defined (w, a, s, d, space, enter, esc, etc.). Calling key_tap('x') or key_tap('tab') will fail silently or return 'Unknown key'. LLMs cannot know which keys are supported without explicit documentation of the valid keyset.
No idempotency or retry semantics documented. Control operations like 'key_tap' send raw input events that have side effects. If an LLM retries a 'key_tap' call, the key will be tapped twice. Should either be idempotent (e.g., with deduplication tokens) or document that retries are unsafe.
Composition between key_down/key_up is implicit. LLMs must remember to call key_up after key_down, or the key will remain held forever. Should either provide a 'key_hold' tool that auto-releases after a duration, or explicitly document that key_down REQUIRES a subsequent key_up call.