Repository files navigation

FlaUI-MCP

An MCP (Model Context Protocol) server that enables AI agents to automate Windows desktop applications using accessibility APIs - the same way Playwright automates browsers.

BuildGitHub releaseLicense: MIT

Why This Exists

When Playwright's MCP server automates browsers, it provides:

  • browser_snapshot → Structured accessibility tree with element refs
  • browser_click ref="..." → Click by ref, not coordinates

FlaUI-MCP brings the same pattern to Windows desktop apps:

  • windows_snapshot → Accessibility tree with refs like w1e5
  • windows_click ref="w1e5" → Click element by ref

No screenshot parsing. No coordinate guessing. Just semantic element references.

Quick Demo

Agent: Calculate 3 × 3
1. windows_launch { "app": "calc.exe" }
→ Window handle: w1
2. windows_snapshot { "handle": "w1" }
→ - window "Calculator" [ref=w1]
- button "Three" [ref=w1e43]
- button "Multiply by" [ref=w1e35]
- button "Equals" [ref=w1e38]
- text "Display is 0" [ref=w1e15]
3. windows_batch { "actions": [
{"action": "click", "ref": "w1e43"},
{"action": "click", "ref": "w1e35"},
{"action": "click", "ref": "w1e43"},
{"action": "click", "ref": "w1e38"},
{"action": "snapshot", "handle": "w1"}
]}
→ 1. click: Invoked Three
2. click: Invoked Multiply by
3. click: Invoked Three
4. click: Invoked Equals
5. snapshot: ... "Display is 9" ...

Installation

Prerequisites

  • Windows 10/11
  • .NET 8.0 Runtime

Download Release

Download the latest release from Releases and extract to a folder.

Choose the ZIP that matches your machine:

AssetUse when
FlaUI-MCP-win-x64-*-self-contained.zip64-bit Windows, no .NET runtime required
FlaUI-MCP-win-x64-*.zip64-bit Windows with .NET 8 Runtime already installed
FlaUI-MCP-win-arm64-*-self-contained.zipWindows on ARM64, no .NET runtime required
FlaUI-MCP-win-arm64-*.zipWindows on ARM64 with .NET 8 Runtime already installed

Configure MCP Client

Add to your MCP configuration (e.g., ~/.copilot/mcp-config.json):

{
"mcpServers": {
"windows": {
"type": "local",
"command": "C:\\path\\to\\FlaUI-MCP.exe",
"tools": ["*"]
}
}
}

Or using dotnet run:

{
"mcpServers": {
"windows": {
"type": "local",
"command": "dotnet",
"args": ["run", "--project", "C:\\path\\to\\src\\FlaUI.Mcp"]
}
}
}

Available Tools

ToolDescription
windows_launchLaunch a Windows application
windows_snapshotGet accessibility tree with element refs
windows_clickClick an element by ref
windows_typeType text into an element
windows_send_keysSend key presses or key chords (for example Ctrl+A)
windows_fillClear and fill a text field
windows_get_textGet text content of an element
windows_screenshotCapture window/element as PNG
windows_list_windowsList all open windows
windows_focusBring a window to foreground
windows_closeClose a window
windows_batchExecute multiple actions in one call

windows_screenshot supports an optional background: true argument when a window handle is provided. This uses native background capture when available and falls back to the normal screenshot path if Windows returns a blank frame. It can also save screenshots with savePath, which must be an absolute local .png path. Existing files are not replaced unless overwrite: true is set.

Tool calls have a 30-second timeout so a blocked UI Automation provider or modal dialog returns an actionable error instead of hanging the MCP server forever.

Keeping the Screen Awake

Long automation runs generate no keyboard or mouse input, so Windows would normally turn off the display and show the lock screen mid-run — which breaks screenshots and can freeze rendering. While tools are actively being called, FlaUI-MCP holds a Windows power availability request (the same signal video players and conferencing apps send) that keeps the display on and suppresses the idle lock. The request appears in powercfg /requests (run as admin) with the reason "FlaUI-MCP is driving Windows UI automation".

The request is released after 5 minutes without a tool call, so an idle MCP server does not keep your screen on. Configure via the FLAUI_MCP_KEEP_AWAKE_SECONDS environment variable: a positive value changes the idle period, 0 disables keep-awake entirely.

Note: this covers the common idle-lock paths (display timeout, screensaver, sleep). A domain group policy that enforces a hard machine inactivity limit ("Interactive logon: Machine inactivity limit") locks based on input idle time and is not suppressed by availability requests — no application can override that policy.

Restricting Which Apps Can Be Automated

By default, FlaUI-MCP can automate any application on the desktop — including File Explorer, browsers, and terminals. If the agent driving it is ever misled (for example by prompt injection), that is a large attack surface. Set the FLAUI_MCP_ALLOWED_APPS environment variable to restrict automation to specific apps:

{
"mcpServers": {
"windows": {
"type": "local",
"command": "C:\\path\\to\\FlaUI.Mcp.exe",
"env": {
"FLAUI_MCP_ALLOWED_APPS": "TabularEditor3"
}
}
}
}

The value is a semicolon- or comma-separated list of process names, matched case-insensitively, with or without .exe (full paths are reduced to their file name). When the variable is unset or empty, everything is allowed.

While the allowlist is active:

  • windows_launch refuses to start non-allowed executables.
  • Window handles are only ever issued for allowed processes, so every ref-based tool (snapshot, click, type, fill, get_text, screenshot by handle/ref) is automatically scoped to allowed apps. windows_list_windows lists only allowed apps' windows.
  • Ref-less keyboard input (windows_send_keys / windows_type without a ref) verifies the foreground window belongs to an allowed process first, and the Windows key is rejected outright (it opens system UI like the Start menu and Win+R outside any allowlist).
  • windows_screenshot refuses fullScreen capture and foreground-window capture of non-allowed apps, so other windows' content is not disclosed.

Scope honestly stated: matching is by process name, so this is a guard against a misdirected or prompt-injected agent driving unintended apps through this server — not a sandbox against a local attacker, and it does not restrict anything the agent can do through other tools (like a shell). Dialogs owned by the allowed process (including common file dialogs, which run in-process) keep working; apps it launches as separate processes (e.g. a browser for OAuth) are blocked unless also listed.

Tool Examples

Send a keyboard chord to a target element:

{
"ref": "w1e5",
"chord": "Ctrl+A"
}

Send a sequence of key presses or chords:

{
"keys": ["Ctrl+A", "Delete", "Enter"]
}

Capture a window using opt-in background capture:

{
"handle": "w1",
"background": true
}

Save a screenshot to disk without replacing existing files:

{
"handle": "w1",
"savePath": "C:\\Temp\\capture.png"
}

Replace an existing screenshot file explicitly:

{
"handle": "w1",
"savePath": "C:\\Temp\\capture.png",
"overwrite": true
}

Safety and Limitations

  • Keyboard input is focus-dependent. When you use windows_send_keys, the tool focuses the supplied ref first when possible, but Windows still sends keys to the active keyboard focus.
  • windows_screenshotbackground: true is only valid with a window handle. If native background capture returns a blank frame, FlaUI-MCP falls back to the normal capture path.
  • savePath accepts absolute local .png paths only. UNC paths, device paths, non-PNG extensions, and existing files without overwrite: true are rejected.
  • Desktop integration tests require an interactive Windows session because they launch real WinForms and WPF windows.
  • A timeout error means the MCP request returned, but a blocked Windows UI Automation provider or modal dialog may still need to be dismissed before retrying the operation.

How It Works

The Accessibility Snapshot

When you call windows_snapshot, you get a structured text tree:

- window "Calculator" [ref=w1e1]
- group "Number pad" [ref=w1e39]
- button "Seven" [ref=w1e47]
- button "Eight" [ref=w1e48]
- button "Nine" [ref=w1e49]
- text "Display is 0" [ref=w1e15]

This comes from Windows UI Automation - the same API screen readers use. Each element has:

  • Role (button, text, group, textbox)
  • Name ("Seven", "Display is 0")
  • Ref (w1e47) - a handle for interaction
  • State ([disabled], [readonly], [checked])

Why Not Screenshots?

ApproachProsCons
Accessibility TreeSemantic, precise, fast, works at any resolutionRequires UI Automation support
Screenshot + VisionWorks with any appSlow, expensive, imprecise, resolution-dependent

FlaUI-MCP uses accessibility because it's what screen readers use - it's designed for programmatic UI interaction.

Modal Dialogs

UI Automation pattern calls (Invoke, Toggle, Select) are synchronous cross-process calls. When a click handler opens a modal dialog (ShowDialog() in WinForms/WPF), the handler — and therefore the pattern call and the app's entire UIA provider — stays blocked until the dialog closes.

FlaUI-MCP handles this instead of hanging:

  • windows_click runs the pattern call on a background thread and watches the app's top-level windows via non-blocking Win32 APIs. If a modal appears, the tool returns immediately with the dialog's title.
  • While the call is pending, UIA-based tools targeting that app (windows_snapshot, windows_get_text, ref-based typing/clicking) fail fast with guidance instead of timing out.
  • Tools that don't need UIA keep working throughout: windows_screenshot, windows_send_keys / windows_typewithout a ref (pure keyboard input), windows_list_windows, windows_focus, and windows_close.

Typical flow: click a button → "modal dialog opened" → screenshot to see it → send keys (e.g. Enter or Tab+Enter) to dismiss it → snapshot works again.

Building from Source

# Clone
git clone https://github.com/shanselman/FlaUI-MCP.git
cd FlaUI-MCP
# Build
dotnet build src/FlaUI.Mcp
# Run
dotnet run --project src/FlaUI.Mcp

Testing

# Unit tests
dotnet test tests\FlaUI.Mcp.Tests
# Desktop integration tests; requires an interactive Windows session
dotnet test tests\FlaUI.Mcp.IntegrationTests

Architecture

┌─────────────────────────────────────────────────────────────────┐
│ AI Agent (GitHub Copilot, Claude, etc.) │
│ - Calls MCP tools: windows_snapshot, windows_click, etc. │
└─────────────────────────────────────────────────────────────────┘
│ MCP Protocol (JSON-RPC over stdio)
▼
┌─────────────────────────────────────────────────────────────────┐
│ FlaUI-MCP Server (.NET 8) │
│ - Implements MCP tool handlers │
│ - Builds agent-friendly accessibility snapshots │
│ - Maps element refs ↔ AutomationElements │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ FlaUI Library (github.com/FlaUI/FlaUI) │
│ - UIA3Automation for modern apps (WPF, UWP, Win32) │
│ - Control patterns: Invoke, Value, Toggle, Selection │
│ - Tree walking and element discovery │
└─────────────────────────────────────────────────────────────────┘

Supported Applications

Works with any Windows application that supports UI Automation:

  • ✅ Win32 apps (Notepad, Explorer, etc.)
  • ✅ WPF applications
  • ✅ WinForms applications
  • ✅ UWP/Store apps (Calculator, Settings, etc.)
  • ⚠️ Electron apps (partial - depends on accessibility implementation)
  • ❌ Games (typically no UI Automation support)

Contributing

Contributions welcome! Please see CONTRIBUTING.md for guidelines.

License

MIT License - see LICENSE for details.

Acknowledgments

  • FlaUI - The excellent .NET UI Automation library this project is built on
  • Playwright - Inspiration for the snapshot/ref interaction model
  • Model Context Protocol - The protocol that makes this possible

About

MCP server for Windows desktop automation using FlaUI and UI Automation APIs

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

FlaUI-MCP

An MCP (Model Context Protocol) server that enables AI agents to automate Windows desktop applications using accessibility APIs - the same way Playwright automates browsers.

BuildGitHub releaseLicense: MIT

Why This Exists

When Playwright's MCP server automates browsers, it provides:

  • browser_snapshot → Structured accessibility tree with element refs
  • browser_click ref="..." → Click by ref, not coordinates

FlaUI-MCP brings the same pattern to Windows desktop apps:

  • windows_snapshot → Accessibility tree with refs like w1e5
  • windows_click ref="w1e5" → Click element by ref

No screenshot parsing. No coordinate guessing. Just semantic element references.

Quick Demo

Agent: Calculate 3 × 3
1. windows_launch { "app": "calc.exe" }
→ Window handle: w1
2. windows_snapshot { "handle": "w1" }
→ - window "Calculator" [ref=w1]
- button "Three" [ref=w1e43]
- button "Multiply by" [ref=w1e35]
- button "Equals" [ref=w1e38]
- text "Display is 0" [ref=w1e15]
3. windows_batch { "actions": [
{"action": "click", "ref": "w1e43"},
{"action": "click", "ref": "w1e35"},
{"action": "click", "ref": "w1e43"},
{"action": "click", "ref": "w1e38"},
{"action": "snapshot", "handle": "w1"}
]}
→ 1. click: Invoked Three
2. click: Invoked Multiply by
3. click: Invoked Three
4. click: Invoked Equals
5. snapshot: ... "Display is 9" ...

Installation

Prerequisites

  • Windows 10/11
  • .NET 8.0 Runtime

Download Release

Download the latest release from Releases and extract to a folder.

Choose the ZIP that matches your machine:

AssetUse when
FlaUI-MCP-win-x64-*-self-contained.zip64-bit Windows, no .NET runtime required
FlaUI-MCP-win-x64-*.zip64-bit Windows with .NET 8 Runtime already installed
FlaUI-MCP-win-arm64-*-self-contained.zipWindows on ARM64, no .NET runtime required
FlaUI-MCP-win-arm64-*.zipWindows on ARM64 with .NET 8 Runtime already installed

Configure MCP Client

Add to your MCP configuration (e.g., ~/.copilot/mcp-config.json):

{
"mcpServers": {
"windows": {
"type": "local",
"command": "C:\\path\\to\\FlaUI-MCP.exe",
"tools": ["*"]
}
}
}

Or using dotnet run:

{
"mcpServers": {
"windows": {
"type": "local",
"command": "dotnet",
"args": ["run", "--project", "C:\\path\\to\\src\\FlaUI.Mcp"]
}
}
}

Available Tools

ToolDescription
windows_launchLaunch a Windows application
windows_snapshotGet accessibility tree with element refs
windows_clickClick an element by ref
windows_typeType text into an element
windows_send_keysSend key presses or key chords (for example Ctrl+A)
windows_fillClear and fill a text field
windows_get_textGet text content of an element
windows_screenshotCapture window/element as PNG
windows_list_windowsList all open windows
windows_focusBring a window to foreground
windows_closeClose a window
windows_batchExecute multiple actions in one call

windows_screenshot supports an optional background: true argument when a window handle is provided. This uses native background capture when available and falls back to the normal screenshot path if Windows returns a blank frame. It can also save screenshots with savePath, which must be an absolute local .png path. Existing files are not replaced unless overwrite: true is set.

Tool calls have a 30-second timeout so a blocked UI Automation provider or modal dialog returns an actionable error instead of hanging the MCP server forever.

Keeping the Screen Awake

Long automation runs generate no keyboard or mouse input, so Windows would normally turn off the display and show the lock screen mid-run — which breaks screenshots and can freeze rendering. While tools are actively being called, FlaUI-MCP holds a Windows power availability request (the same signal video players and conferencing apps send) that keeps the display on and suppresses the idle lock. The request appears in powercfg /requests (run as admin) with the reason "FlaUI-MCP is driving Windows UI automation".

The request is released after 5 minutes without a tool call, so an idle MCP server does not keep your screen on. Configure via the FLAUI_MCP_KEEP_AWAKE_SECONDS environment variable: a positive value changes the idle period, 0 disables keep-awake entirely.

Note: this covers the common idle-lock paths (display timeout, screensaver, sleep). A domain group policy that enforces a hard machine inactivity limit ("Interactive logon: Machine inactivity limit") locks based on input idle time and is not suppressed by availability requests — no application can override that policy.

Restricting Which Apps Can Be Automated

By default, FlaUI-MCP can automate any application on the desktop — including File Explorer, browsers, and terminals. If the agent driving it is ever misled (for example by prompt injection), that is a large attack surface. Set the FLAUI_MCP_ALLOWED_APPS environment variable to restrict automation to specific apps:

{
"mcpServers": {
"windows": {
"type": "local",
"command": "C:\\path\\to\\FlaUI.Mcp.exe",
"env": {
"FLAUI_MCP_ALLOWED_APPS": "TabularEditor3"
}
}
}
}

The value is a semicolon- or comma-separated list of process names, matched case-insensitively, with or without .exe (full paths are reduced to their file name). When the variable is unset or empty, everything is allowed.

While the allowlist is active:

  • windows_launch refuses to start non-allowed executables.
  • Window handles are only ever issued for allowed processes, so every ref-based tool (snapshot, click, type, fill, get_text, screenshot by handle/ref) is automatically scoped to allowed apps. windows_list_windows lists only allowed apps' windows.
  • Ref-less keyboard input (windows_send_keys / windows_type without a ref) verifies the foreground window belongs to an allowed process first, and the Windows key is rejected outright (it opens system UI like the Start menu and Win+R outside any allowlist).
  • windows_screenshot refuses fullScreen capture and foreground-window capture of non-allowed apps, so other windows' content is not disclosed.

Scope honestly stated: matching is by process name, so this is a guard against a misdirected or prompt-injected agent driving unintended apps through this server — not a sandbox against a local attacker, and it does not restrict anything the agent can do through other tools (like a shell). Dialogs owned by the allowed process (including common file dialogs, which run in-process) keep working; apps it launches as separate processes (e.g. a browser for OAuth) are blocked unless also listed.

Tool Examples

Send a keyboard chord to a target element:

{
"ref": "w1e5",
"chord": "Ctrl+A"
}

Send a sequence of key presses or chords:

{
"keys": ["Ctrl+A", "Delete", "Enter"]
}

Capture a window using opt-in background capture:

{
"handle": "w1",
"background": true
}

Save a screenshot to disk without replacing existing files:

{
"handle": "w1",
"savePath": "C:\\Temp\\capture.png"
}

Replace an existing screenshot file explicitly:

{
"handle": "w1",
"savePath": "C:\\Temp\\capture.png",
"overwrite": true
}

Safety and Limitations

  • Keyboard input is focus-dependent. When you use windows_send_keys, the tool focuses the supplied ref first when possible, but Windows still sends keys to the active keyboard focus.
  • windows_screenshotbackground: true is only valid with a window handle. If native background capture returns a blank frame, FlaUI-MCP falls back to the normal capture path.
  • savePath accepts absolute local .png paths only. UNC paths, device paths, non-PNG extensions, and existing files without overwrite: true are rejected.
  • Desktop integration tests require an interactive Windows session because they launch real WinForms and WPF windows.
  • A timeout error means the MCP request returned, but a blocked Windows UI Automation provider or modal dialog may still need to be dismissed before retrying the operation.

How It Works

The Accessibility Snapshot

When you call windows_snapshot, you get a structured text tree:

- window "Calculator" [ref=w1e1]
- group "Number pad" [ref=w1e39]
- button "Seven" [ref=w1e47]
- button "Eight" [ref=w1e48]
- button "Nine" [ref=w1e49]
- text "Display is 0" [ref=w1e15]

This comes from Windows UI Automation - the same API screen readers use. Each element has:

  • Role (button, text, group, textbox)
  • Name ("Seven", "Display is 0")
  • Ref (w1e47) - a handle for interaction
  • State ([disabled], [readonly], [checked])

Why Not Screenshots?

ApproachProsCons
Accessibility TreeSemantic, precise, fast, works at any resolutionRequires UI Automation support
Screenshot + VisionWorks with any appSlow, expensive, imprecise, resolution-dependent

FlaUI-MCP uses accessibility because it's what screen readers use - it's designed for programmatic UI interaction.

Modal Dialogs

UI Automation pattern calls (Invoke, Toggle, Select) are synchronous cross-process calls. When a click handler opens a modal dialog (ShowDialog() in WinForms/WPF), the handler — and therefore the pattern call and the app's entire UIA provider — stays blocked until the dialog closes.

FlaUI-MCP handles this instead of hanging:

  • windows_click runs the pattern call on a background thread and watches the app's top-level windows via non-blocking Win32 APIs. If a modal appears, the tool returns immediately with the dialog's title.
  • While the call is pending, UIA-based tools targeting that app (windows_snapshot, windows_get_text, ref-based typing/clicking) fail fast with guidance instead of timing out.
  • Tools that don't need UIA keep working throughout: windows_screenshot, windows_send_keys / windows_typewithout a ref (pure keyboard input), windows_list_windows, windows_focus, and windows_close.

Typical flow: click a button → "modal dialog opened" → screenshot to see it → send keys (e.g. Enter or Tab+Enter) to dismiss it → snapshot works again.

Building from Source

# Clone
git clone https://github.com/shanselman/FlaUI-MCP.git
cd FlaUI-MCP
# Build
dotnet build src/FlaUI.Mcp
# Run
dotnet run --project src/FlaUI.Mcp

Testing

# Unit tests
dotnet test tests\FlaUI.Mcp.Tests
# Desktop integration tests; requires an interactive Windows session
dotnet test tests\FlaUI.Mcp.IntegrationTests

Architecture

┌─────────────────────────────────────────────────────────────────┐
│ AI Agent (GitHub Copilot, Claude, etc.) │
│ - Calls MCP tools: windows_snapshot, windows_click, etc. │
└─────────────────────────────────────────────────────────────────┘
│ MCP Protocol (JSON-RPC over stdio)
▼
┌─────────────────────────────────────────────────────────────────┐
│ FlaUI-MCP Server (.NET 8) │
│ - Implements MCP tool handlers │
│ - Builds agent-friendly accessibility snapshots │
│ - Maps element refs ↔ AutomationElements │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ FlaUI Library (github.com/FlaUI/FlaUI) │
│ - UIA3Automation for modern apps (WPF, UWP, Win32) │
│ - Control patterns: Invoke, Value, Toggle, Selection │
│ - Tree walking and element discovery │
└─────────────────────────────────────────────────────────────────┘

Supported Applications

Works with any Windows application that supports UI Automation:

  • ✅ Win32 apps (Notepad, Explorer, etc.)
  • ✅ WPF applications
  • ✅ WinForms applications
  • ✅ UWP/Store apps (Calculator, Settings, etc.)
  • ⚠️ Electron apps (partial - depends on accessibility implementation)
  • ❌ Games (typically no UI Automation support)

Contributing

Contributions welcome! Please see CONTRIBUTING.md for guidelines.

License

MIT License - see LICENSE for details.

Acknowledgments

  • FlaUI - The excellent .NET UI Automation library this project is built on
  • Playwright - Inspiration for the snapshot/ref interaction model
  • Model Context Protocol - The protocol that makes this possible

About

MCP server for Windows desktop automation using FlaUI and UI Automation APIs

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

FlaUI-MCP

An MCP (Model Context Protocol) server that enables AI agents to automate Windows desktop applications using accessibility APIs - the same way Playwright automates browsers.

BuildGitHub releaseLicense: MIT

Why This Exists

When Playwright's MCP server automates browsers, it provides:

  • browser_snapshot → Structured accessibility tree with element refs
  • browser_click ref="..." → Click by ref, not coordinates

FlaUI-MCP brings the same pattern to Windows desktop apps:

  • windows_snapshot → Accessibility tree with refs like w1e5
  • windows_click ref="w1e5" → Click element by ref

No screenshot parsing. No coordinate guessing. Just semantic element references.

Quick Demo

Agent: Calculate 3 × 3
1. windows_launch { "app": "calc.exe" }
→ Window handle: w1
2. windows_snapshot { "handle": "w1" }
→ - window "Calculator" [ref=w1]
- button "Three" [ref=w1e43]
- button "Multiply by" [ref=w1e35]
- button "Equals" [ref=w1e38]
- text "Display is 0" [ref=w1e15]
3. windows_batch { "actions": [
{"action": "click", "ref": "w1e43"},
{"action": "click", "ref": "w1e35"},
{"action": "click", "ref": "w1e43"},
{"action": "click", "ref": "w1e38"},
{"action": "snapshot", "handle": "w1"}
]}
→ 1. click: Invoked Three
2. click: Invoked Multiply by
3. click: Invoked Three
4. click: Invoked Equals
5. snapshot: ... "Display is 9" ...

Installation

Prerequisites

  • Windows 10/11
  • .NET 8.0 Runtime

Download Release

Download the latest release from Releases and extract to a folder.

Choose the ZIP that matches your machine:

AssetUse when
FlaUI-MCP-win-x64-*-self-contained.zip64-bit Windows, no .NET runtime required
FlaUI-MCP-win-x64-*.zip64-bit Windows with .NET 8 Runtime already installed
FlaUI-MCP-win-arm64-*-self-contained.zipWindows on ARM64, no .NET runtime required
FlaUI-MCP-win-arm64-*.zipWindows on ARM64 with .NET 8 Runtime already installed

Configure MCP Client

Add to your MCP configuration (e.g., ~/.copilot/mcp-config.json):

{
"mcpServers": {
"windows": {
"type": "local",
"command": "C:\\path\\to\\FlaUI-MCP.exe",
"tools": ["*"]
}
}
}

Or using dotnet run:

{
"mcpServers": {
"windows": {
"type": "local",
"command": "dotnet",
"args": ["run", "--project", "C:\\path\\to\\src\\FlaUI.Mcp"]
}
}
}

Available Tools

ToolDescription
windows_launchLaunch a Windows application
windows_snapshotGet accessibility tree with element refs
windows_clickClick an element by ref
windows_typeType text into an element
windows_send_keysSend key presses or key chords (for example Ctrl+A)
windows_fillClear and fill a text field
windows_get_textGet text content of an element
windows_screenshotCapture window/element as PNG
windows_list_windowsList all open windows
windows_focusBring a window to foreground
windows_closeClose a window
windows_batchExecute multiple actions in one call

windows_screenshot supports an optional background: true argument when a window handle is provided. This uses native background capture when available and falls back to the normal screenshot path if Windows returns a blank frame. It can also save screenshots with savePath, which must be an absolute local .png path. Existing files are not replaced unless overwrite: true is set.

Tool calls have a 30-second timeout so a blocked UI Automation provider or modal dialog returns an actionable error instead of hanging the MCP server forever.

Keeping the Screen Awake

Long automation runs generate no keyboard or mouse input, so Windows would normally turn off the display and show the lock screen mid-run — which breaks screenshots and can freeze rendering. While tools are actively being called, FlaUI-MCP holds a Windows power availability request (the same signal video players and conferencing apps send) that keeps the display on and suppresses the idle lock. The request appears in powercfg /requests (run as admin) with the reason "FlaUI-MCP is driving Windows UI automation".

The request is released after 5 minutes without a tool call, so an idle MCP server does not keep your screen on. Configure via the FLAUI_MCP_KEEP_AWAKE_SECONDS environment variable: a positive value changes the idle period, 0 disables keep-awake entirely.

Note: this covers the common idle-lock paths (display timeout, screensaver, sleep). A domain group policy that enforces a hard machine inactivity limit ("Interactive logon: Machine inactivity limit") locks based on input idle time and is not suppressed by availability requests — no application can override that policy.

Restricting Which Apps Can Be Automated

By default, FlaUI-MCP can automate any application on the desktop — including File Explorer, browsers, and terminals. If the agent driving it is ever misled (for example by prompt injection), that is a large attack surface. Set the FLAUI_MCP_ALLOWED_APPS environment variable to restrict automation to specific apps:

{
"mcpServers": {
"windows": {
"type": "local",
"command": "C:\\path\\to\\FlaUI.Mcp.exe",
"env": {
"FLAUI_MCP_ALLOWED_APPS": "TabularEditor3"
}
}
}
}

The value is a semicolon- or comma-separated list of process names, matched case-insensitively, with or without .exe (full paths are reduced to their file name). When the variable is unset or empty, everything is allowed.

While the allowlist is active:

  • windows_launch refuses to start non-allowed executables.
  • Window handles are only ever issued for allowed processes, so every ref-based tool (snapshot, click, type, fill, get_text, screenshot by handle/ref) is automatically scoped to allowed apps. windows_list_windows lists only allowed apps' windows.
  • Ref-less keyboard input (windows_send_keys / windows_type without a ref) verifies the foreground window belongs to an allowed process first, and the Windows key is rejected outright (it opens system UI like the Start menu and Win+R outside any allowlist).
  • windows_screenshot refuses fullScreen capture and foreground-window capture of non-allowed apps, so other windows' content is not disclosed.

Scope honestly stated: matching is by process name, so this is a guard against a misdirected or prompt-injected agent driving unintended apps through this server — not a sandbox against a local attacker, and it does not restrict anything the agent can do through other tools (like a shell). Dialogs owned by the allowed process (including common file dialogs, which run in-process) keep working; apps it launches as separate processes (e.g. a browser for OAuth) are blocked unless also listed.

Tool Examples

Send a keyboard chord to a target element:

{
"ref": "w1e5",
"chord": "Ctrl+A"
}

Send a sequence of key presses or chords:

{
"keys": ["Ctrl+A", "Delete", "Enter"]
}

Capture a window using opt-in background capture:

{
"handle": "w1",
"background": true
}

Save a screenshot to disk without replacing existing files:

{
"handle": "w1",
"savePath": "C:\\Temp\\capture.png"
}

Replace an existing screenshot file explicitly:

{
"handle": "w1",
"savePath": "C:\\Temp\\capture.png",
"overwrite": true
}

Safety and Limitations

  • Keyboard input is focus-dependent. When you use windows_send_keys, the tool focuses the supplied ref first when possible, but Windows still sends keys to the active keyboard focus.
  • windows_screenshotbackground: true is only valid with a window handle. If native background capture returns a blank frame, FlaUI-MCP falls back to the normal capture path.
  • savePath accepts absolute local .png paths only. UNC paths, device paths, non-PNG extensions, and existing files without overwrite: true are rejected.
  • Desktop integration tests require an interactive Windows session because they launch real WinForms and WPF windows.
  • A timeout error means the MCP request returned, but a blocked Windows UI Automation provider or modal dialog may still need to be dismissed before retrying the operation.

How It Works

The Accessibility Snapshot

When you call windows_snapshot, you get a structured text tree:

- window "Calculator" [ref=w1e1]
- group "Number pad" [ref=w1e39]
- button "Seven" [ref=w1e47]
- button "Eight" [ref=w1e48]
- button "Nine" [ref=w1e49]
- text "Display is 0" [ref=w1e15]

This comes from Windows UI Automation - the same API screen readers use. Each element has:

  • Role (button, text, group, textbox)
  • Name ("Seven", "Display is 0")
  • Ref (w1e47) - a handle for interaction
  • State ([disabled], [readonly], [checked])

Why Not Screenshots?

ApproachProsCons
Accessibility TreeSemantic, precise, fast, works at any resolutionRequires UI Automation support
Screenshot + VisionWorks with any appSlow, expensive, imprecise, resolution-dependent

FlaUI-MCP uses accessibility because it's what screen readers use - it's designed for programmatic UI interaction.

Modal Dialogs

UI Automation pattern calls (Invoke, Toggle, Select) are synchronous cross-process calls. When a click handler opens a modal dialog (ShowDialog() in WinForms/WPF), the handler — and therefore the pattern call and the app's entire UIA provider — stays blocked until the dialog closes.

FlaUI-MCP handles this instead of hanging:

  • windows_click runs the pattern call on a background thread and watches the app's top-level windows via non-blocking Win32 APIs. If a modal appears, the tool returns immediately with the dialog's title.
  • While the call is pending, UIA-based tools targeting that app (windows_snapshot, windows_get_text, ref-based typing/clicking) fail fast with guidance instead of timing out.
  • Tools that don't need UIA keep working throughout: windows_screenshot, windows_send_keys / windows_typewithout a ref (pure keyboard input), windows_list_windows, windows_focus, and windows_close.

Typical flow: click a button → "modal dialog opened" → screenshot to see it → send keys (e.g. Enter or Tab+Enter) to dismiss it → snapshot works again.

Building from Source

# Clone
git clone https://github.com/shanselman/FlaUI-MCP.git
cd FlaUI-MCP
# Build
dotnet build src/FlaUI.Mcp
# Run
dotnet run --project src/FlaUI.Mcp

Testing

# Unit tests
dotnet test tests\FlaUI.Mcp.Tests
# Desktop integration tests; requires an interactive Windows session
dotnet test tests\FlaUI.Mcp.IntegrationTests

Architecture

┌─────────────────────────────────────────────────────────────────┐
│ AI Agent (GitHub Copilot, Claude, etc.) │
│ - Calls MCP tools: windows_snapshot, windows_click, etc. │
└─────────────────────────────────────────────────────────────────┘
│ MCP Protocol (JSON-RPC over stdio)
▼
┌─────────────────────────────────────────────────────────────────┐
│ FlaUI-MCP Server (.NET 8) │
│ - Implements MCP tool handlers │
│ - Builds agent-friendly accessibility snapshots │
│ - Maps element refs ↔ AutomationElements │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ FlaUI Library (github.com/FlaUI/FlaUI) │
│ - UIA3Automation for modern apps (WPF, UWP, Win32) │
│ - Control patterns: Invoke, Value, Toggle, Selection │
│ - Tree walking and element discovery │
└─────────────────────────────────────────────────────────────────┘

Supported Applications

Works with any Windows application that supports UI Automation:

  • ✅ Win32 apps (Notepad, Explorer, etc.)
  • ✅ WPF applications
  • ✅ WinForms applications
  • ✅ UWP/Store apps (Calculator, Settings, etc.)
  • ⚠️ Electron apps (partial - depends on accessibility implementation)
  • ❌ Games (typically no UI Automation support)

Contributing

Contributions welcome! Please see CONTRIBUTING.md for guidelines.

License

MIT License - see LICENSE for details.

Acknowledgments

  • FlaUI - The excellent .NET UI Automation library this project is built on
  • Playwright - Inspiration for the snapshot/ref interaction model
  • Model Context Protocol - The protocol that makes this possible

About

MCP server for Windows desktop automation using FlaUI and UI Automation APIs

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

FlaUI-MCP

An MCP (Model Context Protocol) server that enables AI agents to automate Windows desktop applications using accessibility APIs - the same way Playwright automates browsers.

BuildGitHub releaseLicense: MIT

Why This Exists

When Playwright's MCP server automates browsers, it provides:

  • browser_snapshot → Structured accessibility tree with element refs
  • browser_click ref="..." → Click by ref, not coordinates

FlaUI-MCP brings the same pattern to Windows desktop apps:

  • windows_snapshot → Accessibility tree with refs like w1e5
  • windows_click ref="w1e5" → Click element by ref

No screenshot parsing. No coordinate guessing. Just semantic element references.

Quick Demo

Agent: Calculate 3 × 3
1. windows_launch { "app": "calc.exe" }
→ Window handle: w1
2. windows_snapshot { "handle": "w1" }
→ - window "Calculator" [ref=w1]
- button "Three" [ref=w1e43]
- button "Multiply by" [ref=w1e35]
- button "Equals" [ref=w1e38]
- text "Display is 0" [ref=w1e15]
3. windows_batch { "actions": [
{"action": "click", "ref": "w1e43"},
{"action": "click", "ref": "w1e35"},
{"action": "click", "ref": "w1e43"},
{"action": "click", "ref": "w1e38"},
{"action": "snapshot", "handle": "w1"}
]}
→ 1. click: Invoked Three
2. click: Invoked Multiply by
3. click: Invoked Three
4. click: Invoked Equals
5. snapshot: ... "Display is 9" ...

Installation

Prerequisites

  • Windows 10/11
  • .NET 8.0 Runtime

Download Release

Download the latest release from Releases and extract to a folder.

Choose the ZIP that matches your machine:

AssetUse when
FlaUI-MCP-win-x64-*-self-contained.zip64-bit Windows, no .NET runtime required
FlaUI-MCP-win-x64-*.zip64-bit Windows with .NET 8 Runtime already installed
FlaUI-MCP-win-arm64-*-self-contained.zipWindows on ARM64, no .NET runtime required
FlaUI-MCP-win-arm64-*.zipWindows on ARM64 with .NET 8 Runtime already installed

Configure MCP Client

Add to your MCP configuration (e.g., ~/.copilot/mcp-config.json):

{
"mcpServers": {
"windows": {
"type": "local",
"command": "C:\\path\\to\\FlaUI-MCP.exe",
"tools": ["*"]
}
}
}

Or using dotnet run:

{
"mcpServers": {
"windows": {
"type": "local",
"command": "dotnet",
"args": ["run", "--project", "C:\\path\\to\\src\\FlaUI.Mcp"]
}
}
}

Available Tools

ToolDescription
windows_launchLaunch a Windows application
windows_snapshotGet accessibility tree with element refs
windows_clickClick an element by ref
windows_typeType text into an element
windows_send_keysSend key presses or key chords (for example Ctrl+A)
windows_fillClear and fill a text field
windows_get_textGet text content of an element
windows_screenshotCapture window/element as PNG
windows_list_windowsList all open windows
windows_focusBring a window to foreground
windows_closeClose a window
windows_batchExecute multiple actions in one call

windows_screenshot supports an optional background: true argument when a window handle is provided. This uses native background capture when available and falls back to the normal screenshot path if Windows returns a blank frame. It can also save screenshots with savePath, which must be an absolute local .png path. Existing files are not replaced unless overwrite: true is set.

Tool calls have a 30-second timeout so a blocked UI Automation provider or modal dialog returns an actionable error instead of hanging the MCP server forever.

Keeping the Screen Awake

Long automation runs generate no keyboard or mouse input, so Windows would normally turn off the display and show the lock screen mid-run — which breaks screenshots and can freeze rendering. While tools are actively being called, FlaUI-MCP holds a Windows power availability request (the same signal video players and conferencing apps send) that keeps the display on and suppresses the idle lock. The request appears in powercfg /requests (run as admin) with the reason "FlaUI-MCP is driving Windows UI automation".

The request is released after 5 minutes without a tool call, so an idle MCP server does not keep your screen on. Configure via the FLAUI_MCP_KEEP_AWAKE_SECONDS environment variable: a positive value changes the idle period, 0 disables keep-awake entirely.

Note: this covers the common idle-lock paths (display timeout, screensaver, sleep). A domain group policy that enforces a hard machine inactivity limit ("Interactive logon: Machine inactivity limit") locks based on input idle time and is not suppressed by availability requests — no application can override that policy.

Restricting Which Apps Can Be Automated

By default, FlaUI-MCP can automate any application on the desktop — including File Explorer, browsers, and terminals. If the agent driving it is ever misled (for example by prompt injection), that is a large attack surface. Set the FLAUI_MCP_ALLOWED_APPS environment variable to restrict automation to specific apps:

{
"mcpServers": {
"windows": {
"type": "local",
"command": "C:\\path\\to\\FlaUI.Mcp.exe",
"env": {
"FLAUI_MCP_ALLOWED_APPS": "TabularEditor3"
}
}
}
}

The value is a semicolon- or comma-separated list of process names, matched case-insensitively, with or without .exe (full paths are reduced to their file name). When the variable is unset or empty, everything is allowed.

While the allowlist is active:

  • windows_launch refuses to start non-allowed executables.
  • Window handles are only ever issued for allowed processes, so every ref-based tool (snapshot, click, type, fill, get_text, screenshot by handle/ref) is automatically scoped to allowed apps. windows_list_windows lists only allowed apps' windows.
  • Ref-less keyboard input (windows_send_keys / windows_type without a ref) verifies the foreground window belongs to an allowed process first, and the Windows key is rejected outright (it opens system UI like the Start menu and Win+R outside any allowlist).
  • windows_screenshot refuses fullScreen capture and foreground-window capture of non-allowed apps, so other windows' content is not disclosed.

Scope honestly stated: matching is by process name, so this is a guard against a misdirected or prompt-injected agent driving unintended apps through this server — not a sandbox against a local attacker, and it does not restrict anything the agent can do through other tools (like a shell). Dialogs owned by the allowed process (including common file dialogs, which run in-process) keep working; apps it launches as separate processes (e.g. a browser for OAuth) are blocked unless also listed.

Tool Examples

Send a keyboard chord to a target element:

{
"ref": "w1e5",
"chord": "Ctrl+A"
}

Send a sequence of key presses or chords:

{
"keys": ["Ctrl+A", "Delete", "Enter"]
}

Capture a window using opt-in background capture:

{
"handle": "w1",
"background": true
}

Save a screenshot to disk without replacing existing files:

{
"handle": "w1",
"savePath": "C:\\Temp\\capture.png"
}

Replace an existing screenshot file explicitly:

{
"handle": "w1",
"savePath": "C:\\Temp\\capture.png",
"overwrite": true
}

Safety and Limitations

  • Keyboard input is focus-dependent. When you use windows_send_keys, the tool focuses the supplied ref first when possible, but Windows still sends keys to the active keyboard focus.
  • windows_screenshotbackground: true is only valid with a window handle. If native background capture returns a blank frame, FlaUI-MCP falls back to the normal capture path.
  • savePath accepts absolute local .png paths only. UNC paths, device paths, non-PNG extensions, and existing files without overwrite: true are rejected.
  • Desktop integration tests require an interactive Windows session because they launch real WinForms and WPF windows.
  • A timeout error means the MCP request returned, but a blocked Windows UI Automation provider or modal dialog may still need to be dismissed before retrying the operation.

How It Works

The Accessibility Snapshot

When you call windows_snapshot, you get a structured text tree:

- window "Calculator" [ref=w1e1]
- group "Number pad" [ref=w1e39]
- button "Seven" [ref=w1e47]
- button "Eight" [ref=w1e48]
- button "Nine" [ref=w1e49]
- text "Display is 0" [ref=w1e15]

This comes from Windows UI Automation - the same API screen readers use. Each element has:

  • Role (button, text, group, textbox)
  • Name ("Seven", "Display is 0")
  • Ref (w1e47) - a handle for interaction
  • State ([disabled], [readonly], [checked])

Why Not Screenshots?

ApproachProsCons
Accessibility TreeSemantic, precise, fast, works at any resolutionRequires UI Automation support
Screenshot + VisionWorks with any appSlow, expensive, imprecise, resolution-dependent

FlaUI-MCP uses accessibility because it's what screen readers use - it's designed for programmatic UI interaction.

Modal Dialogs

UI Automation pattern calls (Invoke, Toggle, Select) are synchronous cross-process calls. When a click handler opens a modal dialog (ShowDialog() in WinForms/WPF), the handler — and therefore the pattern call and the app's entire UIA provider — stays blocked until the dialog closes.

FlaUI-MCP handles this instead of hanging:

  • windows_click runs the pattern call on a background thread and watches the app's top-level windows via non-blocking Win32 APIs. If a modal appears, the tool returns immediately with the dialog's title.
  • While the call is pending, UIA-based tools targeting that app (windows_snapshot, windows_get_text, ref-based typing/clicking) fail fast with guidance instead of timing out.
  • Tools that don't need UIA keep working throughout: windows_screenshot, windows_send_keys / windows_typewithout a ref (pure keyboard input), windows_list_windows, windows_focus, and windows_close.

Typical flow: click a button → "modal dialog opened" → screenshot to see it → send keys (e.g. Enter or Tab+Enter) to dismiss it → snapshot works again.

Building from Source

# Clone
git clone https://github.com/shanselman/FlaUI-MCP.git
cd FlaUI-MCP
# Build
dotnet build src/FlaUI.Mcp
# Run
dotnet run --project src/FlaUI.Mcp

Testing

# Unit tests
dotnet test tests\FlaUI.Mcp.Tests
# Desktop integration tests; requires an interactive Windows session
dotnet test tests\FlaUI.Mcp.IntegrationTests

Architecture

┌─────────────────────────────────────────────────────────────────┐
│ AI Agent (GitHub Copilot, Claude, etc.) │
│ - Calls MCP tools: windows_snapshot, windows_click, etc. │
└─────────────────────────────────────────────────────────────────┘
│ MCP Protocol (JSON-RPC over stdio)
▼
┌─────────────────────────────────────────────────────────────────┐
│ FlaUI-MCP Server (.NET 8) │
│ - Implements MCP tool handlers │
│ - Builds agent-friendly accessibility snapshots │
│ - Maps element refs ↔ AutomationElements │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ FlaUI Library (github.com/FlaUI/FlaUI) │
│ - UIA3Automation for modern apps (WPF, UWP, Win32) │
│ - Control patterns: Invoke, Value, Toggle, Selection │
│ - Tree walking and element discovery │
└─────────────────────────────────────────────────────────────────┘

Supported Applications

Works with any Windows application that supports UI Automation:

  • ✅ Win32 apps (Notepad, Explorer, etc.)
  • ✅ WPF applications
  • ✅ WinForms applications
  • ✅ UWP/Store apps (Calculator, Settings, etc.)
  • ⚠️ Electron apps (partial - depends on accessibility implementation)
  • ❌ Games (typically no UI Automation support)

Contributing

Contributions welcome! Please see CONTRIBUTING.md for guidelines.

License

MIT License - see LICENSE for details.

Acknowledgments

  • FlaUI - The excellent .NET UI Automation library this project is built on
  • Playwright - Inspiration for the snapshot/ref interaction model
  • Model Context Protocol - The protocol that makes this possible

About

MCP server for Windows desktop automation using FlaUI and UI Automation APIs

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

FlaUI-MCP

An MCP (Model Context Protocol) server that enables AI agents to automate Windows desktop applications using accessibility APIs - the same way Playwright automates browsers.

BuildGitHub releaseLicense: MIT

Why This Exists

When Playwright's MCP server automates browsers, it provides:

  • browser_snapshot → Structured accessibility tree with element refs
  • browser_click ref="..." → Click by ref, not coordinates

FlaUI-MCP brings the same pattern to Windows desktop apps:

  • windows_snapshot → Accessibility tree with refs like w1e5
  • windows_click ref="w1e5" → Click element by ref

No screenshot parsing. No coordinate guessing. Just semantic element references.

Quick Demo

Agent: Calculate 3 × 3
1. windows_launch { "app": "calc.exe" }
→ Window handle: w1
2. windows_snapshot { "handle": "w1" }
→ - window "Calculator" [ref=w1]
- button "Three" [ref=w1e43]
- button "Multiply by" [ref=w1e35]
- button "Equals" [ref=w1e38]
- text "Display is 0" [ref=w1e15]
3. windows_batch { "actions": [
{"action": "click", "ref": "w1e43"},
{"action": "click", "ref": "w1e35"},
{"action": "click", "ref": "w1e43"},
{"action": "click", "ref": "w1e38"},
{"action": "snapshot", "handle": "w1"}
]}
→ 1. click: Invoked Three
2. click: Invoked Multiply by
3. click: Invoked Three
4. click: Invoked Equals
5. snapshot: ... "Display is 9" ...

Installation

Prerequisites

  • Windows 10/11
  • .NET 8.0 Runtime

Download Release

Download the latest release from Releases and extract to a folder.

Choose the ZIP that matches your machine:

AssetUse when
FlaUI-MCP-win-x64-*-self-contained.zip64-bit Windows, no .NET runtime required
FlaUI-MCP-win-x64-*.zip64-bit Windows with .NET 8 Runtime already installed
FlaUI-MCP-win-arm64-*-self-contained.zipWindows on ARM64, no .NET runtime required
FlaUI-MCP-win-arm64-*.zipWindows on ARM64 with .NET 8 Runtime already installed

Configure MCP Client

Add to your MCP configuration (e.g., ~/.copilot/mcp-config.json):

{
"mcpServers": {
"windows": {
"type": "local",
"command": "C:\\path\\to\\FlaUI-MCP.exe",
"tools": ["*"]
}
}
}

Or using dotnet run:

{
"mcpServers": {
"windows": {
"type": "local",
"command": "dotnet",
"args": ["run", "--project", "C:\\path\\to\\src\\FlaUI.Mcp"]
}
}
}

Available Tools

ToolDescription
windows_launchLaunch a Windows application
windows_snapshotGet accessibility tree with element refs
windows_clickClick an element by ref
windows_typeType text into an element
windows_send_keysSend key presses or key chords (for example Ctrl+A)
windows_fillClear and fill a text field
windows_get_textGet text content of an element
windows_screenshotCapture window/element as PNG
windows_list_windowsList all open windows
windows_focusBring a window to foreground
windows_closeClose a window
windows_batchExecute multiple actions in one call

windows_screenshot supports an optional background: true argument when a window handle is provided. This uses native background capture when available and falls back to the normal screenshot path if Windows returns a blank frame. It can also save screenshots with savePath, which must be an absolute local .png path. Existing files are not replaced unless overwrite: true is set.

Tool calls have a 30-second timeout so a blocked UI Automation provider or modal dialog returns an actionable error instead of hanging the MCP server forever.

Keeping the Screen Awake

Long automation runs generate no keyboard or mouse input, so Windows would normally turn off the display and show the lock screen mid-run — which breaks screenshots and can freeze rendering. While tools are actively being called, FlaUI-MCP holds a Windows power availability request (the same signal video players and conferencing apps send) that keeps the display on and suppresses the idle lock. The request appears in powercfg /requests (run as admin) with the reason "FlaUI-MCP is driving Windows UI automation".

The request is released after 5 minutes without a tool call, so an idle MCP server does not keep your screen on. Configure via the FLAUI_MCP_KEEP_AWAKE_SECONDS environment variable: a positive value changes the idle period, 0 disables keep-awake entirely.

Note: this covers the common idle-lock paths (display timeout, screensaver, sleep). A domain group policy that enforces a hard machine inactivity limit ("Interactive logon: Machine inactivity limit") locks based on input idle time and is not suppressed by availability requests — no application can override that policy.

Restricting Which Apps Can Be Automated

By default, FlaUI-MCP can automate any application on the desktop — including File Explorer, browsers, and terminals. If the agent driving it is ever misled (for example by prompt injection), that is a large attack surface. Set the FLAUI_MCP_ALLOWED_APPS environment variable to restrict automation to specific apps:

{
"mcpServers": {
"windows": {
"type": "local",
"command": "C:\\path\\to\\FlaUI.Mcp.exe",
"env": {
"FLAUI_MCP_ALLOWED_APPS": "TabularEditor3"
}
}
}
}

The value is a semicolon- or comma-separated list of process names, matched case-insensitively, with or without .exe (full paths are reduced to their file name). When the variable is unset or empty, everything is allowed.

While the allowlist is active:

  • windows_launch refuses to start non-allowed executables.
  • Window handles are only ever issued for allowed processes, so every ref-based tool (snapshot, click, type, fill, get_text, screenshot by handle/ref) is automatically scoped to allowed apps. windows_list_windows lists only allowed apps' windows.
  • Ref-less keyboard input (windows_send_keys / windows_type without a ref) verifies the foreground window belongs to an allowed process first, and the Windows key is rejected outright (it opens system UI like the Start menu and Win+R outside any allowlist).
  • windows_screenshot refuses fullScreen capture and foreground-window capture of non-allowed apps, so other windows' content is not disclosed.

Scope honestly stated: matching is by process name, so this is a guard against a misdirected or prompt-injected agent driving unintended apps through this server — not a sandbox against a local attacker, and it does not restrict anything the agent can do through other tools (like a shell). Dialogs owned by the allowed process (including common file dialogs, which run in-process) keep working; apps it launches as separate processes (e.g. a browser for OAuth) are blocked unless also listed.

Tool Examples

Send a keyboard chord to a target element:

{
"ref": "w1e5",
"chord": "Ctrl+A"
}

Send a sequence of key presses or chords:

{
"keys": ["Ctrl+A", "Delete", "Enter"]
}

Capture a window using opt-in background capture:

{
"handle": "w1",
"background": true
}

Save a screenshot to disk without replacing existing files:

{
"handle": "w1",
"savePath": "C:\\Temp\\capture.png"
}

Replace an existing screenshot file explicitly:

{
"handle": "w1",
"savePath": "C:\\Temp\\capture.png",
"overwrite": true
}

Safety and Limitations

  • Keyboard input is focus-dependent. When you use windows_send_keys, the tool focuses the supplied ref first when possible, but Windows still sends keys to the active keyboard focus.
  • windows_screenshotbackground: true is only valid with a window handle. If native background capture returns a blank frame, FlaUI-MCP falls back to the normal capture path.
  • savePath accepts absolute local .png paths only. UNC paths, device paths, non-PNG extensions, and existing files without overwrite: true are rejected.
  • Desktop integration tests require an interactive Windows session because they launch real WinForms and WPF windows.
  • A timeout error means the MCP request returned, but a blocked Windows UI Automation provider or modal dialog may still need to be dismissed before retrying the operation.

How It Works

The Accessibility Snapshot

When you call windows_snapshot, you get a structured text tree:

- window "Calculator" [ref=w1e1]
- group "Number pad" [ref=w1e39]
- button "Seven" [ref=w1e47]
- button "Eight" [ref=w1e48]
- button "Nine" [ref=w1e49]
- text "Display is 0" [ref=w1e15]

This comes from Windows UI Automation - the same API screen readers use. Each element has:

  • Role (button, text, group, textbox)
  • Name ("Seven", "Display is 0")
  • Ref (w1e47) - a handle for interaction
  • State ([disabled], [readonly], [checked])

Why Not Screenshots?

ApproachProsCons
Accessibility TreeSemantic, precise, fast, works at any resolutionRequires UI Automation support
Screenshot + VisionWorks with any appSlow, expensive, imprecise, resolution-dependent

FlaUI-MCP uses accessibility because it's what screen readers use - it's designed for programmatic UI interaction.

Modal Dialogs

UI Automation pattern calls (Invoke, Toggle, Select) are synchronous cross-process calls. When a click handler opens a modal dialog (ShowDialog() in WinForms/WPF), the handler — and therefore the pattern call and the app's entire UIA provider — stays blocked until the dialog closes.

FlaUI-MCP handles this instead of hanging:

  • windows_click runs the pattern call on a background thread and watches the app's top-level windows via non-blocking Win32 APIs. If a modal appears, the tool returns immediately with the dialog's title.
  • While the call is pending, UIA-based tools targeting that app (windows_snapshot, windows_get_text, ref-based typing/clicking) fail fast with guidance instead of timing out.
  • Tools that don't need UIA keep working throughout: windows_screenshot, windows_send_keys / windows_typewithout a ref (pure keyboard input), windows_list_windows, windows_focus, and windows_close.

Typical flow: click a button → "modal dialog opened" → screenshot to see it → send keys (e.g. Enter or Tab+Enter) to dismiss it → snapshot works again.

Building from Source

# Clone
git clone https://github.com/shanselman/FlaUI-MCP.git
cd FlaUI-MCP
# Build
dotnet build src/FlaUI.Mcp
# Run
dotnet run --project src/FlaUI.Mcp

Testing

# Unit tests
dotnet test tests\FlaUI.Mcp.Tests
# Desktop integration tests; requires an interactive Windows session
dotnet test tests\FlaUI.Mcp.IntegrationTests

Architecture

┌─────────────────────────────────────────────────────────────────┐
│ AI Agent (GitHub Copilot, Claude, etc.) │
│ - Calls MCP tools: windows_snapshot, windows_click, etc. │
└─────────────────────────────────────────────────────────────────┘
│ MCP Protocol (JSON-RPC over stdio)
▼
┌─────────────────────────────────────────────────────────────────┐
│ FlaUI-MCP Server (.NET 8) │
│ - Implements MCP tool handlers │
│ - Builds agent-friendly accessibility snapshots │
│ - Maps element refs ↔ AutomationElements │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ FlaUI Library (github.com/FlaUI/FlaUI) │
│ - UIA3Automation for modern apps (WPF, UWP, Win32) │
│ - Control patterns: Invoke, Value, Toggle, Selection │
│ - Tree walking and element discovery │
└─────────────────────────────────────────────────────────────────┘

Supported Applications

Works with any Windows application that supports UI Automation:

  • ✅ Win32 apps (Notepad, Explorer, etc.)
  • ✅ WPF applications
  • ✅ WinForms applications
  • ✅ UWP/Store apps (Calculator, Settings, etc.)
  • ⚠️ Electron apps (partial - depends on accessibility implementation)
  • ❌ Games (typically no UI Automation support)

Contributing

Contributions welcome! Please see CONTRIBUTING.md for guidelines.

License

MIT License - see LICENSE for details.

Acknowledgments

  • FlaUI - The excellent .NET UI Automation library this project is built on
  • Playwright - Inspiration for the snapshot/ref interaction model
  • Model Context Protocol - The protocol that makes this possible

About

MCP server for Windows desktop automation using FlaUI and UI Automation APIs

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

FlaUI-MCP

An MCP (Model Context Protocol) server that enables AI agents to automate Windows desktop applications using accessibility APIs - the same way Playwright automates browsers.

BuildGitHub releaseLicense: MIT

Why This Exists

When Playwright's MCP server automates browsers, it provides:

  • browser_snapshot → Structured accessibility tree with element refs
  • browser_click ref="..." → Click by ref, not coordinates

FlaUI-MCP brings the same pattern to Windows desktop apps:

  • windows_snapshot → Accessibility tree with refs like w1e5
  • windows_click ref="w1e5" → Click element by ref

No screenshot parsing. No coordinate guessing. Just semantic element references.

Quick Demo

Agent: Calculate 3 × 3
1. windows_launch { "app": "calc.exe" }
→ Window handle: w1
2. windows_snapshot { "handle": "w1" }
→ - window "Calculator" [ref=w1]
- button "Three" [ref=w1e43]
- button "Multiply by" [ref=w1e35]
- button "Equals" [ref=w1e38]
- text "Display is 0" [ref=w1e15]
3. windows_batch { "actions": [
{"action": "click", "ref": "w1e43"},
{"action": "click", "ref": "w1e35"},
{"action": "click", "ref": "w1e43"},
{"action": "click", "ref": "w1e38"},
{"action": "snapshot", "handle": "w1"}
]}
→ 1. click: Invoked Three
2. click: Invoked Multiply by
3. click: Invoked Three
4. click: Invoked Equals
5. snapshot: ... "Display is 9" ...

Installation

Prerequisites

  • Windows 10/11
  • .NET 8.0 Runtime

Download Release

Download the latest release from Releases and extract to a folder.

Choose the ZIP that matches your machine:

AssetUse when
FlaUI-MCP-win-x64-*-self-contained.zip64-bit Windows, no .NET runtime required
FlaUI-MCP-win-x64-*.zip64-bit Windows with .NET 8 Runtime already installed
FlaUI-MCP-win-arm64-*-self-contained.zipWindows on ARM64, no .NET runtime required
FlaUI-MCP-win-arm64-*.zipWindows on ARM64 with .NET 8 Runtime already installed

Configure MCP Client

Add to your MCP configuration (e.g., ~/.copilot/mcp-config.json):

{
"mcpServers": {
"windows": {
"type": "local",
"command": "C:\\path\\to\\FlaUI-MCP.exe",
"tools": ["*"]
}
}
}

Or using dotnet run:

{
"mcpServers": {
"windows": {
"type": "local",
"command": "dotnet",
"args": ["run", "--project", "C:\\path\\to\\src\\FlaUI.Mcp"]
}
}
}

Available Tools

ToolDescription
windows_launchLaunch a Windows application
windows_snapshotGet accessibility tree with element refs
windows_clickClick an element by ref
windows_typeType text into an element
windows_send_keysSend key presses or key chords (for example Ctrl+A)
windows_fillClear and fill a text field
windows_get_textGet text content of an element
windows_screenshotCapture window/element as PNG
windows_list_windowsList all open windows
windows_focusBring a window to foreground
windows_closeClose a window
windows_batchExecute multiple actions in one call

windows_screenshot supports an optional background: true argument when a window handle is provided. This uses native background capture when available and falls back to the normal screenshot path if Windows returns a blank frame. It can also save screenshots with savePath, which must be an absolute local .png path. Existing files are not replaced unless overwrite: true is set.

Tool calls have a 30-second timeout so a blocked UI Automation provider or modal dialog returns an actionable error instead of hanging the MCP server forever.

Keeping the Screen Awake

Long automation runs generate no keyboard or mouse input, so Windows would normally turn off the display and show the lock screen mid-run — which breaks screenshots and can freeze rendering. While tools are actively being called, FlaUI-MCP holds a Windows power availability request (the same signal video players and conferencing apps send) that keeps the display on and suppresses the idle lock. The request appears in powercfg /requests (run as admin) with the reason "FlaUI-MCP is driving Windows UI automation".

The request is released after 5 minutes without a tool call, so an idle MCP server does not keep your screen on. Configure via the FLAUI_MCP_KEEP_AWAKE_SECONDS environment variable: a positive value changes the idle period, 0 disables keep-awake entirely.

Note: this covers the common idle-lock paths (display timeout, screensaver, sleep). A domain group policy that enforces a hard machine inactivity limit ("Interactive logon: Machine inactivity limit") locks based on input idle time and is not suppressed by availability requests — no application can override that policy.

Restricting Which Apps Can Be Automated

By default, FlaUI-MCP can automate any application on the desktop — including File Explorer, browsers, and terminals. If the agent driving it is ever misled (for example by prompt injection), that is a large attack surface. Set the FLAUI_MCP_ALLOWED_APPS environment variable to restrict automation to specific apps:

{
"mcpServers": {
"windows": {
"type": "local",
"command": "C:\\path\\to\\FlaUI.Mcp.exe",
"env": {
"FLAUI_MCP_ALLOWED_APPS": "TabularEditor3"
}
}
}
}

The value is a semicolon- or comma-separated list of process names, matched case-insensitively, with or without .exe (full paths are reduced to their file name). When the variable is unset or empty, everything is allowed.

While the allowlist is active:

  • windows_launch refuses to start non-allowed executables.
  • Window handles are only ever issued for allowed processes, so every ref-based tool (snapshot, click, type, fill, get_text, screenshot by handle/ref) is automatically scoped to allowed apps. windows_list_windows lists only allowed apps' windows.
  • Ref-less keyboard input (windows_send_keys / windows_type without a ref) verifies the foreground window belongs to an allowed process first, and the Windows key is rejected outright (it opens system UI like the Start menu and Win+R outside any allowlist).
  • windows_screenshot refuses fullScreen capture and foreground-window capture of non-allowed apps, so other windows' content is not disclosed.

Scope honestly stated: matching is by process name, so this is a guard against a misdirected or prompt-injected agent driving unintended apps through this server — not a sandbox against a local attacker, and it does not restrict anything the agent can do through other tools (like a shell). Dialogs owned by the allowed process (including common file dialogs, which run in-process) keep working; apps it launches as separate processes (e.g. a browser for OAuth) are blocked unless also listed.

Tool Examples

Send a keyboard chord to a target element:

{
"ref": "w1e5",
"chord": "Ctrl+A"
}

Send a sequence of key presses or chords:

{
"keys": ["Ctrl+A", "Delete", "Enter"]
}

Capture a window using opt-in background capture:

{
"handle": "w1",
"background": true
}

Save a screenshot to disk without replacing existing files:

{
"handle": "w1",
"savePath": "C:\\Temp\\capture.png"
}

Replace an existing screenshot file explicitly:

{
"handle": "w1",
"savePath": "C:\\Temp\\capture.png",
"overwrite": true
}

Safety and Limitations

  • Keyboard input is focus-dependent. When you use windows_send_keys, the tool focuses the supplied ref first when possible, but Windows still sends keys to the active keyboard focus.
  • windows_screenshotbackground: true is only valid with a window handle. If native background capture returns a blank frame, FlaUI-MCP falls back to the normal capture path.
  • savePath accepts absolute local .png paths only. UNC paths, device paths, non-PNG extensions, and existing files without overwrite: true are rejected.
  • Desktop integration tests require an interactive Windows session because they launch real WinForms and WPF windows.
  • A timeout error means the MCP request returned, but a blocked Windows UI Automation provider or modal dialog may still need to be dismissed before retrying the operation.

How It Works

The Accessibility Snapshot

When you call windows_snapshot, you get a structured text tree:

- window "Calculator" [ref=w1e1]
- group "Number pad" [ref=w1e39]
- button "Seven" [ref=w1e47]
- button "Eight" [ref=w1e48]
- button "Nine" [ref=w1e49]
- text "Display is 0" [ref=w1e15]

This comes from Windows UI Automation - the same API screen readers use. Each element has:

  • Role (button, text, group, textbox)
  • Name ("Seven", "Display is 0")
  • Ref (w1e47) - a handle for interaction
  • State ([disabled], [readonly], [checked])

Why Not Screenshots?

ApproachProsCons
Accessibility TreeSemantic, precise, fast, works at any resolutionRequires UI Automation support
Screenshot + VisionWorks with any appSlow, expensive, imprecise, resolution-dependent

FlaUI-MCP uses accessibility because it's what screen readers use - it's designed for programmatic UI interaction.

Modal Dialogs

UI Automation pattern calls (Invoke, Toggle, Select) are synchronous cross-process calls. When a click handler opens a modal dialog (ShowDialog() in WinForms/WPF), the handler — and therefore the pattern call and the app's entire UIA provider — stays blocked until the dialog closes.

FlaUI-MCP handles this instead of hanging:

  • windows_click runs the pattern call on a background thread and watches the app's top-level windows via non-blocking Win32 APIs. If a modal appears, the tool returns immediately with the dialog's title.
  • While the call is pending, UIA-based tools targeting that app (windows_snapshot, windows_get_text, ref-based typing/clicking) fail fast with guidance instead of timing out.
  • Tools that don't need UIA keep working throughout: windows_screenshot, windows_send_keys / windows_typewithout a ref (pure keyboard input), windows_list_windows, windows_focus, and windows_close.

Typical flow: click a button → "modal dialog opened" → screenshot to see it → send keys (e.g. Enter or Tab+Enter) to dismiss it → snapshot works again.

Building from Source

# Clone
git clone https://github.com/shanselman/FlaUI-MCP.git
cd FlaUI-MCP
# Build
dotnet build src/FlaUI.Mcp
# Run
dotnet run --project src/FlaUI.Mcp

Testing

# Unit tests
dotnet test tests\FlaUI.Mcp.Tests
# Desktop integration tests; requires an interactive Windows session
dotnet test tests\FlaUI.Mcp.IntegrationTests

Architecture

┌─────────────────────────────────────────────────────────────────┐
│ AI Agent (GitHub Copilot, Claude, etc.) │
│ - Calls MCP tools: windows_snapshot, windows_click, etc. │
└─────────────────────────────────────────────────────────────────┘
│ MCP Protocol (JSON-RPC over stdio)
▼
┌─────────────────────────────────────────────────────────────────┐
│ FlaUI-MCP Server (.NET 8) │
│ - Implements MCP tool handlers │
│ - Builds agent-friendly accessibility snapshots │
│ - Maps element refs ↔ AutomationElements │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ FlaUI Library (github.com/FlaUI/FlaUI) │
│ - UIA3Automation for modern apps (WPF, UWP, Win32) │
│ - Control patterns: Invoke, Value, Toggle, Selection │
│ - Tree walking and element discovery │
└─────────────────────────────────────────────────────────────────┘

Supported Applications

Works with any Windows application that supports UI Automation:

  • ✅ Win32 apps (Notepad, Explorer, etc.)
  • ✅ WPF applications
  • ✅ WinForms applications
  • ✅ UWP/Store apps (Calculator, Settings, etc.)
  • ⚠️ Electron apps (partial - depends on accessibility implementation)
  • ❌ Games (typically no UI Automation support)

Contributing

Contributions welcome! Please see CONTRIBUTING.md for guidelines.

License

MIT License - see LICENSE for details.

Acknowledgments

  • FlaUI - The excellent .NET UI Automation library this project is built on
  • Playwright - Inspiration for the snapshot/ref interaction model
  • Model Context Protocol - The protocol that makes this possible

About

MCP server for Windows desktop automation using FlaUI and UI Automation APIs

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

FlaUI-MCP

An MCP (Model Context Protocol) server that enables AI agents to automate Windows desktop applications using accessibility APIs - the same way Playwright automates browsers.

BuildGitHub releaseLicense: MIT

Why This Exists

When Playwright's MCP server automates browsers, it provides:

  • browser_snapshot → Structured accessibility tree with element refs
  • browser_click ref="..." → Click by ref, not coordinates

FlaUI-MCP brings the same pattern to Windows desktop apps:

  • windows_snapshot → Accessibility tree with refs like w1e5
  • windows_click ref="w1e5" → Click element by ref

No screenshot parsing. No coordinate guessing. Just semantic element references.

Quick Demo

Agent: Calculate 3 × 3
1. windows_launch { "app": "calc.exe" }
→ Window handle: w1
2. windows_snapshot { "handle": "w1" }
→ - window "Calculator" [ref=w1]
- button "Three" [ref=w1e43]
- button "Multiply by" [ref=w1e35]
- button "Equals" [ref=w1e38]
- text "Display is 0" [ref=w1e15]
3. windows_batch { "actions": [
{"action": "click", "ref": "w1e43"},
{"action": "click", "ref": "w1e35"},
{"action": "click", "ref": "w1e43"},
{"action": "click", "ref": "w1e38"},
{"action": "snapshot", "handle": "w1"}
]}
→ 1. click: Invoked Three
2. click: Invoked Multiply by
3. click: Invoked Three
4. click: Invoked Equals
5. snapshot: ... "Display is 9" ...

Installation

Prerequisites

  • Windows 10/11
  • .NET 8.0 Runtime

Download Release

Download the latest release from Releases and extract to a folder.

Choose the ZIP that matches your machine:

AssetUse when
FlaUI-MCP-win-x64-*-self-contained.zip64-bit Windows, no .NET runtime required
FlaUI-MCP-win-x64-*.zip64-bit Windows with .NET 8 Runtime already installed
FlaUI-MCP-win-arm64-*-self-contained.zipWindows on ARM64, no .NET runtime required
FlaUI-MCP-win-arm64-*.zipWindows on ARM64 with .NET 8 Runtime already installed

Configure MCP Client

Add to your MCP configuration (e.g., ~/.copilot/mcp-config.json):

{
"mcpServers": {
"windows": {
"type": "local",
"command": "C:\\path\\to\\FlaUI-MCP.exe",
"tools": ["*"]
}
}
}

Or using dotnet run:

{
"mcpServers": {
"windows": {
"type": "local",
"command": "dotnet",
"args": ["run", "--project", "C:\\path\\to\\src\\FlaUI.Mcp"]
}
}
}

Available Tools

ToolDescription
windows_launchLaunch a Windows application
windows_snapshotGet accessibility tree with element refs
windows_clickClick an element by ref
windows_typeType text into an element
windows_send_keysSend key presses or key chords (for example Ctrl+A)
windows_fillClear and fill a text field
windows_get_textGet text content of an element
windows_screenshotCapture window/element as PNG
windows_list_windowsList all open windows
windows_focusBring a window to foreground
windows_closeClose a window
windows_batchExecute multiple actions in one call

windows_screenshot supports an optional background: true argument when a window handle is provided. This uses native background capture when available and falls back to the normal screenshot path if Windows returns a blank frame. It can also save screenshots with savePath, which must be an absolute local .png path. Existing files are not replaced unless overwrite: true is set.

Tool calls have a 30-second timeout so a blocked UI Automation provider or modal dialog returns an actionable error instead of hanging the MCP server forever.

Keeping the Screen Awake

Long automation runs generate no keyboard or mouse input, so Windows would normally turn off the display and show the lock screen mid-run — which breaks screenshots and can freeze rendering. While tools are actively being called, FlaUI-MCP holds a Windows power availability request (the same signal video players and conferencing apps send) that keeps the display on and suppresses the idle lock. The request appears in powercfg /requests (run as admin) with the reason "FlaUI-MCP is driving Windows UI automation".

The request is released after 5 minutes without a tool call, so an idle MCP server does not keep your screen on. Configure via the FLAUI_MCP_KEEP_AWAKE_SECONDS environment variable: a positive value changes the idle period, 0 disables keep-awake entirely.

Note: this covers the common idle-lock paths (display timeout, screensaver, sleep). A domain group policy that enforces a hard machine inactivity limit ("Interactive logon: Machine inactivity limit") locks based on input idle time and is not suppressed by availability requests — no application can override that policy.

Restricting Which Apps Can Be Automated

By default, FlaUI-MCP can automate any application on the desktop — including File Explorer, browsers, and terminals. If the agent driving it is ever misled (for example by prompt injection), that is a large attack surface. Set the FLAUI_MCP_ALLOWED_APPS environment variable to restrict automation to specific apps:

{
"mcpServers": {
"windows": {
"type": "local",
"command": "C:\\path\\to\\FlaUI.Mcp.exe",
"env": {
"FLAUI_MCP_ALLOWED_APPS": "TabularEditor3"
}
}
}
}

The value is a semicolon- or comma-separated list of process names, matched case-insensitively, with or without .exe (full paths are reduced to their file name). When the variable is unset or empty, everything is allowed.

While the allowlist is active:

  • windows_launch refuses to start non-allowed executables.
  • Window handles are only ever issued for allowed processes, so every ref-based tool (snapshot, click, type, fill, get_text, screenshot by handle/ref) is automatically scoped to allowed apps. windows_list_windows lists only allowed apps' windows.
  • Ref-less keyboard input (windows_send_keys / windows_type without a ref) verifies the foreground window belongs to an allowed process first, and the Windows key is rejected outright (it opens system UI like the Start menu and Win+R outside any allowlist).
  • windows_screenshot refuses fullScreen capture and foreground-window capture of non-allowed apps, so other windows' content is not disclosed.

Scope honestly stated: matching is by process name, so this is a guard against a misdirected or prompt-injected agent driving unintended apps through this server — not a sandbox against a local attacker, and it does not restrict anything the agent can do through other tools (like a shell). Dialogs owned by the allowed process (including common file dialogs, which run in-process) keep working; apps it launches as separate processes (e.g. a browser for OAuth) are blocked unless also listed.

Tool Examples

Send a keyboard chord to a target element:

{
"ref": "w1e5",
"chord": "Ctrl+A"
}

Send a sequence of key presses or chords:

{
"keys": ["Ctrl+A", "Delete", "Enter"]
}

Capture a window using opt-in background capture:

{
"handle": "w1",
"background": true
}

Save a screenshot to disk without replacing existing files:

{
"handle": "w1",
"savePath": "C:\\Temp\\capture.png"
}

Replace an existing screenshot file explicitly:

{
"handle": "w1",
"savePath": "C:\\Temp\\capture.png",
"overwrite": true
}

Safety and Limitations

  • Keyboard input is focus-dependent. When you use windows_send_keys, the tool focuses the supplied ref first when possible, but Windows still sends keys to the active keyboard focus.
  • windows_screenshotbackground: true is only valid with a window handle. If native background capture returns a blank frame, FlaUI-MCP falls back to the normal capture path.
  • savePath accepts absolute local .png paths only. UNC paths, device paths, non-PNG extensions, and existing files without overwrite: true are rejected.
  • Desktop integration tests require an interactive Windows session because they launch real WinForms and WPF windows.
  • A timeout error means the MCP request returned, but a blocked Windows UI Automation provider or modal dialog may still need to be dismissed before retrying the operation.

How It Works

The Accessibility Snapshot

When you call windows_snapshot, you get a structured text tree:

- window "Calculator" [ref=w1e1]
- group "Number pad" [ref=w1e39]
- button "Seven" [ref=w1e47]
- button "Eight" [ref=w1e48]
- button "Nine" [ref=w1e49]
- text "Display is 0" [ref=w1e15]

This comes from Windows UI Automation - the same API screen readers use. Each element has:

  • Role (button, text, group, textbox)
  • Name ("Seven", "Display is 0")
  • Ref (w1e47) - a handle for interaction
  • State ([disabled], [readonly], [checked])

Why Not Screenshots?

ApproachProsCons
Accessibility TreeSemantic, precise, fast, works at any resolutionRequires UI Automation support
Screenshot + VisionWorks with any appSlow, expensive, imprecise, resolution-dependent

FlaUI-MCP uses accessibility because it's what screen readers use - it's designed for programmatic UI interaction.

Modal Dialogs

UI Automation pattern calls (Invoke, Toggle, Select) are synchronous cross-process calls. When a click handler opens a modal dialog (ShowDialog() in WinForms/WPF), the handler — and therefore the pattern call and the app's entire UIA provider — stays blocked until the dialog closes.

FlaUI-MCP handles this instead of hanging:

  • windows_click runs the pattern call on a background thread and watches the app's top-level windows via non-blocking Win32 APIs. If a modal appears, the tool returns immediately with the dialog's title.
  • While the call is pending, UIA-based tools targeting that app (windows_snapshot, windows_get_text, ref-based typing/clicking) fail fast with guidance instead of timing out.
  • Tools that don't need UIA keep working throughout: windows_screenshot, windows_send_keys / windows_typewithout a ref (pure keyboard input), windows_list_windows, windows_focus, and windows_close.

Typical flow: click a button → "modal dialog opened" → screenshot to see it → send keys (e.g. Enter or Tab+Enter) to dismiss it → snapshot works again.

Building from Source

# Clone
git clone https://github.com/shanselman/FlaUI-MCP.git
cd FlaUI-MCP
# Build
dotnet build src/FlaUI.Mcp
# Run
dotnet run --project src/FlaUI.Mcp

Testing

# Unit tests
dotnet test tests\FlaUI.Mcp.Tests
# Desktop integration tests; requires an interactive Windows session
dotnet test tests\FlaUI.Mcp.IntegrationTests

Architecture

┌─────────────────────────────────────────────────────────────────┐
│ AI Agent (GitHub Copilot, Claude, etc.) │
│ - Calls MCP tools: windows_snapshot, windows_click, etc. │
└─────────────────────────────────────────────────────────────────┘
│ MCP Protocol (JSON-RPC over stdio)
▼
┌─────────────────────────────────────────────────────────────────┐
│ FlaUI-MCP Server (.NET 8) │
│ - Implements MCP tool handlers │
│ - Builds agent-friendly accessibility snapshots │
│ - Maps element refs ↔ AutomationElements │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ FlaUI Library (github.com/FlaUI/FlaUI) │
│ - UIA3Automation for modern apps (WPF, UWP, Win32) │
│ - Control patterns: Invoke, Value, Toggle, Selection │
│ - Tree walking and element discovery │
└─────────────────────────────────────────────────────────────────┘

Supported Applications

Works with any Windows application that supports UI Automation:

  • ✅ Win32 apps (Notepad, Explorer, etc.)
  • ✅ WPF applications
  • ✅ WinForms applications
  • ✅ UWP/Store apps (Calculator, Settings, etc.)
  • ⚠️ Electron apps (partial - depends on accessibility implementation)
  • ❌ Games (typically no UI Automation support)

Contributing

Contributions welcome! Please see CONTRIBUTING.md for guidelines.

License

MIT License - see LICENSE for details.

Acknowledgments

  • FlaUI - The excellent .NET UI Automation library this project is built on
  • Playwright - Inspiration for the snapshot/ref interaction model
  • Model Context Protocol - The protocol that makes this possible

About

MCP server for Windows desktop automation using FlaUI and UI Automation APIs

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

FlaUI-MCP

An MCP (Model Context Protocol) server that enables AI agents to automate Windows desktop applications using accessibility APIs - the same way Playwright automates browsers.

BuildGitHub releaseLicense: MIT

Why This Exists

When Playwright's MCP server automates browsers, it provides:

  • browser_snapshot → Structured accessibility tree with element refs
  • browser_click ref="..." → Click by ref, not coordinates

FlaUI-MCP brings the same pattern to Windows desktop apps:

  • windows_snapshot → Accessibility tree with refs like w1e5
  • windows_click ref="w1e5" → Click element by ref

No screenshot parsing. No coordinate guessing. Just semantic element references.

Quick Demo

Agent: Calculate 3 × 3
1. windows_launch { "app": "calc.exe" }
→ Window handle: w1
2. windows_snapshot { "handle": "w1" }
→ - window "Calculator" [ref=w1]
- button "Three" [ref=w1e43]
- button "Multiply by" [ref=w1e35]
- button "Equals" [ref=w1e38]
- text "Display is 0" [ref=w1e15]
3. windows_batch { "actions": [
{"action": "click", "ref": "w1e43"},
{"action": "click", "ref": "w1e35"},
{"action": "click", "ref": "w1e43"},
{"action": "click", "ref": "w1e38"},
{"action": "snapshot", "handle": "w1"}
]}
→ 1. click: Invoked Three
2. click: Invoked Multiply by
3. click: Invoked Three
4. click: Invoked Equals
5. snapshot: ... "Display is 9" ...

Installation

Prerequisites

  • Windows 10/11
  • .NET 8.0 Runtime

Download Release

Download the latest release from Releases and extract to a folder.

Choose the ZIP that matches your machine:

AssetUse when
FlaUI-MCP-win-x64-*-self-contained.zip64-bit Windows, no .NET runtime required
FlaUI-MCP-win-x64-*.zip64-bit Windows with .NET 8 Runtime already installed
FlaUI-MCP-win-arm64-*-self-contained.zipWindows on ARM64, no .NET runtime required
FlaUI-MCP-win-arm64-*.zipWindows on ARM64 with .NET 8 Runtime already installed

Configure MCP Client

Add to your MCP configuration (e.g., ~/.copilot/mcp-config.json):

{
"mcpServers": {
"windows": {
"type": "local",
"command": "C:\\path\\to\\FlaUI-MCP.exe",
"tools": ["*"]
}
}
}

Or using dotnet run:

{
"mcpServers": {
"windows": {
"type": "local",
"command": "dotnet",
"args": ["run", "--project", "C:\\path\\to\\src\\FlaUI.Mcp"]
}
}
}

Available Tools

ToolDescription
windows_launchLaunch a Windows application
windows_snapshotGet accessibility tree with element refs
windows_clickClick an element by ref
windows_typeType text into an element
windows_send_keysSend key presses or key chords (for example Ctrl+A)
windows_fillClear and fill a text field
windows_get_textGet text content of an element
windows_screenshotCapture window/element as PNG
windows_list_windowsList all open windows
windows_focusBring a window to foreground
windows_closeClose a window
windows_batchExecute multiple actions in one call

windows_screenshot supports an optional background: true argument when a window handle is provided. This uses native background capture when available and falls back to the normal screenshot path if Windows returns a blank frame. It can also save screenshots with savePath, which must be an absolute local .png path. Existing files are not replaced unless overwrite: true is set.

Tool calls have a 30-second timeout so a blocked UI Automation provider or modal dialog returns an actionable error instead of hanging the MCP server forever.

Keeping the Screen Awake

Long automation runs generate no keyboard or mouse input, so Windows would normally turn off the display and show the lock screen mid-run — which breaks screenshots and can freeze rendering. While tools are actively being called, FlaUI-MCP holds a Windows power availability request (the same signal video players and conferencing apps send) that keeps the display on and suppresses the idle lock. The request appears in powercfg /requests (run as admin) with the reason "FlaUI-MCP is driving Windows UI automation".

The request is released after 5 minutes without a tool call, so an idle MCP server does not keep your screen on. Configure via the FLAUI_MCP_KEEP_AWAKE_SECONDS environment variable: a positive value changes the idle period, 0 disables keep-awake entirely.

Note: this covers the common idle-lock paths (display timeout, screensaver, sleep). A domain group policy that enforces a hard machine inactivity limit ("Interactive logon: Machine inactivity limit") locks based on input idle time and is not suppressed by availability requests — no application can override that policy.

Restricting Which Apps Can Be Automated

By default, FlaUI-MCP can automate any application on the desktop — including File Explorer, browsers, and terminals. If the agent driving it is ever misled (for example by prompt injection), that is a large attack surface. Set the FLAUI_MCP_ALLOWED_APPS environment variable to restrict automation to specific apps:

{
"mcpServers": {
"windows": {
"type": "local",
"command": "C:\\path\\to\\FlaUI.Mcp.exe",
"env": {
"FLAUI_MCP_ALLOWED_APPS": "TabularEditor3"
}
}
}
}

The value is a semicolon- or comma-separated list of process names, matched case-insensitively, with or without .exe (full paths are reduced to their file name). When the variable is unset or empty, everything is allowed.

While the allowlist is active:

  • windows_launch refuses to start non-allowed executables.
  • Window handles are only ever issued for allowed processes, so every ref-based tool (snapshot, click, type, fill, get_text, screenshot by handle/ref) is automatically scoped to allowed apps. windows_list_windows lists only allowed apps' windows.
  • Ref-less keyboard input (windows_send_keys / windows_type without a ref) verifies the foreground window belongs to an allowed process first, and the Windows key is rejected outright (it opens system UI like the Start menu and Win+R outside any allowlist).
  • windows_screenshot refuses fullScreen capture and foreground-window capture of non-allowed apps, so other windows' content is not disclosed.

Scope honestly stated: matching is by process name, so this is a guard against a misdirected or prompt-injected agent driving unintended apps through this server — not a sandbox against a local attacker, and it does not restrict anything the agent can do through other tools (like a shell). Dialogs owned by the allowed process (including common file dialogs, which run in-process) keep working; apps it launches as separate processes (e.g. a browser for OAuth) are blocked unless also listed.

Tool Examples

Send a keyboard chord to a target element:

{
"ref": "w1e5",
"chord": "Ctrl+A"
}

Send a sequence of key presses or chords:

{
"keys": ["Ctrl+A", "Delete", "Enter"]
}

Capture a window using opt-in background capture:

{
"handle": "w1",
"background": true
}

Save a screenshot to disk without replacing existing files:

{
"handle": "w1",
"savePath": "C:\\Temp\\capture.png"
}

Replace an existing screenshot file explicitly:

{
"handle": "w1",
"savePath": "C:\\Temp\\capture.png",
"overwrite": true
}

Safety and Limitations

  • Keyboard input is focus-dependent. When you use windows_send_keys, the tool focuses the supplied ref first when possible, but Windows still sends keys to the active keyboard focus.
  • windows_screenshotbackground: true is only valid with a window handle. If native background capture returns a blank frame, FlaUI-MCP falls back to the normal capture path.
  • savePath accepts absolute local .png paths only. UNC paths, device paths, non-PNG extensions, and existing files without overwrite: true are rejected.
  • Desktop integration tests require an interactive Windows session because they launch real WinForms and WPF windows.
  • A timeout error means the MCP request returned, but a blocked Windows UI Automation provider or modal dialog may still need to be dismissed before retrying the operation.

How It Works

The Accessibility Snapshot

When you call windows_snapshot, you get a structured text tree:

- window "Calculator" [ref=w1e1]
- group "Number pad" [ref=w1e39]
- button "Seven" [ref=w1e47]
- button "Eight" [ref=w1e48]
- button "Nine" [ref=w1e49]
- text "Display is 0" [ref=w1e15]

This comes from Windows UI Automation - the same API screen readers use. Each element has:

  • Role (button, text, group, textbox)
  • Name ("Seven", "Display is 0")
  • Ref (w1e47) - a handle for interaction
  • State ([disabled], [readonly], [checked])

Why Not Screenshots?

ApproachProsCons
Accessibility TreeSemantic, precise, fast, works at any resolutionRequires UI Automation support
Screenshot + VisionWorks with any appSlow, expensive, imprecise, resolution-dependent

FlaUI-MCP uses accessibility because it's what screen readers use - it's designed for programmatic UI interaction.

Modal Dialogs

UI Automation pattern calls (Invoke, Toggle, Select) are synchronous cross-process calls. When a click handler opens a modal dialog (ShowDialog() in WinForms/WPF), the handler — and therefore the pattern call and the app's entire UIA provider — stays blocked until the dialog closes.

FlaUI-MCP handles this instead of hanging:

  • windows_click runs the pattern call on a background thread and watches the app's top-level windows via non-blocking Win32 APIs. If a modal appears, the tool returns immediately with the dialog's title.
  • While the call is pending, UIA-based tools targeting that app (windows_snapshot, windows_get_text, ref-based typing/clicking) fail fast with guidance instead of timing out.
  • Tools that don't need UIA keep working throughout: windows_screenshot, windows_send_keys / windows_typewithout a ref (pure keyboard input), windows_list_windows, windows_focus, and windows_close.

Typical flow: click a button → "modal dialog opened" → screenshot to see it → send keys (e.g. Enter or Tab+Enter) to dismiss it → snapshot works again.

Building from Source

# Clone
git clone https://github.com/shanselman/FlaUI-MCP.git
cd FlaUI-MCP
# Build
dotnet build src/FlaUI.Mcp
# Run
dotnet run --project src/FlaUI.Mcp

Testing

# Unit tests
dotnet test tests\FlaUI.Mcp.Tests
# Desktop integration tests; requires an interactive Windows session
dotnet test tests\FlaUI.Mcp.IntegrationTests

Architecture

┌─────────────────────────────────────────────────────────────────┐
│ AI Agent (GitHub Copilot, Claude, etc.) │
│ - Calls MCP tools: windows_snapshot, windows_click, etc. │
└─────────────────────────────────────────────────────────────────┘
│ MCP Protocol (JSON-RPC over stdio)
▼
┌─────────────────────────────────────────────────────────────────┐
│ FlaUI-MCP Server (.NET 8) │
│ - Implements MCP tool handlers │
│ - Builds agent-friendly accessibility snapshots │
│ - Maps element refs ↔ AutomationElements │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ FlaUI Library (github.com/FlaUI/FlaUI) │
│ - UIA3Automation for modern apps (WPF, UWP, Win32) │
│ - Control patterns: Invoke, Value, Toggle, Selection │
│ - Tree walking and element discovery │
└─────────────────────────────────────────────────────────────────┘

Supported Applications

Works with any Windows application that supports UI Automation:

  • ✅ Win32 apps (Notepad, Explorer, etc.)
  • ✅ WPF applications
  • ✅ WinForms applications
  • ✅ UWP/Store apps (Calculator, Settings, etc.)
  • ⚠️ Electron apps (partial - depends on accessibility implementation)
  • ❌ Games (typically no UI Automation support)

Contributing

Contributions welcome! Please see CONTRIBUTING.md for guidelines.

License

MIT License - see LICENSE for details.

Acknowledgments

  • FlaUI - The excellent .NET UI Automation library this project is built on
  • Playwright - Inspiration for the snapshot/ref interaction model
  • Model Context Protocol - The protocol that makes this possible

About

MCP server for Windows desktop automation using FlaUI and UI Automation APIs

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages