Summary
windows-mcp install (v0.8.1, installed via uv tool install windows-mcp) fails for a standard, non-elevated Windows user, and separately, the start-server.cmd wrapper it generates points at a different windows-mcp binary than the one running the install.
Environment
- windows-mcp 0.8.1 (PyPI, installed as a uv tool; Python 3.13.13 in the tool venv)
- Windows, standard user account, non-elevated shell
- A second, older windows-mcp also present on PATH: the Claude Desktop Extension venv (
...\Claude Extensions\ant.dir.cursortouch.windows-mcp\.venv\Scripts\windows-mcp.exe, v0.7.1)
Repro
uv tool install windows-mcp (lands at %USERPROFILE%\.local\bin\windows-mcp.exe, not on PATH by default per uv's own warning)
- From a non-elevated terminal:
windows-mcp install
Result:
Error: schtasks /Create failed:
ERROR: Access is denied.
Bug A — install requires elevation but isn't documented as such
install() in windows_mcp/__main__.py calls:
schtasks /Create /SC ONLOGON /TN windows-mcp-server /TR "<start-server.cmd>" /F
with no /RU. schtasks.exe requires an elevated context to create an ONLOGON-triggered task, so a standard user is denied. The README presents windows-mcp install as a simple per-user task with no mention that an elevated terminal is required.
Proposed fix: register the task through the Task Scheduler API instead of schtasks.exe, scoped to the current user at a Limited run level, which does not require elevation. PowerShell equivalent that succeeds non-elevated:
$action = New-ScheduledTaskAction -Execute "$env:USERPROFILE\.windows-mcp\start-server.cmd"
$trigger = New-ScheduledTaskTrigger -AtLogOn -User $env:USERNAME
$set = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
Register-ScheduledTask -TaskName 'windows-mcp-server' -Action $action -Trigger $trigger `
-Settings $set -RunLevel Limited -Force
Alternatively, detect non-elevation and either self-elevate or print a clear "run from an elevated terminal" message rather than the raw Access is denied.
Bug B — _resolve_program() picks the wrong binary
_resolve_program() resolves the server command via shutil.which("windows-mcp") and only skips paths containing uv\tools / .cache\uv:
windows_mcp = shutil.which("windows-mcp")
if windows_mcp and not any(m in windows_mcp for m in (".cache\\uv", ".cache/uv", "uv\\tools", "uv/tools")):
return [windows_mcp]
When windows-mcp is installed as a uv tool, the executable shim lives in %USERPROFILE%\.local\bin, which is frequently not on PATH. So which("windows-mcp") resolves to whatever other windows-mcp is on PATH — in my case the Claude Desktop Extension's v0.7.1 build, which has no serve subcommand. The generated wrapper is therefore:
"...\Claude Extensions\...\.venv\Scripts\windows-mcp.EXE" serve --transport streamable-http --host 127.0.0.1 --port 8000 ...
Running v0.7.1 with serve errors with Got unexpected extra argument (serve), so the background server would never start even if the task were created.
Proposed fix: build the wrapper command from the interpreter/entry point actually running install (e.g. sys.executable -m windows_mcp ..., or the resolved path of the running console script) rather than re-resolving an unrelated copy via PATH.
Net effect
On a standard user with the Claude Desktop Extension also installed, windows-mcp install cannot succeed (Bug A), and if the schtasks call were elevated it would still register a task that fails at runtime (Bug B).
Summary
windows-mcp install(v0.8.1, installed viauv tool install windows-mcp) fails for a standard, non-elevated Windows user, and separately, thestart-server.cmdwrapper it generates points at a differentwindows-mcpbinary than the one running the install.Environment
...\Claude Extensions\ant.dir.cursortouch.windows-mcp\.venv\Scripts\windows-mcp.exe, v0.7.1)Repro
uv tool install windows-mcp(lands at%USERPROFILE%\.local\bin\windows-mcp.exe, not on PATH by default per uv's own warning)windows-mcp installResult:
Bug A — install requires elevation but isn't documented as such
install()inwindows_mcp/__main__.pycalls:with no
/RU.schtasks.exerequires an elevated context to create anONLOGON-triggered task, so a standard user is denied. The README presentswindows-mcp installas a simple per-user task with no mention that an elevated terminal is required.Proposed fix: register the task through the Task Scheduler API instead of
schtasks.exe, scoped to the current user at aLimitedrun level, which does not require elevation. PowerShell equivalent that succeeds non-elevated:Alternatively, detect non-elevation and either self-elevate or print a clear "run from an elevated terminal" message rather than the raw
Access is denied.Bug B —
_resolve_program()picks the wrong binary_resolve_program()resolves the server command viashutil.which("windows-mcp")and only skips paths containinguv\tools/.cache\uv:When windows-mcp is installed as a uv tool, the executable shim lives in
%USERPROFILE%\.local\bin, which is frequently not on PATH. Sowhich("windows-mcp")resolves to whatever otherwindows-mcpis on PATH — in my case the Claude Desktop Extension's v0.7.1 build, which has noservesubcommand. The generated wrapper is therefore:Running v0.7.1 with
serveerrors withGot unexpected extra argument (serve), so the background server would never start even if the task were created.Proposed fix: build the wrapper command from the interpreter/entry point actually running
install(e.g.sys.executable -m windows_mcp ..., or the resolved path of the running console script) rather than re-resolving an unrelated copy via PATH.Net effect
On a standard user with the Claude Desktop Extension also installed,
windows-mcp installcannot succeed (Bug A), and if the schtasks call were elevated it would still register a task that fails at runtime (Bug B).