Windows-MCP
Enables browser automation for Firefox through DOM mode, allowing agents to inspect web page content and interact with pages using an IAccessible2 fallback.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Windows-MCPlist the files in my Downloads folder"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
This is a fork of CursorTouch/Windows-MCP, based on upstream main at a868ed6 (version 0.8.5), made by kino-tk.
I made these changes for my own use. Windows-MCP kept getting in my way under Claude Desktop (hanging calls, failing ssh, commands cut off after four minutes, garbled Japanese, CRLF line endings), so I changed it to work the way I wanted. They are published as they are.
The original README follows unchanged after this section.
About this fork
Why
Under Claude Desktop, an MSIX-packaged host, Windows-MCP had these practical problems for me:
Calls hung after the command had finished. A detached grandchild inherited the capture pipes, so the reader never saw EOF.
ssh,scpandsftpfailed with exit code 255 and no output. The packaged host hands down an environment block withoutProgramDataand the other known-folder variables, and Win32 OpenSSH aborts before it even parses its arguments.Anything longer than about four minutes was cut off. The host abandons a tool call after 240 s, and a server cannot extend that.
When a call did hang, nothing showed which tool or command it was. The host's log records only that a
tools/callwas sent.Japanese text printed by Python came back garbled. With its output captured, Python writes in the Windows code page (cp932 here), while the server reads the output as UTF-8.
Files written by the FileSystem tool always had CRLF line endings. I move files back and forth between Linux and Windows, and because the two use different line endings I had to check them every time, which was tedious. I wanted files that I bring down to Windows to keep LF line endings, so I added a way to choose them.
Installation
The upstream instructions further down (uvx windows-mcp, PyPI, the Claude Desktop extension) install the upstream package, which does not include these changes. To use this fork, run it from a clone:
Clone the fork. uv is required; it fetches Python 3.14 and the dependencies on the first run.
git clone https://github.com/kino-tk/Windows-MCP-private.git C:\path\to\Windows-MCP-privateAdd the server to
claude_desktop_config.json(for Claude Desktop:%APPDATA%\Claude\claude_desktop_config.json, also reachable from Settings → Developer → Edit Config):{ "mcpServers": { "windows-mcp": { "command": "uv", "args": ["--directory", "C:\\path\\to\\Windows-MCP-private", "run", "windows-mcp", "serve"] } } }If Claude Desktop cannot find
uv, give its full path incommand(runwhere uvto see it).Restart Claude Desktop to load the server. Quit it completely, including the tray icon and any Claude processes left in Task Manager, then start it again.
Verified on 2026-09-26: a fresh clone started this way lists 21 tools, including PowerShellJob.
Changes
Commit | Source files | Change |
|
| Capture output into temporary files instead of pipes, so an orphaned grandchild cannot block the call. Measured on a 60 s detached job with |
|
| Restore |
|
| Bound every wait on a child process tree. |
|
| Background jobs for long PowerShell commands, and the new |
|
| Default |
|
| Bind every job to the server with a Windows Job Object, record each job in a metadata file, and clean up after an earlier server run at start. See Job lifetime and cleanup. |
|
| Sweep finished jobs and stray files on a schedule, so a server left running for days still cleans up. Retention and interval are configurable. |
|
| Keep what a finished job left running bound to the job, even if its root process was killed from outside; add a script that reports every job record and, as a last resort, kills a verified PID. The scheduled sweep defaults to every 90 minutes. |
|
| FileSystem |
|
| Job cleanup only ever reads or deletes files named like job files, so other files in the job directory are safe. |
Paths are under src/windows_mcp/, except scripts/.
Long commands: timeout and PowerShellJob
Command syntax
PowerShell command=<string> [timeout=<seconds>]
PowerShellJob action=list
PowerShellJob action=status job_id=<id> [tail_chars=<n>]
PowerShellJob [action=wait] job_id=<id> [wait_seconds=<seconds>] [tail_chars=<n>]
PowerShellJob action=kill job_id=<id>[ ]marks an optional argument;<...>is a value you supply. Anything not listed for a form is ignored.MCP arguments are passed by name, so their order does not matter; the forms above show the conventional order.
action=waitis the default, soPowerShellJob job_id=<id>waits.
Argument | Tool | Default | Meaning |
| PowerShell | (required) | The PowerShell command line to run. |
| PowerShell |
| Hard limit in seconds; the command is killed when it expires. No upper bound. |
| PowerShellJob |
|
|
| PowerShellJob | (required except for | The id returned by PowerShell, for example |
| PowerShellJob |
| For |
| PowerShellJob |
| While the command is still running: how many of the latest characters of stdout and of stderr to show. A finished job always shows its full output. |
How it behaves
If
timeoutis longer than about 200 s and the command is still running at that point, the PowerShell call returns early with the output so far,Status Code: runningand a job id. The command keeps running.A command that finishes within 200 s returns exactly what a plain call would.
Calls whose
timeoutis 200 s or less take the pre-job code path (execute_command), which still has the bounded waits added inf789895.waitreturns the full output and the exit code once the command has finished. If it is still running,waitreturns the latest output withStatus Code: running; call it again.statusreturns at once.killstops the command and every process it started.listshows every job with its state.Output is written to files, so it can be read while the command runs. Programs must print progressively to show partial output (for Python, use
-u). Interactive prompts are not supported.
Examples
Results are shown without the Response: prefix that the tool puts before the first line.
PowerShell command="ssh host 'long-task.sh'" timeout=600
-> Still running after 200s (waited 200s in this call). The command continues in the
background as job-1 (PID 4196); it is stopped at its hard timeout of 600s ...
--- output so far (last 4000 chars of each stream) ---
...
Status Code: running
PowerShellJob action=status job_id=job-1 tail_chars=500
-> Still running after 245s ... (only the last 500 characters of output)
Status Code: running
PowerShellJob job_id=job-1 wait_seconds=200
-> job-1 finished: exited, exit 0, after 302s.
<full output>
Status Code: 0
PowerShellJob action=kill job_id=job-2
-> job-2 finished: killed, exit 1, after 40s. ...
Status Code: 1
PowerShellJob action=list
-> job-1: exited, exit 0, 302s, PID 4196, hard timeout 600s: ssh host 'long-task.sh'
job-2: killed, exit 1, 40s, PID 5120, hard timeout 900s: ...
Status Code: 0The first and third results are from a verified run under Claude Desktop.
Listing jobs
PowerShellJob action=listExample response (illustrative values):
job-1: killed (from an earlier server run), 5s, PID 77412, hard timeout 900s: & 'C:\tools\python.exe' -u 'C:\work\train.py'
job-3: running, 412s, PID 80844, hard timeout 900s: & 'C:\tools\python.exe' -u 'C:\work\long-task.py'
job-4: exited, exit 0, 245s, PID 81120, hard timeout 600s: ssh host 'backup.sh'
job-5: exited, exit 0, 1 leftover process(es), 230s, PID 81502, hard timeout 600s: & '.\start-worker.ps1'
Status Code: 0Jobs are listed in the order they started. Each line is <job id>: <state>[, exit <code>][, (from an earlier server run)][, <n> leftover process(es)], <elapsed>, PID <pid>, hard timeout <seconds>: <command>. The PID is that of the PowerShell process that runs the command. No jobs. means there are none.
If kill does not work (last resort)
Normally PowerShellJob action=kill job_id=<id> is enough, and if the server itself is gone, every job's processes have already ended with it (the one exception is a job recorded with "job_object": false, see the fallback under Job lifetime and cleanup; its leftovers live until the next server start, which kills them after checking PID and start time). If a job still seems to be running, or the server does not answer, use the job records directly:
Find the PID. If the server answers,
PowerShellJob action=listshows it. If not, run the report script from the clone. It reads every record (~/.windows-mcp/jobs/*.json) and prints all its fields, plus two live checks:RunningisTrueonly if the recorded PID still belongs to the same process (same PID and same start time), andServersays whether the owning server is still running. Add-Tail 20to see the last 20 lines of each job's output.powershell -ExecutionPolicy Bypass -File C:\path\to\Windows-MCP-private\scripts\windows-mcp-jobs.ps1Id : job-3 Record : 75036-job-3 State : running Running : True Pid : 80844 PidStarted : 2026/09/26 21:10:05 PidCreatedEpoch : 1790424605.123 ExitCode : Started : 2026/09/26 21:10:05 Ended : HardTimeout : 900 ServerPid : 75036 Server : running JobObject : True Adopted : False Command : & 'C:\tools\python.exe' -u 'C:\work\long-task.py' Note : OutFile : C:\Users\you\.windows-mcp\jobs\75036-job-3.out OutBytes : 1834 ErrFile : C:\Users\you\.windows-mcp\jobs\75036-job-3.err ErrBytes : 0 RecordFile : C:\Users\you\.windows-mcp\jobs\75036-job-3.json(Illustrative values; dates follow your locale.)
Kill it, only if
RunningisTrue. Either let the script do it, which re-checks the PID and lists what it will kill:powershell -ExecutionPolicy Bypass -File C:\path\to\Windows-MCP-private\scripts\windows-mcp-jobs.ps1 -Kill job-3or run taskkill yourself with the PID from step 1:
taskkill /PID 80844 /T /FIf
RunningisFalse, do not use that PID: the job's process is gone, and Windows may have given the number to an unrelated process.Descendants whose parent has already exited are not reached by
taskkill /T, which follows parent links. They stay bound to the job, soPowerShellJob action=kill job_id=job-3ends them. If the server does not answer, restart Claude Desktop: when the server exits, Windows ends every process in its jobs. (For a job recorded with"job_object": false, the next server start does this instead, after checking PID and start time.)
Job lifetime and cleanup
Every job is bound to the server process that started it. Normally you never need to find and kill a job's processes yourself; the last-resort steps above are for when something has gone wrong.
Situation | What happens |
The command finishes | Its output and exit code stay available to |
| CTRL_BREAK is sent. As soon as the command exits, or after 2 s if it does not, whatever is left of its process tree is ended through the Job Object, including grandchildren whose parent has already exited. |
The server shuts down normally | Running jobs are stopped and recorded as killed, with the reason. |
The server crashes or is killed | Windows closes the server's Job Object handles, which immediately ends every process bound to a job, running or left over. No job keeps running unsupervised. (In the fallback case described below, leftovers are instead killed at the next server start.) |
The next server starts | It reads the records left by servers that are gone. A leftover process is killed only if both its PID and its creation time match the record, so a process that merely reused the PID is never touched. The job is then listed as |
How this works: each command is started suspended, placed in its own Windows Job Object with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, and only then resumed, so nothing it starts can escape the Job Object. Only a command that finishes within its first call has its Job Object released without killing. If a Job Object cannot be created, the job still runs: this is logged and recorded as "job_object": false, kill and the hard timeout fall back to taskkill /T, and the next server start still kills verified leftovers.
No depth limit. Windows puts every process that a job's process starts into the same Job Object, at any depth, so there is no cut-off at children or grandchildren. Verified on 2026-09-26:
A chain of 7 processes, in which every intermediate generation had already exited (so the deepest one was an orphan several levels down and ignored CTRL_BREAK), was ended completely by
killand by closing the Job Object.A child that asks to leave the Job Object (
CREATE_BREAKAWAY_FROM_JOB) is refused with "access denied".
What is not covered: a program that is not a descendant of the command, because Windows starts it on behalf of another process. Examples are a program launched through a Windows service, Task Scheduler, WMI (Win32_Process.Create), an out-of-process COM server, or with elevation (UAC). Such a program is outside the Job Object, just as it is outside the command's process tree. (This follows from how Windows creates those processes; it was not tested here.)
Files. Each job keeps three files in ~/.windows-mcp/jobs, named <server PID>-<job id>. Only files named exactly like this (<digits>-job-<digits>.out, .err, .json, and a leftover .json.tmp) are ever read or deleted, so other files in the directory are never touched. Still, if you set WINDOWS_MCP_JOB_DIR, point it at a folder of its own.
File | Content |
| stdout |
| stderr |
| The job's record: id, PID and its creation time, the first 200 characters of the command, hard timeout, state, exit code, start and end times, note, and the server that owns it. It is rewritten atomically whenever the state changes. |
When files are deleted.
A finished job is kept for at least 6 hours after it ended (
WINDOWS_MCP_JOB_RETENTION_HOURS), and deleted at the first sweep after that. With the default settings it therefore disappears between 6 and 7.5 hours after it ended. Until then it stays inlist.A sweeper thread in the server checks every 90 minutes (
WINDOWS_MCP_JOB_SWEEP_INTERVAL) and deletes the three files of every job past its retention. A server left running for days therefore still cleans up on schedule, even if no one touches it. Server start, any new job and anyPowerShellJobcall sweep as well. Deleting a job also ends any leftover process still bound to it.A job that finishes within the first 200 s of its call is deleted right away, because its output was already returned.
Job files that no record claims (for example output left by older versions of this fork) are deleted by the same sweep once they are older than 10 minutes. Only names of the job-file form are considered.
Job numbers continue across restarts, so an id is never reused while an older job with it is still on disk.
Changing the cleanup settings. Add an env block to the server's entry in claude_desktop_config.json. Values are strings; the interval is in seconds and the retention in hours:
{
"mcpServers": {
"windows-mcp": {
"command": "uv",
"args": ["--directory", "C:\\path\\to\\Windows-MCP-private", "run", "windows-mcp", "serve"],
"env": {
"WINDOWS_MCP_JOB_SWEEP_INTERVAL": "600",
"WINDOWS_MCP_JOB_RETENTION_HOURS": "24"
}
}
}
}This example sweeps every 10 minutes and keeps finished jobs for 24 hours. The server reads its environment only when it starts, so restart Claude Desktop completely (tray icon and any Claude processes in Task Manager included) for the change to take effect. Remove the lines to go back to the defaults (90 minutes, 6 hours).
Line endings in FileSystem write
Upstream, the FileSystem tool's write mode opens files in text mode, so on Windows every \n becomes \r\n and files meant for Linux always come out with CRLF line endings. In this fork the line endings are exactly what you ask for:
FileSystem mode=write path=<file> content=<text> [append=true] [encoding=<name>] [line_ending=keep|lf|crlf]
| Result |
| The content is written exactly as given. Text sent with |
| Every line break becomes LF, the Linux line ending. |
| Every line break becomes CRLF, for files such as |
append=true follows the same rule. Windows Notepad reads and keeps LF files correctly since Windows 10 version 1809 (it shows "Unix (LF)" in the status bar), so LF is safe for files you also open on Windows.
To change the default for every write, set WINDOWS_MCP_WRITE_LINE_ENDING in the env block of the server's entry in claude_desktop_config.json (the same place as the cleanup settings above), then restart Claude Desktop completely:
"env": {
"WINDOWS_MCP_WRITE_LINE_ENDING": "lf"
}An explicit line_ending argument always wins over the default. An unknown value in the environment variable is ignored (the default stays keep); an unknown argument value is reported as an error and nothing is written.
Tool call log
Every tool call writes a start and an end record to ~/.windows-mcp/calls.log as JSON lines, plus a cancelled record if the host gives up first. Jobs add job_start and job_end records, and job_adopted when a new server takes over the record of a job from an earlier run.
idis<server PID>-<sequence>and pairs a call'sstart,cancelledandend. Job records use the job id.toolis the server's internal label (for examplePowershell-Tool), not the MCP tool name.A call stuck inside the server shows up as a
startwithout anend.For sync tools the
endrecord is written from the worker thread, so after acancelledit still shows when the work really stopped.Commands are truncated to 200 characters. Free-text arguments (
content,text,value,data,input) are logged by length only.The file rotates at 1 MB and keeps 3 older generations, about 4 MB in total.
A call that became a job (from a verified run; the command is replaced, and job_start also shows the job_object field that the current version adds):
{"ts": "2026-09-26T09:21:06.411+09:00", "event": "start", "id": "9696-1", "tool": "Powershell-Tool", "args": {"command": "ssh host 'long-task.sh'", "timeout": 600}, "client": "claude-ai"}
{"ts": "2026-09-26T09:21:06.447+09:00", "event": "job_start", "id": "job-1", "tool": "PowerShell", "pid": 4196, "hard_timeout": 600.0, "job_object": true, "command": "ssh host 'long-task.sh'"}
{"ts": "2026-09-26T09:24:26.471+09:00", "event": "end", "id": "9696-1", "tool": "Powershell-Tool", "status": "ok", "duration_ms": 200059, "result_chars": 788, "exit": "running"}
{"ts": "2026-09-26T09:26:07.956+09:00", "event": "job_end", "id": "job-1", "tool": "PowerShell", "state": "exited", "returncode": 0, "duration_ms": 301509}A job whose server was killed, taken over by the next server (real record):
{"ts": "2026-09-26T18:56:47.000+09:00", "event": "job_adopted", "id": "job-1", "tool": "PowerShell", "state": "killed", "previous_server_pid": 75036, "note": "Its Windows-MCP server exited while it was running; the job was ended together with the server."}A call the host abandoned (illustrative values, in the format the server writes):
{"ts": "2026-09-26T10:00:00.010+09:00", "event": "start", "id": "9696-7", "tool": "Screenshot-Tool", "args": {}, "client": "claude-ai"}
{"ts": "2026-09-26T10:04:00.012+09:00", "event": "cancelled", "id": "9696-7", "tool": "Screenshot-Tool", "after_ms": 240002}
{"ts": "2026-09-26T10:05:13.550+09:00", "event": "end", "id": "9696-7", "tool": "Screenshot-Tool", "status": "ok", "duration_ms": 313540, "result_chars": 5120}Environment variables added by this fork
Variable | Default | Meaning |
|
| Seconds a call waits before handing a command over as a job (capped at 225) |
|
| Where job output and records are written. Use a folder of its own. |
|
| How long a finished job is kept |
|
| Seconds between scheduled sweeps (90 minutes) |
|
| Call log path, or |
|
| Rotation threshold in bytes |
|
| Older generations to keep |
|
| Default line endings for FileSystem |
PYTHONIOENCODING is set to utf-8 only when it is not already set, and a command can still override it.
Notes
PowerShell reports a failing native command as exit code 1. Append
; exit $LASTEXITCODEto pass the original code on. This is unchanged upstream behaviour.Text output is UTF-8 end to end for PowerShell itself and for the programs checked on 2026-09-26 (
cmd,dir,where,findstr, git, node, and Python with the default above). The exception is older tools that read text piped into them in the system's ANSI code page, such assort.exeon a Japanese system; setting[Console]::InputEncodingdoes not change that. Use the PowerShell equivalent (Sort-Object) instead.Tests: 572 passed, against an upstream baseline of 508. The new tests are
tests/test_kill_bounds.py,tests/test_calllog.py,tests/test_powershell_jobs.py,tests/test_job_lifecycle.py,tests/test_python_io_encoding.pyandtests/test_line_endings.py.
License
MIT, the same as upstream. Copyright (c) 2025 JEOMON GEORGE; see LICENSE.md. The changes in this fork are provided under the same license.
Windows-MCP is a lightweight, open-source project that enables seamless integration between AI agents and the Windows operating system. Acting as an MCP server bridges the gap between LLMs and the Windows operating system, allowing agents to perform tasks such as file navigation, application control, UI interaction, QA testing, and more.
mcp-name: io.github.CursorTouch/Windows-MCP
Related MCP server: Windows-MCP
Updates
Windows-MCP reached
2M+ Usersin Claude Desktop Extensiosn.Try out 🪟Windows-Use, an agent built using Windows-MCP.
Windows-MCP is now available on PyPI (thus supports
uvx windows-mcp)Windows-MCP is added to MCP Registry
Supported Operating Systems
Windows 7
Windows 8, 8.1
Windows 10
Windows 11
🎥 Demos
https://github.com/user-attachments/assets/d0e7ed1d-6189-4de6-838a-5ef8e1cad54e
https://github.com/user-attachments/assets/d2b372dc-8d00-4d71-9677-4c64f5987485
✨ Key Features
Seamless Windows Integration
Interacts natively with Windows UI elements, opens apps, controls windows, simulates user input, and more.Use Any LLM (Vision Optional) Unlike many automation tools, Windows-MCP doesn't rely on any traditional computer vision techniques or specific fine-tuned models; it works with any LLMs, reducing complexity and setup time.
Rich Toolset for UI Automation
Includes tools for basic keyboard, mouse operation and capturing window/UI state.Lightweight & Open-Source
Minimal dependencies and easy setup with full source code available under MIT license.Customizable & Extendable
Easily adapt or extend tools to suit your unique automation or AI integration needs.Real-Time Interaction
Typical latency between actions (e.g., from one mouse click to the next) ranges from 0.2 to 0.5 secs, and may slightly vary based on the number of active applications and system load, also the inferencing speed of the llm.DOM Mode for Browser Automation
Specialuse_dom=Truemode for State-Tool that focuses exclusively on web page content, filtering out browser UI elements for cleaner, more efficient web automation. Supports Chrome, Edge, and Firefox (Firefox uses an IAccessible2 fallback since it doesn't exposeRootWebAreavia UIA).
🛠️Installation
Note: When you install this MCP server for the first time it may take a minute or two because of installing the dependencies in pyproject.toml. In the first run the server may timeout ignore it and restart it.
Prerequisites
Python 3.13+
UV (Package Manager) from Astra, install with
pip install uvorcurl -LsSf https://astral.sh/uv/install.sh | shEnglishas the default language in Windows preferred else disable theApp-Toolin the MCP Server for Windows with other languages.
Run at Login
Run the server directly when needed:
uvx windows-mcp serve
uvx windows-mcp serve --transport sse --host localhost --port 8000
uvx windows-mcp serve --transport streamable-http --host localhost --port 8000Install it as a background task that starts now and at every login:
windows-mcp install
# Or choose the HTTP transport and bind address explicitly
windows-mcp install --transport sse --host 127.0.0.1 --port 8000This creates a per-user Scheduled Task named windows-mcp-server and a wrapper script at
~/.windows-mcp/start-server.cmd. Use windows-mcp uninstall to remove it. Logs are written
to ~/.windows-mcp/server.log and ~/.windows-mcp/server.error.log.
Install Claude Desktop.
npm install -g @anthropic-ai/mcpbConfigure the MCP server.
Option A: Install from PyPI (Recommended)
Use uvx to run the latest version directly from PyPI.
Add this to your claude_desktop_config.json:
{
"mcpServers": {
"windows-mcp": {
"command": "uvx",
"args": [
"windows-mcp",
"serve"
]
}
}
}Option B: Install from Source
Clone the repository:
git clone https://github.com/CursorTouch/Windows-MCP.git
cd Windows-MCPAdd this to your
claude_desktop_config.json:
{
"mcpServers": {
"windows-mcp": {
"command": "uv",
"args": [
"--directory",
"<path to the windows-mcp directory>",
"run",
"windows-mcp",
"serve"
]
}
}
}Fully restart Claude Desktop and verify the server appears in the MCP tools list.
Claude Desktop MSIX (Windows Store)
The MSIX-packaged Claude Desktop (Microsoft Store version) virtualizes %APPDATA%. This causes two main issues:
The config file is located at:
%LOCALAPPDATA%\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\Claude\claude_desktop_config.json(not%APPDATA%\Claude\).Automatic installation from the "Claude Directory" will fail because the
${__dirname}variable resolves to the incorrect (non-virtualized) path.
To configure Windows-MCP on the Windows Store version of Claude:
You must manually edit the configuration file. Note that Electron apps in the MSIX sandbox do not inherit the system PATH, so you must use the full absolute path to uvx.exe (or uv.exe).
Option A: Using pre-installed executable
In a terminal, run
uv tool install windows-mcp.Use the generated executable in your config:
{
"mcpServers": {
"windows-mcp": {
"command": "C:\\Users\\<user>\\.local\\bin\\windows-mcp.exe",
"args": ["serve"]
}
}
}Option B: Using uvx
{
"mcpServers": {
"windows-mcp": {
"command": "C:\\Users\\<user>\\.local\\bin\\uvx.exe",
"args": ["windows-mcp", "serve"]
}
}
}Option C: Install from Source
{
"mcpServers": {
"windows-mcp": {
"command": "C:\\Users\\<user>\\.local\\bin\\uv.exe",
"args": [
"--directory",
"C:\\path\\to\\Windows-MCP",
"run",
"windows-mcp",
"serve"
]
}
}
} Replace <user> with your Windows username. To find the correct paths, run where uvx, where windows-mcp, or where uv. Fully quit Claude Desktop (Tray → Quit) and reopen after saving the config.
For additional Claude Desktop integration troubleshooting, see the MCP documentation.
Install Perplexity Desktop.
Open Perplexity Desktop and go to
Settings -> Connectors -> Add Connector -> Advanced.Enter the name as
Windows-MCP, then paste one of the following configs.
Option A: Install from PyPI (Recommended)
{
"command": "uvx",
"args": [
"windows-mcp",
"serve"
]
}Option B: Install from Source
{
"command": "uv",
"args": [
"--directory",
"<path to the windows-mcp directory>",
"run",
"windows-mcp",
"serve"
]
}Click
Save, then restart Perplexity Desktop if needed.
For additional Claude Desktop integration troubleshooting, see the Perplexity MCP Support. The documentation includes helpful tips for checking logs and resolving common issues.
Install Gemini CLI.
npm install -g @google/gemini-cliOpen
%USERPROFILE%/.gemini/settings.json.Add the
windows-mcpconfig and save it.
{
"theme": "Default",
...
"mcpServers": {
"windows-mcp": {
"command": "uvx",
"args": [
"windows-mcp",
"serve"
]
}
}
}Note: To run from source, replace the command with uv and args with ["--directory", "<path>", "run", "windows-mcp", "serve"].
Restart Gemini CLI.
npm install -g @qwen-code/qwen-code@latestOpen
%USERPROFILE%/.qwen/settings.json.Add the
windows-mcpconfig and save it.
{
"mcpServers": {
"windows-mcp": {
"command": "uvx",
"args": [
"windows-mcp",
"serve"
]
}
}
}Note: To run from source, replace the command with uv and args with ["--directory", "<path>", "run", "windows-mcp", "serve"].
Restart Qwen Code.
npm install -g @openai/codexOpen
%USERPROFILE%/.codex/config.toml.Add the
windows-mcpconfig and save it.
[mcp_servers.windows-mcp]
command="uvx"
args=[
"windows-mcp",
"serve"
]Note: To run from source, replace the command with uv and args with ["--directory", "<path>", "run", "windows-mcp", "serve"].
Restart Codex CLI.
Add the published stdio server from a Windows terminal:
autohand mcp add windows-mcp uvx windows-mcp serve Add --scope project after add to keep the server configuration in the current project. See Autohand Code for current installation and CLI details.
Install Claude Code:
npm install -g @anthropic-ai/claude-codeConfigure the server:
Option A: Install from PyPI (Recommended)
Use uvx to run the latest version directly from PyPI.
claude mcp add --transport stdio windows-mcp -- uvx windows-mcp serveOption B: Install from Source
Clone the repository:
git clone https://github.com/CursorTouch/Windows-MCP.git
cd Windows-MCPRun the following command in your terminal:
claude mcp add --transport stdio windows-mcp -- uv --directory "<path>" run windows-mcp serve Note: To make the server available across all projects, add --scope user to the command.
Rerun Claude Code in terminal. Enjoy 🥳
Note: On Windows, if you encounter "Connection closed" errors, use the full path to uvx.exe:
claude mcp add --transport stdio windows-mcp -- C:\Users\<user>\.local\bin\uvx.exe windows-mcp serve To verify the server is registered, run claude mcp list. Inside Claude Code, use /mcp to check server status.
WSL (Windows Subsystem for Linux)
If you run Claude Code from WSL, the MCP server must still execute on the Windows side (it needs Windows APIs for UI automation). Use powershell.exe as the command to bridge WSL and Windows:
Install
uvon Windows (from a PowerShell terminal):
irm https://astral.sh/uv/install.ps1 | iexFrom your WSL terminal, register the server:
claude mcp add windows-mcp --transport stdio -s user -- powershell.exe -Command "C:\Users\<user>\.local\bin\uvx.exe windows-mcp serve" Replace <user> with your Windows username. The -s user flag makes the server available across all projects.
Restart Claude Code and verify with
/mcp.
🖥️ Running Windows-MCP
Windows-MCP runs directly on your Windows machine and exposes its tools to the connected MCP client.
# Runs with stdio transport (default)
uvx windows-mcp serve
# Or with SSE/Streamable HTTP for network access
uvx windows-mcp serve --transport sse --host localhost --port 8000
uvx windows-mcp serve --transport streamable-http --host localhost --port 8000Optional environment variables can be set to customize behavior — see Environment Variables below.
Security for Remote Access
For network access, enable authentication and TLS:
windows-mcp serve --transport sse --host 0.0.0.0 \
--auth-key "your_secret_token" \
--ip-allowlist "203.0.113.0/24" \
--ssl-certfile cert.pem --ssl-keyfile key.pemSee 🔐 Security & Access Control for all options.
Transport Options
Transport | Command | Use Case |
|
| Direct connection from MCP clients like Claude Desktop, Cursor, etc. |
|
| Network-accessible via Server-Sent Events |
|
| Network-accessible via HTTP streaming (recommended for production) |
🔐 Security & Access Control
Authentication
windows-mcp serve --transport sse --host 0.0.0.0 --auth-key "your_token"Requires Authorization: Bearer your_token header on all requests.
IP Allowlist
windows-mcp serve --auth-key "token" --ip-allowlist "203.0.113.0/24,198.51.100.5"Restricts connections to specified CIDR ranges. Blocks private/loopback IPs by default.
CORS Origins
By default, no CORS headers are emitted. Browsers block cross-origin requests via their own Same-Origin Policy, which means arbitrary websites cannot reach the MCP control plane even if the server is on localhost. Host-header validation (DNS rebinding protection) is also applied automatically based on the bind address.
If you need a browser-based MCP client to reach the server, opt in with an explicit origin allowlist:
windows-mcp serve --cors-origins "https://my-client.example.com,https://other.example.com"Only the listed origins receive Access-Control-Allow-Origin headers; all other cross-origin requests are rejected by the browser. The equivalent environment variable is WINDOWS_MCP_CORS_ORIGINS.
Tool Selection
All tools are enabled by default. Use --tools to whitelist specific tools, or --exclude-tools to block specific ones.
windows-mcp serve --tools "Screenshot,Click,Snapshot" # Enable only these tools
windows-mcp serve --exclude-tools "PowerShell,Registry" # Disable specific toolsTLS/HTTPS
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
windows-mcp serve --ssl-certfile cert.pem --ssl-keyfile key.pemOAuth 2.0 + PKCE
For MCP clients that use OAuth (e.g. Claude Desktop) instead of a static API key:
windows-mcp serve --transport streamable-http --host 0.0.0.0 \
--ssl-certfile ~/.windows-mcp/cert.pem \
--ssl-keyfile ~/.windows-mcp/key.pem \
--oauth-client-id my-client \
--oauth-client-secret my-secretClaude Desktop config:
{
"mcpServers": {
"windows-mcp": {
"type": "http",
"url": "https://<host>:8000/mcp/",
"oauth": {
"clientId": "my-client",
"clientSecret": "my-secret"
}
}
}
}The OAuth server exposes:
GET /.well-known/oauth-authorization-server— server metadata (RFC 8414)GET /oauth/authorize— Authorization Code + PKCE (S256required)POST /oauth/token— token exchange (client secret required)POST /oauth/register— disabled; clients must be pre-provisioned
Dynamic client registration is disabled. Redirect URIs must be loopback http(s) only.
Auth key and OAuth can coexist — both are accepted as valid Bearer tokens.
Config File (~/.windows-mcp/config.toml)
Instead of passing flags every time, store your configuration in ~/.windows-mcp/config.toml. CLI flags always override config file values.
Search order:
--config /path/to/config.toml~/.windows-mcp/config.toml
stdio — local only, no security needed:
[server]
transport = "stdio"SSE — network access with auth and IP restriction:
[server]
transport = "sse"
host = "0.0.0.0"
port = 8000
auth_key = "your-secret-key"
[security]
ip_allowlist = ["192.168.1.0/24"]Streamable HTTP — with auth, TLS, and tool exclusions:
[server]
transport = "streamable-http"
host = "0.0.0.0"
port = 8000
auth_key = "your-secret-key"
ssl_certfile = "cert.pem" # resolved relative to ~/.windows-mcp/
ssl_keyfile = "key.pem"
[security]
ip_allowlist = ["192.168.1.0/24"]
cors_origins = ["https://my-client.example.com"] # optional — browser CORS opt-in
oauth_client_id = "my-client" # optional — enables OAuth 2.0 + PKCE
oauth_client_secret = "my-secret"
[tools]
exclude = ["PowerShell", "Registry"] # disable specific toolsPlace cert and key files in the same directory:
~/.windows-mcp/
├── config.toml
├── cert.pem
└── key.pemGenerate a self-signed cert directly into that directory:
mkdir -p ~/.windows-mcp
openssl req -x509 -newkey rsa:4096 \
-keyout ~/.windows-mcp/key.pem \
-out ~/.windows-mcp/cert.pem \
-days 365 -nodesauth Helper
Generate an auth key and save a working config to ~/.windows-mcp/config.toml:
windows-mcp authGenerate auth plus a self-signed TLS certificate:
windows-mcp auth --transport streamable-http --host 0.0.0.0 --port 8000 --with-tlsThis command writes the auth key into the config file, can generate cert.pem and key.pem, and prints an example MCP client configuration for the selected transport.
SSRF Protection
Scrape tool blocks: private IPs, loopback, link-local, credentials-in-URLs, non-HTTP schemes.
⚙️ Environment Variables
All variables are optional unless noted. Set them via the env key in claude_desktop_config.json (or your MCP client's equivalent config).
Screenshot & Snapshot
Variable | Default | Description |
|
| Scale factor applied to screenshots before encoding. Accepts a float in the range |
|
| Screenshot capture backend. Accepted values: |
| (disabled) | Set to |
| (disabled) | Set to |
Security
Variable | Default | Description |
| (none) | Bearer token required on all HTTP requests. Alternative to |
| (none) | Comma-separated list of allowed client IPs or CIDR ranges (e.g., |
| (none) | Comma-separated list of origins permitted to make cross-origin browser requests (e.g., |
| (all enabled) | Comma-separated explicit list of tools to enable (e.g., |
| (none) | Comma-separated list of tools to disable (e.g., |
| (none) | Path to TLS certificate file (.pem) for HTTPS. Must be provided with |
| (none) | Path to TLS private key file (.pem) for HTTPS. Must be provided with |
| (none) | OAuth client ID for HTTP transports. Must be provided with |
| (none) | OAuth client secret for HTTP transports. Must be provided with |
|
| Set to |
Telemetry
Variable | Default | Description |
|
| Set to |
| Project default | Override the PostHog project write key used for anonymous telemetry. Set to an empty string to skip PostHog client initialization. |
|
| Override the PostHog host for anonymous telemetry, such as for a self-hosted PostHog deployment. |
Debug
Variable | Default | Description |
|
| Set to |
WatchDog
Variable | Default | Description |
|
| Set to |
Example claude_desktop_config.json:
Local (no security):
{
"mcpServers": {
"windows-mcp": {
"command": "uvx",
"args": ["windows-mcp", "serve"],
"env": { "WINDOWS_MCP_SCREENSHOT_SCALE": "0.5" }
}
}
}Remote (with auth + IP allowlist + TLS):
{
"mcpServers": {
"windows-mcp": {
"command": "uvx",
"args": ["windows-mcp", "serve", "--transport", "sse", "--host", "0.0.0.0"],
"env": {
"WINDOWS_MCP_AUTH_KEY": "your_token",
"WINDOWS_MCP_IP_ALLOWLIST": "203.0.113.0/24",
"WINDOWS_MCP_SSL_CERTFILE": "/path/to/cert.pem",
"WINDOWS_MCP_SSL_KEYFILE": "/path/to/key.pem"
}
}
}
}🔨MCP Tools
MCP Client can access the following tools to interact with Windows:
Click: Click on the screen at the given coordinates.Type: Type text on an element (optionally clears existing text).Scroll: Scroll vertically or horizontally on the window or specific regions.Move: Move mouse pointer or drag (set drag=True) to coordinates. For deterministic drag, setfrom_loc=[x, y]withdrag=Trueto press at an explicit start point and release atlocin one tool call. Optionaldurationadds bounded intermediate movement.Shortcut: Press keyboard shortcuts (Ctrl+c,Alt+Tab, etc).Wait: Pause for a defined duration.WaitFor: Wait until text, an active window, an element, or a focused element appears by polling UI state inside one tool call.DisplayInventory: Read display layout, work areas, effective DPI, and scale metadata.Screenshot: Fast screenshot-first desktop capture with cursor position, active/open windows, and an image. Skips UI tree extraction for speed and should be the default first call when you mainly need visual context. Supportsdisplay=[0]ordisplay=[0,1]using zero-based active Windows display indices, andregion=[left, top, right, bottom](virtual-desktop pixel coordinates) to capture just that rectangle instead of the whole screen — cheaper on tokens when you already know which area matters.regiontakes precedence overdisplaywhen both are given; an invalid or out-of-bounds region raises an error. After capture, a brief orange-red glowing border is drawn inside the captured area as a visual confirmation (setWINDOWS_MCP_DISABLE_FLASH=1to disable).Snapshot: Full desktop state capture for workflows that need interactive element ids, scrollable regions, oruse_dom=Truebrowser extraction. Supportsuse_vision=Truefor including screenshots,display=[0]ordisplay=[0,1]using zero-based active Windows display indices, andregion=[left, top, right, bottom](virtual-desktop pixel coordinates) to inspect just that rectangle instead of the whole screen;regiontakes precedence overdisplaywhen both are given, and an invalid or out-of-bounds region raises an error.App: Launch an application by Start Menu name or strictly by executable path with separated argv and optional cwd; resize, move, and switch between windows.PowerShell: To execute PowerShell commands.FileSystem: Read, write, copy, move, delete, list, search, and inspect files and directories.Scrape: To scrape the entire webpage for information.MultiSelect: Select multiple items (files, folders, checkboxes) with optional Ctrl key. Uses bulk label-to-coordinate resolution when labels are provided.MultiEdit: Enter text into multiple input fields at specified coordinates. Uses bulk label-to-coordinate resolution when labels are provided.Clipboard: Read or set Windows clipboard content.Process: List running processes or terminate them by PID or name.Notification: Send a Windows toast notification with a title and message.Registry: Read, write, delete, or list Windows Registry values and keys.
🤝 Connect with Us
Stay updated and join our community:
📢 Follow us on X for the latest news and updates
💬 Join our Discord Community
Star History
👥 Contributors
Thanks to all the amazing people who have contributed to Windows-MCP! 🎉
We appreciate every contribution, whether it's code, documentation, bug reports, or feature suggestions. Want to contribute? Check out our Contributing Guidelines!
🔒 Security
Important: Windows-MCP operates with full system access and can perform irreversible operations. Please review our comprehensive security guidelines before deployment.
For detailed security information, including:
Tool-specific risk assessments
Deployment recommendations
Vulnerability reporting procedures
Compliance and auditing guidelines
Please read our Security Policy.
📊 Telemetry
Windows-MCP collects usage data to help improve the MCP server. No personal information, no tool arguments, no outputs are tracked.
To disable telemetry, set ANONYMIZED_TELEMETRY to false in your MCP client configuration:
{
"mcpServers": {
"windows-mcp": {
"command": "uvx",
"args": [
"windows-mcp",
"serve"
],
"env": {
"ANONYMIZED_TELEMETRY": "false"
}
}
}
}See the Environment Variables section for the full list of configurable options.
For detailed information on what data is collected and how it is handled, please refer to the Telemetry and Data Privacy section in our Security Policy.
📝 Limitations
Selecting specific sections of the text in a paragraph, as the MCP is relying on a11y tree. (⌛ Working on it.)
Type-Toolis meant for typing text, not programming in IDE because of it types program as a whole in a file. (⌛ Working on it.)This MCP server can't be used to play video games 🎮.
🪪 License
This project is licensed under the MIT License - see the LICENSE file for details.
🙏 Acknowledgements
Windows-MCP makes use of several excellent open-source projects that power its Windows automation features:
Huge thanks to the maintainers and contributors of these libraries for their outstanding work and open-source spirit.
🤝Contributing
Contributions are welcome! Please see CONTRIBUTING for setup instructions and development guidelines.
Made with ❤️ by CursorTouch
Citation
@software{
author = {CursorTouch},
title = {Windows-MCP: Lightweight open-source project for integrating LLM agents with Windows},
year = {2024},
publisher = {GitHub},
url={https://github.com/CursorTouch/Windows-MCP}
}This server cannot be deployed
Maintenance
Related MCP Connectors
Operate Linux, macOS and Windows from your LLM. Every action runs through an auditable allowlist.
AI-powered web automation. Navigate websites using AI agents for one page or a thousand
AI-powered web automation. Navigate websites using AI agents for one page or a thousand
- mcp-serverOAuthcom.make
Give your AI agents the tools to build, manage, and run automation workflows.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to interact with Windows operating systems through native UI automation, file navigation, application control, and system commands. Provides seamless integration between LLMs and Windows environments for tasks like clicking, typing, launching apps, and capturing desktop state.MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to interact with Windows operating systems by providing tools for UI automation, file navigation, application control, and system operations. Works with any LLM to perform tasks like clicking, typing, launching applications, and executing PowerShell commands through native Windows integration.MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to interact with the Windows operating system, performing tasks such as file navigation, application control, UI interaction, and QA testing.MIT
- FlicenseNot gradedqualityCmaintenanceEnables AI agents to seamlessly integrate with the Windows operating system, performing tasks such as file navigation, application control, UI interaction, and QA testing via the MCP protocol.-