winauto-mcp
MCP server that lets an LLM see and drive any running Windows app: attach a window, screenshot it, read its UI Automation tree, and send clicks, typed text, and key presses with pixel coordinates.
README
winauto-mcp
MCP server that lets an LLM see and drive any running Windows app: pick a window, screenshot it, read its UI Automation element tree (buttons/menus/ inputs with coordinates), click, type, and press keys. A transparent green outline tracks whichever window is currently attached; red boxes can be drawn on demand to highlight specific elements.
Windows-only. Built and tested against Python 3.12 on Windows 11.
Design
- Coordinates: every coordinate a tool accepts or returns (click x/y,
element rects, highlight rects) is relative to the attached window's
client area — the same pixel space as the image
capture_screenreturns. The server does all client-rect <-> screen-rect translation internally. - Hybrid inspection:
capture_screenreturns both an image and a text summary built from the Windows UI Automation (UIA) tree. For native apps (browsers, Win32/WPF/UWP apps) the UIA summary is usually enough to act on by name. For canvas-rendered apps (games, custom-drawn UIs) the UIA tree comes back empty — the calling LLM should read the screenshot directly and pass pixel coordinates toclick. - Single active target: one attached window at a time (
attach_windowreplaces whatever was attached before). - Input simulation: uses the Win32
SendInputAPI directly (notpyautogui), withKEYEVENTF_UNICODEfor typing — so arbitrary Unicode text (Thai included) types correctly without keycode mapping. - DPI awareness: the server calls
SetProcessDpiAwarenessContext(with fallbacks) at startup, before any other Win32/GDI call. Without this, an unaware process gets its coordinates silently scaled to 96 DPI whilemsscaptures true physical pixels — on any scaled display (the common case) click coordinates read off a screenshot would land in the wrong place. - Foreground-lock workaround:
attach_windowusesAttachThreadInputto temporarily join input queues with the current foreground window's thread before callingSetForegroundWindow— otherwise Windows silently ignores that call when it comes from a background/automated process (this server).
Tools
| Tool | Purpose |
|---|---|
list_windows |
List visible top-level windows (title, process, hwnd) |
attach_window(hwnd) |
Attach to a window, foreground it, show tracking outline |
detach_window() |
Detach and hide the overlay |
capture_screen() |
Screenshot + UIA text summary of the attached window |
screenshot() |
Screenshot only, no UIA tree walk — fast, for re-checking the screen |
get_elements() |
UIA element list only, as JSON (no screenshot) |
click(x, y, button, double, modifiers) |
Click at client-relative coordinates; modifiers (e.g. ["ctrl"]) are held down for the click -- for ctrl/shift-click multi-selection |
click_element(name, button, double) |
Click a UIA element by visible name (exact, else substring match) |
wait_for(name, timeout, interval) |
Poll until an element matching name appears, or time out |
type_text(text) |
Type Unicode text into the focused control |
press_key(key) |
Press a named special key (enter, tab, escape, arrows, f1-f12, ...) |
hotkey(keys) |
Press a chord together, e.g. ["ctrl", "shift", "p"] for Ctrl+Shift+P |
scroll(x, y, clicks) |
Mouse-wheel scroll at client-relative coordinates (positive = up, negative = down) |
drag(x1, y1, x2, y2, button) |
Drag from one point to another -- moves through intermediate points, not a teleport, since many apps only recognize a drag if the mouse visibly moves while held |
locate_in_region(x1, y1, x2, y2, threshold) |
Find exact click coordinates by pixel contrast within a small region -- returns the tight content bbox and its center. Use instead of eyeballing coordinates off a displayed screenshot crop, which has repeatedly been wrong by 50-150+ px (displayed crops can be rescaled in ways that don't map back to real source pixels) |
snapshot() |
Store the current screenshot as a reference point |
diff_since_snapshot(threshold) |
Compare the current screen to the last snapshot(), return the bounding box of changed pixels or "no change detected" -- objective confirmation an action had a visible effect, instead of eyeballing two screenshots side by side |
remember_location(label, x1, y1, x2, y2) |
Save a click target under a semantic label, scoped to the attached process's name + client size |
recall_location(label, margin=15, threshold) |
Look up a saved label, but only after re-scanning the live screen (find_content_bbox on the cached area expanded by margin) and confirming it still matches -- returns cache_hit: false and drops the stale entry if the UI moved or that area is now empty. Use a smaller margin (5-10px) for labels packed into a dense menu/toolbar row -- see Known limitations |
highlight(rects) |
Draw debug boxes on the overlay (visual only) |
Setup
cd F:\tools\winauto-mcp
python -m venv .venv
.venv\Scripts\pip install -r requirements.txt
Register with an MCP client
Claude Desktop / Claude Code mcpServers config:
{
"mcpServers": {
"winauto": {
"command": "F:\\tools\\winauto-mcp\\.venv\\Scripts\\python.exe",
"args": ["F:\\tools\\winauto-mcp\\server.py"]
}
}
}
Claude Code CLI:
claude mcp add winauto -- F:\tools\winauto-mcp\.venv\Scripts\python.exe F:\tools\winauto-mcp\server.py
Known limitations (v0.1)
- One attached window at a time.
SendInputis userspace input — most apps and browsers receive it fine, but some DirectInput/XInput-hooked fullscreen games may ignore it.SetForegroundWindow's foreground-lock rejection is mitigated (seeAttachThreadInputnote above) but not guaranteed in every edge case;attach_window/click/type_textdon't hard-fail if it's still refused.- UIA tree walk is capped (depth 15 / 3000 nodes / 150 results) to stay fast on deep trees (e.g. Chrome with heavy pages) — very deeply nested elements past the cap won't show up in the text summary.
click_element/wait_formatch by UIAName(exact, then substring, case-insensitive); if multiple elements share a name the first one found in tree order is used — checkget_elementsif you need to disambiguate.- Some apps with a custom-drawn title bar (e.g. Windows 11's modern Notepad)
report menu items slightly outside
GetClientRect, so their rect's y can be negative — a UIA quirk of that app, not a bug in this server. diff_since_snapshotcan false-positive on apps that redraw part of their own UI continuously with no user input (e.g. a game/tool-script gizmo, blinking cursor, live counter) — confirmed against Godot's editor, whose 2D viewport keeps redrawing a debug hint label for the currently selected node. A nonzerochanged_bboxmeans "something changed," not necessarily "your action caused it" — sanity-check that the bbox's location/size fits the expected effect.- The location cache (
remember_location/recall_location) is stored in.location_cache.jsonnext to the server, keyed by process name + client size + label. It is never trusted blind: everyrecall_locationcall re-scans the live screen and compares against the cached bbox before returning a coordinate, so a stale entry (app updated, window resized, target scrolled out of view) is detected and dropped rather than causing a misclick. recall_location's re-verify margin can itself cause a false "stale" report if set too large for the target's surroundings: expanding into a neighboring label/icon merges them into one bigger bbox, which fails the size-ratio check even though the actual target never moved — confirmed against Blender's tightly packed viewport header (View Select Add Object, items only ~15-20px apart). Use a smallmargin(5-10px) for dense menu/toolbar rows; the default (15) assumes moderate spacing.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。