Skip to content
plugmcp
DirectorySkillsCategoriesBlog
EN/ไธญๆ–‡
Sign inSign up
Home/Servers/Screen Control

Screen Control

I
by io.github.Xeakaes

Safety-gated computer use for AI agents: screen OCR and vision, mouse, keyboard, window control.

Power your agent

Add this server

Install from the publisher. Not a PlugMCP endpoint.

No packaged release in the registry yet. Follow the repository README.

Provider
Step-by-step: add this in Claude

Remote URL or a command on your machine? How the two installs differ

Run on PlugMCP

Host a copy with our keys and logs.

A new server you create. It is not this listing's official endpoint. Use the publisher install when you want their URL and their credentials.

Launch

Related MCP

View more
  • Firecrawl๐Ÿ”ฅ Official Firecrawl MCP Server - Adds powerful web scraping and search to Cursor, Claude and any other LLM clients.
  • N
    NotionToken-auth Notion MCP: pages, databases, blocks, comments, files. Runs headless in CI and agents.
  • Context7Context7 Platform -- Up-to-date code documentation for LLMs and AI code editors

Related skills

View all
  • reaReverse engineer anything with agents, from app behavior down to native binaries.
  • rea-changelog-updatePrepare or rewrite REA release changelogs and GitHub release notes from pinned Git history, with verified contributor thanks and Release Please synchronization.
  • rea-tool-designDesign or change REA investigation tools, CLI/MCP contracts, provider capabilities, and Evidence semantics.

Questions

How do I install Screen Control?
Copy the stdio config for Cursor, Claude Desktop, Claude Code, or VS Code on this page. It uses the publisher's install, not a PlugMCP proxy.
Does Screen Control need an API key?
No required environment variable is listed on this record.
What transport does Screen Control use?
Screen Control runs locally over stdio.
When was Screen Control last checked?
Last checked 2026-10-16. Safety-gated computer use for AI agents: screen OCR and vision, mouse, keyboard, window control.

Safety-gated computer use for AI agents: screen OCR and vision, mouse, keyboard, window control.

๐Ÿ–ฅ๏ธ Computer use for all agents

A local remote-control system for AI agents: watch your computer's screen live and send mouse/keyboard commands to it. Everything runs on your own machine โ€” no data ever leaves it, no cloud middleman.


If You Are an AI Agent

Read AGENT_GUIDE.md first โ€” it is written for you: endpoints, safety gates and working recipes. This README is the human-facing reference.

First ask the server what it is โ€” GET /api/capabilities โ†’ platform. Windows: follow the whole guide as written. Linux: check echo $XDG_SESSION_TYPE โ€” X11 is supported (guide โ†’ Linux (X11) Usage); Wayland is unsupported: the server refuses to start, HTTP 501 UNSUPPORTED_DISPLAY_SERVER (switch to an X11 session). Full routing lives in AGENT_GUIDE.md โ†’ Find Your Environment First.


Why Screen Control?

AI agents today can write code and call APIs โ€” but they can't see or touch your desktop. Screen Control gives any agent general-purpose computer use over a clean, safety-gated HTTP/MCP interface:

  • Perceive โ€” OCR for text, single frames or a live MJPEG stream for vision-capable models, and a text-only diff endpoint for models that can't consume images at all.
  • Act โ€” absolute and relative mouse, Unicode-safe keyboard, window management, background (focus-free) control, virtual desktops.
  • Stay safe โ€” token auth, blocked deadly shortcuts, focus guard, a stuck-input watchdog and an emergency failsafe are all enforced server-side, no matter how confused the agent gets.

One process, zero configuration, works with any language that can speak HTTP โ€” or natively through MCP in Claude Desktop, Cursor, VS Code and cloud agents.

Performance Is Agent-Bound

Screen Control is the perception and actuation layer โ€” the eyes and hands. The effective speed and capability of any agent using it are bounded by that agent itself and by the environment it runs in:

  • Thinking speed โ€” one action per agent "turn": the perceive โ†’ plan โ†’ act โ†’ verify loop lives in the agent, so model inference latency and reasoning depth directly set the pace. The API itself adds only milliseconds per call.
  • Context capacity โ€” screen readings (OCR text, frames, diffs) consume the agent's context window; a larger window means more situational awareness before verification degrades.
  • Runtime environment โ€” network latency, MCP/HTTP round-trip overhead, tool-call limits and hosting constraints all stack on top of the loop.

In practice this means: the same repo makes a fast reasoning model fast and capable, and makes a slow model slow โ€” the toolchain is not the bottleneck. Real-time or action-heavy tasks need an agent with fast inference and tight tool-loop latency; slower agents should prefer deliberate, verification-heavy tasks.


Table of Contents

  • If You Are an AI Agent
  • Why Screen Control?
  • Features
  • Architecture
  • Installation
  • Linux (X11)
  • Quick Start
  • API Reference
    • Authentication
    • Screen Capture
    • Mouse Control
    • Keyboard Control
    • OCR (Screen Reading)
    • Vision Access (Image Models)
    • Window Management
    • Focus-Free (Background) Control
    • Virtual Desktops
    • Game Mode
    • Safety Endpoints
  • ๐Ÿค– For AI Agents
  • MCP Support (One-Click Cloud Agents)
  • Security Model
  • Game Mode Guide
  • Vision Access Guide
  • Troubleshooting
  • Testing
  • Contributing
  • License

Features

FeatureDescription
๐Ÿ–ผ๏ธ Live screen feedContinuously refreshing screenshot in the browser
๐Ÿ–ฑ๏ธ Mouse controlClick, right-click, double-click, scroll, drag & drop via live screenshot
โŒจ๏ธ Keyboard controlText typing (Unicode/Turkish included, layout-independent), keys and shortcuts (Ctrl+C, Alt+Tabโ€ฆ)
๐Ÿ‘๏ธ OCRConverts on-screen text to machine-readable format
๐Ÿ“ท Vision accessRaw-pixel paths for image-capable models: single frames, MJPEG stream, text-based motion detection
๐ŸชŸ Window managementList, focus, safe close (WM_CLOSE), kill (task-manager style)
๐Ÿ–ฅ๏ธ Focus-free controlRead/write background windows via PostMessage without stealing focus
๐ŸŽฎ Game modeCamera look via relative mouse movement, hold-to-move keys
๐Ÿ” Token authEvery request requires X-Auth-Token (CSRF protection)
๐Ÿฆบ Stuck-input watchdogAuto-releases held keys after 30 s of inactivity
๐Ÿ›Ÿ FailsafeCursor to top-left corner aborts all commands (disabled in game mode)

Architecture

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Browser (Web UI)                      โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
โ”‚  โ”‚  Live     โ”‚  โ”‚  Control  โ”‚  โ”‚  Windows / Game Mode   โ”‚ โ”‚
โ”‚  โ”‚  View     โ”‚  โ”‚  Panel    โ”‚  โ”‚  Panel                 โ”‚ โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
โ”‚       โ”‚              โ”‚                     โ”‚              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
        โ”‚              โ”‚                     โ”‚
        โ–ผ              โ–ผ                     โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                  HTTP API (Flask)                        โ”‚
โ”‚                  127.0.0.1:8745                           โ”‚
โ”‚                                                         โ”‚
โ”‚  /api/screenshot    /api/mouse     /api/key              โ”‚
โ”‚  /api/vision/*      /api/ocr       /api/window           โ”‚
โ”‚  /api/game          /api/held      /api/release_all      โ”‚
โ”‚  /api/windows       /api/desktops  /api/desktop          โ”‚
โ”‚                                                         โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚  โ”‚ Auth Layer  โ”‚  โ”‚  Watchdog    โ”‚  โ”‚  OCR Engine  โ”‚   โ”‚
โ”‚  โ”‚ (token)     โ”‚  โ”‚  (30s auto)  โ”‚  โ”‚  (RapidOCR)  โ”‚   โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
        โ”‚              โ”‚                     โ”‚
        โ–ผ              โ–ผ                     โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                  control.py (Core)                        โ”‚
โ”‚                                                         โ”‚
โ”‚  Screen:  mss (fast capture), PIL (processing)          โ”‚
โ”‚  Mouse:   pyautogui (absolute), SendInput (relative)    โ”‚
โ”‚  Keyboard: pyautogui + SendInput+KEYEVENTF_UNICODE      โ”‚
โ”‚  Windows: Win32 API (EnumWindows, SetForegroundWindow)   โ”‚
โ”‚  Background: PrintWindow (capture), PostMessage (input)  โ”‚
โ”‚  Virtual Desktops: pyvda                                 โ”‚
โ”‚  Game Mode: ClipCursor + MOUSE_MOVE_RELATIVE             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚              backends/ (pluggable)           โ”‚
โ”‚  WindowsBackend  โ”‚ LinuxBackend โ”‚ MacOSBackendโ”‚
โ”‚      (full)      โ”‚    (X11)     โ”‚   (stub)   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Coordinates & Concurrency

Per-Monitor DPI awareness. control.py calls SetProcessDpiAwarenessContext(PER_MONITOR_AWARE_V2) at import time โ€” before the pyautogui import, because pyautogui touches coordinate APIs during import and would otherwise lock the process to the interpreter manifest's default (system-aware). With PMv2 active, every coordinate in the system is a physical pixel end to end: mss capture, OCR bounding boxes, pyautogui/SendInput clicks, ClipCursor. On High-DPI displays (125%/150% scaling) nothing drifts between what OCR reports and where the mouse clicks.

Lock architecture. The server uses two independent locks instead of one global lock:

LockProtectsEndpoints
_input_lockmouse, keyboard, game mode, window ops/api/mouse, /api/key, /api/game, /api/window/post, ...
_read_lockcapture, OCR, vision, enumeration/api/screenshot, /api/ocr, /api/vision/*, /api/windows, ...

A slow OCR (3โ€“5 s on a busy screen) no longer freezes concurrent screenshot or vision reads โ€” reads queue behind reads, inputs behind inputs.

Live-Loop Working Principle

This system is designed for a live perceive-act loop, not pre-written command chains:

  1. READ โ€” OCR or vision reads the screen before and after every action
  2. ONE ACTION โ€” each round sends a single command
  3. VERIFY โ€” acceptance is "it appeared on screen", not "I sent it"
  4. ADAPT โ€” if verification fails, the next step changes based on what is actually seen

This is enforced by the expect_hwnd guard: typing is refused (409) if the foreground window doesn't match the target.


Installation

cd screen-control
pip install -r requirements.txt

Requirements

PackagePurposeRequired?
mssFast screen captureโœ… Yes
pyautoguiMouse/keyboard controlโœ… Yes
pyvdaVirtual desktop managementโœ… Yes
flaskHTTP serverโœ… Yes
PillowImage processingโœ… Yes
rapidocr-onnxruntimeOCR (screen text reading)โš ๏ธ Optional

Note: The OCR package is large and may take a while to install. If it fails, everything else still works โ€” only the OCR feature is unavailable.

System Requirements

  • OS: Windows 10/11 (x64), or Linux (Ubuntu/Mint family, X11 session โ€” see Linux (X11))
  • Python: 3.10+
  • Display: Any resolution; the system adapts automatically

Linux (X11)

Linux is supported with limits (ROADMAP Phase 5) โ€” tested on Linux Mint with Cinnamon in an X11 session; other X11 desktop environments are expected to work. Windows remains the reference backend.

Requirements

  • OS: Ubuntu / Linux Mint family (apt-based)
  • Session: X11 โ€” check this first:
echo $XDG_SESSION_TYPE    # must print: x11
  • Wayland? Not supported โ€” the server fails fast instead of half-working (see Known limitations).
  • System packages (installed by the setup script below):
sudo apt install -y xdotool wmctrl imagemagick python3-venv python3-tk python3-dev

xdotool drives input/focus, wmctrl window management, ImageMagick's import captures window pixels (optional โ€” an mss region fallback exists), python3-venv creates the virtualenv and python3-dev lets evdev (game mode) compile from source.

One-command setup

cd screen-control
./install-linux.sh

The script is idempotent and checks, in order: Wayland (exits with a clear message), DISPLAY, the apt packages, the .venv virtualenv, pip install -r requirements.txt, and finally reports whether game mode can work.

uinput permissions (game mode)

Game mode writes to /dev/uinput. Verify access with:

[ -w /dev/uinput ] && echo "game mode: OK" || echo "game mode: unavailable"

If it is unavailable, either remediation works (spec ยง5.4):

sudo usermod -aG input $USER        # then log out and back in
# โ€” or a udev rule:
echo 'KERNEL=="uinput", MODE="0660", GROUP="input"' | sudo tee /etc/udev/rules.d/99-uinput.rules
sudo udevadm control --reload && sudo udevadm trigger

Everything except game mode works without this access.

Run

./start-server.sh
# โ†’ same API as Windows: http://127.0.0.1:8745 (X-Auth-Token from .token)

Known limitations (Linux X11)

LimitationDetail
X11 onlyOn Wayland the backend fails fast: ApiError("UNSUPPORTED_DISPLAY_SERVER") (HTTP 501), and server.py prints that message and exits instead of serving. Fix: log out โ†’ session chooser at the login screen โ†’ "Cinnamon on Xorg". Wayland support is ROADMAP Phase 6.
Background input = focus-firstEvery /api/window/post action focuses the target window first, then sends input globally (X11 has no reliable posted-message path) โ€” i.e. focus stealing. capabilities.background_input is false.
No cursor clip in game modeX11 cannot clip the cursor โ€” the game must capture the pointer itself. Emergency stop: physical Esc/Alt+Tab or POST /api/release_all.
No virtual desktopscapabilities.virtual_desktops is false; GET /api/desktops and POST /api/desktop return 503.
Fractional scaling untestedThe verification display runs at scaling 1.0; coordinates under fractional scaling are unverified.
capture_window (window capture)Client area only, for both client=0 and client=1 โ€” decorations are not captured (on Windows client=0 includes the frame). Uses ImageMagick import when installed (usually the window's own pixels even when occluded); otherwise falls back to an mss region grab of the window's rectangle, so an occluded window shows whatever is on top. Failures map to HTTP 409.
Window geometry sourcerect comes from xwininfo -root -tree absolute bounds โ€” outer/frame bounds (Windows GetWindowRect parity); client_to_screen uses xwininfo "Absolute upper-left".
window_hotkey responseReturns {"ok","vk","vks"}; for the hotkey action vk (X11 keysym) exists only on Linux (Windows returns {"ok","vks"}) โ€” cross-platform code must read vks.

Agent note: OCR is the slowest read (โ‰ˆ5โ€“6 s full-screen measured). Prefer /api/vision/frame or /api/stream for repeated reads, use region OCR, and batch reads between actions โ€” see AGENT_GUIDE.md โ†’ Linux (X11) Usage.


Quick Start

# 1. Start the server
cd screen-control
python server.py

# 2. Open in browser
#    http://127.0.0.1:8745

# 3. Or control via API
TOKEN=$(cat .token)
curl -H "X-Auth-Token: $TOKEN" http://127.0.0.1:8745/api/screenshot -o screen.jpg

API Reference

Capabilities

Returns the active backend name and what it can do. Agents should call this first (see ROADMAP.md for the multi-platform plan).

GET /api/capabilities
โ†’ {"ok": true, "backend": "windows",
   "capabilities": {"screen_capture": true, "game_mode": true, ...}}

Feature values: true (supported), false (absent), null (unknown โ€” stub backend), "optional" (depends on an optional dependency).

Platform Support Matrix

CapabilityWindowsLinux X11Linux Waylandโ€ macOS
Screen captureFullFullPortal-dependentPermission required
OCRFull/optionalFull/optionalFull/optionalFull/optional
Mouse controlFullFullRestrictedAccessibility permission
Keyboard controlFullFullRestrictedAccessibility permission
Window enumerationFullWM-dependentLimitedAccessibility/API-dependent
Background inputStrongFocus-first (stealing)Usually unavailableLimited
Virtual desktopsSupportedNo (503)DE/WM-dependentSpaces-specific
Game modeSupportedExperimentalLimitedExperimental

Linux X11 is implemented and supported with limits โ€” setup, requirements and the full limitations list are in Linux (X11). Wayland fails fast with UNSUPPORTED_DISPLAY_SERVER (501), and macOS remains a fail-closed stub: every operation returns BACKEND_UNAVAILABLE (501) until ROADMAP Phase 7. Windows is the reference backend.

โ€  Wayland column is hypothetical capability planning only โ€” the server never starts on Wayland (see the note above), so no Wayland cell is currently reachable.

Authentication

Every request must include the X-Auth-Token header. The token is generated on each server start and written to .token.

TOKEN=$(cat .token)
CodeMeaning
401Missing or invalid token
415POST without Content-Type: application/json

Token bootstrap (for the bundled web UI):

GET /token
โ†’ {"ok": true, "token": "abc123..."}

The /token endpoint is safe: Same-Origin Policy prevents foreign pages from reading it.


Screen Capture

GET /api/screenshot

Returns a JPEG screenshot.

ParameterTypeDefaultDescription
monitorint1Monitor index
regionstringโ€”x,y,w,h sub-region
curl -H "X-Auth-Token: $TOKEN" -o screen.jpg http://127.0.0.1:8745/api/screenshot
curl -H "X-Auth-Token: $TOKEN" "http://127.0.0.1:8745/api/screenshot?region=0,0,800,600"
GET /api/info

Returns screen dimensions and system state.

{"ok": true, "width": 1920, "height": 1080, "ocr_available": true,
 "failsafe": true, "game_mode": false}

Mouse Control

POST /api/mouse
actionRequired paramsOptional paramsDescription
movex, yduration (default 0.15)Move cursor to absolute position
clickx, ybutton (left/right), clicks (default 1)Click at position
scrollclicksx, yScroll wheel (positive=up)
dragx1, y1, x, yduration, buttonDrag between two points
downbutton (default "left")โ€”Press and hold mouse button
upbutton (default "left")โ€”Release held mouse button
# Click at center of screen
curl -X POST http://127.0.0.1:8745/api/mouse -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"action":"click","x":960,"y":540,"button":"left"}'

# Right-click
curl -X POST http://127.0.0.1:8745/api/mouse -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"action":"click","button":"right","x":960,"y":540}'

# Scroll down
curl -X POST http://127.0.0.1:8745/api/mouse -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"action":"scroll","clicks":-3}'

Keyboard Control

POST /api/key
actionRequired paramsDescription
presskeyPress and release a key
downkeyHold a key down (tracked for watchdog)
upkeyRelease a held key
hotkeykeys (array)Key combination (e.g. ["ctrl","c"])
typetextType text (Unicode, layout-independent)
Optional paramDefaultDescription
expect_hwndโ€”Window handle to verify focus (409 if mismatch)
interval0.03Delay between characters for type
# Press Enter
curl -X POST http://127.0.0.1:8745/api/key -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" -d '{"action":"press","key":"enter"}'

# Ctrl+C
curl -X POST http://127.0.0.1:8745/api/key -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" -d '{"action":"hotkey","keys":["ctrl","c"]}'

# Type text (Turkish characters supported)
curl -X POST http://127.0.0.1:8745/api/key -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" -d '{"action":"type","text":"Merhaba dรผnya"}'

# Hold W key down (for walking in games)
curl -X POST http://127.0.0.1:8745/api/key -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" -d '{"action":"down","key":"w"}'

OCR (Screen Reading)

POST /api/ocr

Converts on-screen text to machine-readable format.

ParamTypeDefaultDescription
regionarrayโ€”[x, y, w, h] sub-region (faster)
{
  "ok": true,
  "text": "Hello World\nFile Edit View",
  "lines": ["Hello World", "File Edit View"],
  "items": [
    {"text": "Hello World", "x": 960, "y": 40},
    {"text": "File Edit View", "x": 100, "y": 15}
  ]
}
# Full screen OCR
curl -X POST http://127.0.0.1:8745/api/ocr -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" -d '{}'

# Region-only (faster, ~10x for small regions)
curl -X POST http://127.0.0.1:8745/api/ocr -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" -d '{"region":[0,0,800,100]}'

Vision Access (Image Models)

Three endpoints for models that can consume images:

EndpointDescription
GET /api/vision/frameSingle JPEG frame (raw or base64)
GET /api/streamMJPEG live stream
POST /api/vision/diffText-based motion detection (no vision needed)
GET /api/vision/frame
ParamDefaultDescription
scale1.0Downscale factor (0.5 = half size)
gray01 for greyscale
quality80JPEG quality (20-95)
formatโ€”base64 for JSON response
regionโ€”x,y,w,h sub-region
# Half-size greyscale frame as base64 (for text-only models)
curl "http://127.0.0.1:8745/api/vision/frame?scale=0.5&gray=1&format=base64" \
  -H "X-Auth-Token: $TOKEN"
GET /api/stream

MJPEG live stream. Drop into <img src> or consume frame-by-frame.

ParamDefaultDescription
fps10Frames per second (1-30)
quality70JPEG quality
scale1.0Downscale factor
regionโ€”x,y,w,h sub-region
POST /api/vision/diff

Text-based motion detection โ€” no vision model required.

BodyDescription
{}Compare against last stored frame
{"grab":"gray"}Store current frame for next comparison
{"b64_prev":"..."}Compare against provided previous frame
{
  "ok": true,
  "changed": true,
  "changed_pct": 12.5,
  "bbox": [100, 200, 400, 350],
  "tiles": [
    {"row": 2, "col": 4, "pct": 35.2, "center": [1000, 390]}
  ]
}

Window Management

GET /api/windows

List all visible windows.

{
  "ok": true,
  "windows": [
    {
      "hwnd": 123456,
      "title": "My Application",
      "process": "app.exe",
      "pid": 7890,
      "focused": true,
      "rect": [0, 0, 1920, 1080],
      "desktop": 1
    }
  ]
}
POST /api/window
actionRequiredOptionalDescription
focushwndโ€”Bring window to foreground
closehwndexpect_title, expect_processSafe close via WM_CLOSE
killhwnd, pidโ€”Force kill (task-manager style)
topmosthwndโ€”Set always-on-top
untopmosthwndโ€”Remove always-on-top
maximizehwndโ€”Maximise window
# Focus a window
curl -X POST http://127.0.0.1:8745/api/window -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" -d '{"hwnd":12345,"action":"focus"}'

# Safe close (with title verification)
curl -X POST http://127.0.0.1:8745/api/window -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"action":"close","hwnd":12345,"expect_title":"Notepad"}'

# Kill process
curl -X POST http://127.0.0.1:8745/api/window -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" -d '{"action":"kill","hwnd":12345,"pid":7890}'
POST /api/launch

Spawn an application, open a URL, or open a local file. Exactly one of app | url | file. Never runs through a shell: app is a plain command name resolved on PATH ([A-Za-z0-9._+-]+), url/file go through xdg-open.

FieldTypeDescription
appstringCommand name; must exist on PATH (else 400)
urlstringscheme://... (e.g. https://, steam://)
filestringAbsolute path to an existing file
argslistArguments for app only
expect_titlestringAlso wait for a window whose title contains it
timeoutnumberWait for expect_title: default 10, max 30 s
{"ok": true, "pid": 4321, "window": {"hwnd": ..., "title": "..."}}

With expect_title, window is the match or null (spawn succeeded, no window yet) โ€” the HTTP status stays 200 either way.

curl -X POST http://127.0.0.1:8745/api/launch -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"app":"firefox","expect_title":"Mozilla Firefox","timeout":15}'
GET /api/window/wait

Block until a window whose title contains title (case-insensitive substring) appears, then return it. timeout default 10 s, max 30 s; 200 with the window, or 408 OPERATION_TIMEOUT.

curl -H "X-Auth-Token: $TOKEN" \
  "http://127.0.0.1:8745/api/window/wait?title=Notepad&timeout=10"

Focus-Free (Background) Control

Read and control windows without stealing focus โ€” the user keeps working on their main desktop.

Linux (X11) note: background control there is focus-first โ€” the target window is focused, then input is sent globally (focus stealing). Only background capture is truly focus-free. See Known limitations.

GET /api/window/capture

Capture a window via PrintWindow (works even on another virtual desktop).

ParamDescription
hwnd (required)Window handle
client1 = client area only
ocr1 = return OCR text instead of image
# Capture window as PNG
curl "http://127.0.0.1:8745/api/window/capture?hwnd=12345" \
  -H "X-Auth-Token: $TOKEN" -o window.png

# Capture + OCR in one call
curl "http://127.0.0.1:8745/api/window/capture?hwnd=12345&ocr=1" \
  -H "X-Auth-Token: $TOKEN"
POST /api/window/post

Send input to a window, choosing the delivery path automatically.

actionDescription
typeType text (Unicode-safe)
keySend a key press
hotkeySend a key combination
clickClick at client coordinates
scrollScroll the window
dragDrag inside the window

Optional mode parameter controls routing:

modeBehavior
auto (default)Decided by input-mode probe (see below)
backgroundForce PostMessage path (window keeps focus/z-order)
focusedForce focus + SendInput path
# Type into a background Notepad
curl -X POST http://127.0.0.1:8745/api/window/post -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"hwnd":12345,"action":"type","text":"Hello from background!"}'

Routing rules (mode=auto):

  • postmessage โ€” classic Win32 app: background PostMessage, no focus change.
  • uia โ€” WinUI/UWP/XAML surface (single DirectX canvas, no Win32 child controls): posted messages are silently swallowed, so the window is focused and the action is replayed through SendInput (client coords converted to screen). This is the documented fallback for modern apps.
  • focused โ€” window is already foreground: focused SendInput path.
  • invalid โ€” HTTP 409; not a reachable top-level window.
GET /api/window/input-mode

Classify how a window receives input before posting to it. Returns one of focused | postmessage | uia | invalid.

curl "http://127.0.0.1:8745/api/window/input-mode?hwnd=12345" \
  -H "X-Auth-Token: $TOKEN"

WinUI note: New Notepad (and other XAML-hosted apps) has no classic child Edit control to post to โ€” the whole UI is one DirectX surface. input-mode reports uia for these; /api/window/post then automatically uses the focused SendInput path. /api/window/children remains useful for classic apps with real child controls.

GET /api/window/children

List child controls of a window (class name + title + hwnd).

curl "http://127.0.0.1:8745/api/window/children?hwnd=12345" -H "X-Auth-Token: $TOKEN"

Virtual Desktops

GET /api/desktops

List all virtual desktops.

POST /api/desktop
actionParamsDescription
switchnumberSwitch to desktop N
createโ€”Create a new desktop
curl http://127.0.0.1:8745/api/desktops -H "X-Auth-Token: $TOKEN"

curl -X POST http://127.0.0.1:8745/api/desktop -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" -d '{"action":"switch","number":2}'

Game Mode

actionParamsDescription
startsensitivity (default 12)Lock cursor to center, enable game input
movedx, dy, sensitivityRotate camera (relative mouse)
stopโ€”Release cursor + all held input
heartbeatโ€”Keep-alive for long holds
# Start game mode
curl -X POST http://127.0.0.1:8745/api/game -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" -d '{"action":"start","sensitivity":12}'

# Look right
curl -X POST http://127.0.0.1:8745/api/game -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" -d '{"action":"move","dx":50,"dy":0}'

# Hold W to walk forward
curl -X POST http://127.0.0.1:8745/api/key -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" -d '{"action":"down","key":"w"}'

# ... later ...
curl -X POST http://127.0.0.1:8745/api/key -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" -d '{"action":"up","key":"w"}'

# Stop game mode
curl -X POST http://127.0.0.1:8745/api/game -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" -d '{"action":"stop"}'

Safety Endpoints

GET /api/held

Returns currently held keys/buttons and watchdog status.

{
  "ok": true,
  "keys": ["w", "shift"],
  "buttons": ["left"],
  "game_mode": true,
  "idle_seconds": 5.2,
  "watchdog_count": 0,
  "last_watchdog": null
}
POST /api/release_all

Emergency: release everything (held keys, mouse buttons, game-mode cursor lock).

curl -X POST http://127.0.0.1:8745/api/release_all -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" -d '{}'


๐Ÿค– For AI Agents

A dedicated, comprehensive guide for AI agents (LLMs, vision models, automation frameworks) is available in AGENT_GUIDE.md.

A portable SKILL.md pack lives in skills/computer-use/ โ€” drop it into ~/.claude/skills/ (or any agent's skills folder) to get setup, the perceive-act loop, safety rules and a full API reference loaded on demand; the same pack is what we list on skill marketplaces.

It covers:

  • Environment routing โ€” find Windows vs Linux (X11/Wayland) first
  • Perceive-act loop (read โ†’ plan โ†’ act โ†’ verify)
  • Focus guard (expect_hwnd) to prevent wrong-window accidents
  • App automation and game control workflows
  • Linux (X11) usage: slow-OCR batching, capture semantics, game-mode caveats
  • Vision access for image-capable models
  • Text-based motion detection
  • Bandwidth optimization
  • Complete curl examples

MCP Support (One-Click Cloud Agents)

Model Context Protocol (MCP) turns this project into a plug-and-play toolbox for any MCP-capable agent: Claude Desktop, Claude Code, Cursor, VS Code Copilot Agent mode, custom cloud agents โ€” no custom glue code, no curl scripts. The agent discovers and calls the tools natively.

How it works

MCP agent (cloud or desktop)
        โ”‚  MCP protocol (stdio or streamable-HTTP)
        โ–ผ
  mcp_server.py   โ† thin wrapper: tools โ†’ HTTP calls, token auto-read
        โ”‚  REST + X-Auth-Token (localhost only)
        โ–ผ
  server.py       โ† the single source of truth:
                    auth, locks, watchdog, focus guard, all safety rules

mcp_server.py adds no new powers โ€” every safety mechanism (auth token, input/read locks, watchdog, Alt+F4 block, focus guard, failsafe) stays enforced by server.py.

Setup

pip install mcp            # optional dependency (see requirements.txt)
python server.py           # start the REST server first (it writes .token)

The MCP server auto-reads the token from .token (or the SCREEN_CONTROL_TOKEN env var) โ€” zero configuration.

Desktop agents (stdio transport)

Claude Desktop โ€” claude_desktop_config.json:

{
  "mcpServers": {
    "screen-control": {
      "command": "python",
      "args": ["C:/path/to/screen-control/mcp_server.py"]
    }
  }
}

Claude Code: claude mcp add screen-control -- python C:/path/to/screen-control/mcp_server.py

Cursor / VS Code: add the same entry to their MCP config files.

Remote / cloud agents (streamable-HTTP transport)

python mcp_server.py --http --port 8751
# MCP endpoint: http://127.0.0.1:8751/mcp

The HTTP transport is token-protected: every request must carry the X-Auth-Token header (same token as the REST server). Query-string tokens (?token=...) are rejected by design โ€” URLs leak into proxy/tunnel logs, browser history and shared links, and this token grants full desktop control. Clients that cannot send custom headers should run a local stdio mcp_server.py instead. Only GET /health is open, for liveness probes. DNS-rebinding protection is disabled on this transport deliberately โ€” tunneled requests arrive with a foreign Host header, and the rebinding threat is already covered by the token guard.

For a cloud agent, expose it through a tunnel:

cloudflared tunnel --url http://127.0.0.1:8751
# โ†’ prints a https://<random>.trycloudflare.com URL

Then configure the agent's MCP connection with <tunnel-url>/mcp plus the token from .token as a header (X-Auth-Token).

Headerless connectors (scoped keys)

For clients that cannot send custom headers (e.g. web connectors that only take an endpoint URL), create a scoped API key โ€” a persistent, optionally time-limited credential โ€” and embed it in the URL path:

# Create a 24-hour scoped key (requires the server to be running)
curl -X POST http://127.0.0.1:8745/api/keys -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"action":"create","name":"spark","expires_in_hours":24}'

# Connector endpoint becomes:
#   https://<tunnel-url>/mcp/<scoped-key>

Design guarantees (SC-06):

  • The master session token is refused in URLs (403) โ€” only scoped keys may travel there
  • Scoped keys expire automatically; expired keys authenticate nothing
  • Scoped keys are revocable by name at any moment via POST /api/keys ({"action":"revoke","name":"spark"}) โ€” revocation takes effect immediately on every endpoint
  • The .apikeys file stores only SHA-256 hashes, never raw keys

โš ๏ธ A tunnel exposes PC control to the internet. Keep the token secret, prefer short-lived tunnels and scoped keys for headerless connectors, and stop the server when not in use.

One-Command Startup (launcher + auto-tunnel)

start-server.bat automates the whole cloud setup and prints everything your cloud agent needs, ready to paste:

  1. Downloads cloudflared.exe if missing (portable, no admin required)
  2. Stops leftover instances from a previous run
  3. Starts the REST server (port 8745) and the MCP HTTP server (port 8751)
  4. Waits until both are healthy (/token and /health probes)
  5. Starts a cloudflared quick tunnel, extracts its public URL from tunnel.log, and prints the summary:
 ============================================================
  ALL SYSTEMS RUNNING
 ============================================================
  Local REST API  : http://127.0.0.1:8745
  Local MCP       : http://127.0.0.1:8751/mcp
  Public MCP URL  : https://<random>.trycloudflare.com/mcp

  ------------------------------------------------------------
  PASTE INTO YOUR CLOUD AGENT  (MCP connector settings)
  ------------------------------------------------------------
  Endpoint : https://<random>.trycloudflare.com/mcp
  Header   : X-Auth-Token: <token>
  URL form : https://<random>.trycloudflare.com/mcp?token=<token>
             (only if the connector cannot send headers)
  ------------------------------------------------------------

stop-server.bat stops all three (REST, MCP, tunnel) in one go.

Available tools (16)

CategoryTools
Perceptionget_info, ocr_screen, screenshot (real image block for vision models), motion_diff
Mouse / keyboardmouse, keyboard (with expect_hwnd), get_held, release_all
Windowslist_windows, focus_window, window_children, window_input_mode, window_post, window_capture_ocr, close_window
Game modegame (start / move / stop / heartbeat)

Which transport for whom

ConsumerTransportCommand
Claude Desktop / Cursor / VS Code (local)stdiopython mcp_server.py
Claude Codestdioclaude mcp add ... (above)
Cloud / remote agentsstreamable-HTTPstart-server.bat (recommended) or python mcp_server.py --http --port 8751 + cloudflared tunnel --url http://127.0.0.1:8751

Note: This project targets MCP Python SDK 2.x (MCPServer API). With SDK 1.x, replace the import with from mcp.server.fastmcp import FastMCP, Image and MCPServer with FastMCP.


Security Model

Threat: Malicious Web Pages (CSRF)

Even bound to 127.0.0.1, a malicious page in the browser can trigger non-preflighted requests (text/plain fetch, HTML form POST) to localhost. The browser blocks the response but not the request โ€” the server would still execute the command.

Mitigation: Every request requires X-Auth-Token. A foreign page cannot read this token (Same-Origin Policy), so it cannot authenticate.

Additional layers:

  • POST requests must use Content-Type: application/json (415 otherwise)
  • This blocks form-encoded and text-plain POSTs even if the token leaked
  • Host header trust (DNS rebinding): when bound to loopback, requests carrying a non-loopback Host header are refused with 421 โ€” a rebinding page that resolves its domain to 127.0.0.1 cannot read /token or call the API
  • /token and / responses carry Cache-Control: no-store so the credential is never persisted by browsers or proxies

Threat: Stuck Keys / Game Mode Lock

In game mode, ClipCursor pins the cursor to a 2ร—2 box โ€” the classic pyautogui failsafe (cursor to top-left) does not work.

Mitigations:

  1. Physical Esc / Alt+Tab โ€” real hardware input; this API cannot block it, and it always works
  2. POST /api/release_all โ€” instant release of everything
  3. Watchdog (automatic) โ€” 30 s of server-side inactivity with held input triggers automatic release

Threat: Wrong Window Typing

Mitigations:

  • expect_hwnd guard on /api/key โ€” if the foreground window doesn't match, typing is refused with 409
  • The focused path of /api/window/post verifies the focus after the focus switch and before any synthetic input (409 on mismatch) โ€” input is never replayed into whatever window happens to be foreground
  • focus_window() raises on failure instead of silently returning

Threat: Dangerous Key Combos

Mitigation: Blocked at the API level (403) on every delivery path โ€” the direct /api/key route, the background /api/window/post route (PostMessage), and the focused fallback route share one safety policy (control._assert_allowed):

  • Alt+F4 โ€” the only banned Alt combo (Alt+Tab, Alt+menu are legitimate)
  • Win key โ€” prevents Start menu, task switching
  • Ctrl+Alt+Del โ€” system security screen
  • Shift+Delete style โ€” prevents permanent deletion

Threat: Killing System Processes

Mitigations:

  • The process name is resolved from the PID directly (Win32 toolhelp snapshot), not from the visible-window inventory โ€” windowless/background system processes get the same protection as visible ones
  • Critical system processes are blacklisted (default-deny for unknown PIDs): winlogon.exe, csrss.exe, smss.exe, services.exe, lsass.exe, svchost.exe, system, registry, dwm.exe
  • Optional expect_process confirmation: a mismatch aborts the kill with 409 โ€” protects against killing a newly-reused PID

Threat: Resource Exhaustion (rogue agent / DoS)

A token-holding but misbehaving client should not be able to exhaust memory or starve the input lock.

Mitigations:

  • MAX_CONTENT_LENGTH = 1 MB โ€” oversized request bodies are rejected (413)
  • region width/height/area and scale are bounded (400 otherwise)
  • text payloads are capped at 10,000 characters per input call
  • MJPEG streams are capped at 10 concurrent clients (429 beyond that)
  • Pillow decompression-bomb limit is set for client-supplied images

Network Access

The server binds to 127.0.0.1 by default. To expose it to the network:

python server.py --host 0.0.0.0  # โš ๏ธ anyone on the network can control this machine

Game Mode Guide

Setup

# 1. Focus the game window
curl -X POST http://127.0.0.1:8745/api/window -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" -d '{"hwnd":GAME_HWND,"action":"focus"}'

# 2. Start game mode
curl -X POST http://127.0.0.1:8745/api/game -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" -d '{"action":"start","sensitivity":12}'

Camera Look

# Look right
curl -X POST http://127.0.0.1:8745/api/game -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" -d '{"action":"move","dx":50,"dy":0}'

# Look down
curl -X POST http://127.0.0.1:8745/api/game -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" -d '{"action":"move","dx":0,"dy":30}'

Movement

# Walk forward (hold W)
curl -X POST http://127.0.0.1:8745/api/key -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" -d '{"action":"down","key":"w"}'

# ... walk for a while ...

# Release W
curl -X POST http://127.0.0.1:8745/api/key -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" -d '{"action":"up","key":"w"}'

Minecraft-Specific

# Place block (right-click)
curl -X POST http://127.0.0.1:8745/api/mouse -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"action":"click","button":"right","x":960,"y":540}'

# Break block (hold left-click)
curl -X POST http://127.0.0.1:8745/api/mouse -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" -d '{"action":"down","button":"left"}'

# ... after breaking ...

curl -X POST http://127.0.0.1:8745/api/mouse -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" -d '{"action":"up","button":"left"}'

# Select hotbar slot
curl -X POST http://127.0.0.1:8745/api/key -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" -d '{"action":"press","key":"1"}'

# Open inventory
curl -X POST http://127.0.0.1:8745/api/key -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" -d '{"action":"press","key":"e"}'

Suitability

Game TypeSuitable?Notes
Minecraft (building)โœ… YesPlace blocks, walk, mine
Minecraft (PvP)โŒ NoToo slow for fast combat
Turn-based gamesโœ… YesAmple time for readโ†’actโ†’verify
RPG / adventureโœ… YesInventory, dialogue, exploration
Fast FPSโŒ NoReaction time insufficient
Puzzle gamesโœ… YesClick-based, read-heavy

Vision Access Guide

For Image-Capable Models

If the consuming model can process images, use the vision endpoints directly:

GET /api/vision/frame?scale=0.5&gray=1&quality=70

This returns a single JPEG that the model can analyze for:

  • Game HUD elements (health, mana, inventory)
  • On-screen text (menus, chat, tooltips)
  • Visual scene understanding (blocks, entities, terrain)

For Text-Only Models

Use the diff endpoint for motion detection without vision:

POST /api/vision/diff {"grab":"gray"}   โ†’ first call: stores frame
POST /api/vision/diff                    โ†’ subsequent calls: returns diff

The response tells you where things changed (tile coordinates) and how much (percentage), which is sufficient for:

  • Detecting that an action had an effect
  • Locating moving elements on screen
  • Tracking animation state changes

Bandwidth Optimization

ApproachPayloadUse Case
scale=1.0, gray=0~500 KBFull detail
scale=0.5, gray=1~50 KBGood for most vision models
scale=0.25, gray=1~10 KBMaximum compression
diff (text)~1 KBText-only agents
region=...VariableFocus on specific area

Troubleshooting

"OCR engine not installed"

pip install rapidocr-onnxruntime

Server won't start (port in use)

# Find the process using port 8745
netstat -ano | findstr ":8745"

# Kill it
taskkill /PID <pid> /F

"Focus mismatch" (409) when typing

The foreground window changed between the focus call and the type call. Solution: always pass expect_hwnd and verify focus before typing.

Window not found

The window may have been closed or may be a system window that EnumWindows doesn't expose. Try:

curl http://127.0.0.1:8745/api/windows -H "X-Auth-Token: $TOKEN"

Game mode cursor stuck

Use POST /api/release_all or press Esc / Alt+Tab physically.

High OCR latency

OCR on a full 1920ร—1080 screen can take from a few seconds up to ~30 s depending on your CPU and on-screen complexity. Use a region โ€” small crops are typically 10ร— faster:

{"region": [0, 0, 800, 100]}

Turkish characters not appearing

The system uses SendInput + KEYEVENTF_UNICODE which is layout-independent. If characters still don't appear, the target app may not support Unicode input โ€” try POST /api/window/post with action: "type" instead.

Linux: server exits immediately with "Wayland display server detected"

Expected on a Wayland session โ€” the backend fails fast with UNSUPPORTED_DISPLAY_SERVER (501) instead of half-working. Log out, choose the X11 session ("Cinnamon on Xorg") at the login screen, then start the server again. See Linux (X11).

Linux: "xdotool is not installed or not on PATH" (501)

The X11 helper tools are missing. Install them (or re-run the installer):

sudo apt install xdotool wmctrl

Project Structure

screen-control/
โ”œโ”€โ”€ server.py            # Flask HTTP server + all API endpoints
โ”œโ”€โ”€ core/                # PlatformBackend interface + standardized errors
โ”‚   โ”œโ”€โ”€ backends.py      # Abstract backend + lazy discovery
โ”‚   โ””โ”€โ”€ errors.py        # ApiError envelope + error codes
โ”œโ”€โ”€ backends/            # OS implementations behind PlatformBackend
โ”‚   โ”œโ”€โ”€ windows.py       # Reference backend (moved from control.py)
โ”‚   โ”œโ”€โ”€ linux.py         # X11 backend: xdotool/wmctrl + mss + uinput (Phase 5)
โ”‚   โ”œโ”€โ”€ imageops.py      # Shared PIL helpers (frame diff, JPEG, scaling)
โ”‚   โ”œโ”€โ”€ uinput.py        # Virtual input device for Linux game mode
โ”‚   โ”œโ”€โ”€ macos.py         # Fail-closed stub (ROADMAP Phase 7)
โ”‚   โ”œโ”€โ”€ fake.py          # In-memory backend for tests
โ”‚   โ””โ”€โ”€ forbidden.py     # Shared blocked-key policy
โ”œโ”€โ”€ control.py           # Compatibility shim re-exporting backends.windows
โ”œโ”€โ”€ tests/unit/          # Offline unit + integration tests (no real input)
โ”œโ”€โ”€ mcp_server.py        # MCP server (stdio + streamable-HTTP) โ€” thin wrapper over the API
โ”œโ”€โ”€ sdk/
โ”‚   โ””โ”€โ”€ screen_control.py  # Python SDK client (pip-installable style)
โ”œโ”€โ”€ index.html           # Bundled web UI (live view + control panels)
โ”œโ”€โ”€ docs/images/         # README assets (demo GIF captured by the API itself)
โ”œโ”€โ”€ requirements.txt     # Python dependencies
โ”œโ”€โ”€ start-server.bat     # One command: REST + MCP + cloud tunnel (Windows)
โ”œโ”€โ”€ stop-server.bat      # Stop all three processes
โ”œโ”€โ”€ install-linux.sh     # One-command Linux setup (apt, venv, uinput check)
โ”œโ”€โ”€ start-server.sh      # Linux launcher (.venv/bin/python server.py)
โ”œโ”€โ”€ test-security.py     # Security + game-mode test suite (34 checks)
โ”œโ”€โ”€ test-linux.py        # Live Linux X11 acceptance suite (7 items)
โ”œโ”€โ”€ test-game.py         # Live game-mechanics test (app launch โ†’ draw โ†’ safe close)
โ”œโ”€โ”€ test-endtoend.py     # End-to-end test: open Notepad โ†’ type โ†’ save โ†’ verify
โ”œโ”€โ”€ .github/workflows/   # CI: runs the security suite on every push
โ”œโ”€โ”€ AGENT_GUIDE.md       # AI agent integration guide (separate from this file)
โ”œโ”€โ”€ README.md            # This file
โ””โ”€โ”€ .token               # Auto-generated auth token (gitignored)

Testing

Prerequisites

The server must be running:

cd screen-control
python server.py

Security test suite

Tests authentication, blocked key combos, window management, safe close, critical process protection, and game mode โ€” all non-destructive.

cd screen-control
python test-security.py

Expected output:

== Token Authentication ==
โœ“ Missing token -> 401
โœ“ Wrong token -> 401
โœ“ Correct token -> 200
โœ“ Non-JSON POST -> 415

== Blocked Key Combos ==
โœ“ Alt+F4 blocked (403)
โœ“ Win key blocked (403)
โœ“ Win+D blocked (403)
โœ“ Delete blocked (403)

== Window List ==
โœ“ Windows list requires GET
โœ“ Window list is non-empty  โ€” 8 windows
โœ“ Exactly one focused window

== Safe Close Verification ==
โœ“ Wrong title aborts close

== Critical Process Protection ==
โœ“ System process (pid 4) rejected (403)
โœ“ pid 0 rejected (403)

== Game Mode ==
โœ“ Game mode started
โœ“ Relative camera look
โœ“ Game mode stopped

== Watchdog (dry run) ==
โœ“ Held state returns ok
โœ“ Watchdog count reported

========================================
RESULT: 19 passed, 0 failed

Live game-mechanics test

Launches a real application (mspaint or notepad), performs hold-to-draw game mechanics, verifies via pixel analysis, then safely closes with "Don't Save" dialog handling.

cd screen-control
python test-game.py

Note: This test launches a real application. It handles cleanup automatically (sends WM_CLOSE and clicks "Don't Save" if a dialog appears).

Live Linux acceptance test (Linux X11 only)

Seven-item live check of the Linux backend: mouse move/click, typing + hotkey, screenshot, OCR, window list/focus, game mode (uinput) and the native-game item (printed as SKIP โ€” no Linux game is installed). Needs a running server and a real X11 session; every helper window is pid-unique and only those are ever focused or clicked.

cd screen-control
./start-server.sh &              # server must be running
.venv/bin/python test-linux.py   # โ†’ "RESULT: 6 passed, 0 failed"; item 7 prints SKIP

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Test on a Windows machine
  5. Submit a pull request

Code Style

  • Python: PEP 8, type hints, docstrings on all public functions
  • Docstrings: English, Google style
  • Error messages: English, descriptive
  • Comments: English, explain why not what

License

MIT License. See LICENSE for details.


Built with โค๏ธ for local automation and AI agent research.

How do I install Screen Control?
Copy the stdio config for Cursor, Claude Desktop, Claude Code, or VS Code on this page. It uses the publisher's install, not a PlugMCP proxy.
Does Screen Control need an API key?
No required environment variable is listed on this record.
What transport does Screen Control use?
Screen Control runs locally over stdio.
When was Screen Control last checked?
Last checked 2026-10-16. Safety-gated computer use for AI agents: screen OCR and vision, mouse, keyboard, window control.

No tool list yet. Remote servers get one after the first health check.

plugmcp

Plug any tool into any agent. MCP servers for people who do not write servers. Gateway for volume, n8n for apps.

  • github.com/TODO
  • x.com/TODO
  • linkedin.com/company/TODO

PlugMCP, 2026

Product
  • Browse
  • Install
  • Host your own
  • Pricing
  • Voice mode
  • Browser
Directory
  • All MCP servers
  • MCP Apps
  • Tools
  • Agent skills
  • Categories
  • New
  • Voice
  • Submit a server
Developers
  • Docs
  • Blog
  • Club
  • Work with us
  • What is MCP
  • What is WebMCP
  • Status
Company
  • About
  • Contact
  • Terms
  • Privacy
  • DPA