Skip to content

windows-mcp install fails for standard (non-elevated) users, and the generated wrapper resolves to the wrong windows-mcp binary #265

@NextLevelManagementAdvisors

Description

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

  1. uv tool install windows-mcp (lands at %USERPROFILE%\.local\bin\windows-mcp.exe, not on PATH by default per uv's own warning)
  2. 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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions