Skip to content

[windows-mcp] Scheduled task auto-start fails (0xC0000005) when uvx installed via WinGet symlink; InteractiveToken logon trigger cannot resolve symlink before shell initialization #4488

Description

@Chris-CGG

Environment

  • OS: Windows 11 (domain-joined, service account manifold.svc)
  • Claude Desktop: latest (2026)
  • windows-mcp version: installed via WinGet (astral-sh.uv)
  • Deployment pattern: MCP server running as Windows Scheduled Task (logon trigger, InteractiveToken)

Description

When an MCP server is installed via WinGet and launched from a Windows Scheduled Task, the server crashes on auto-start with exit code 0xC0000005 (access violation). The root cause is that WinGet installs binaries as symbolic links under AppData\Local\Microsoft\WinGet\Links\, and Windows Scheduled Tasks with InteractiveToken logon type cannot follow these symlinks before the user shell is fully initialized.

The server works correctly when started manually (Start-ScheduledTask) or launched directly by Claude Desktop — only the at-logon scheduled task trigger fails.

Symptoms

  • Scheduled task fires at logon (Event ID 119 — task launched due to user logon)
  • Task exits immediately with LastTaskResult: 3221225477 (0xC0000005)
  • Port never becomes available
  • Claude Desktop shows the connector as registered but tools never load in new sessions

Root Cause

WinGet installs uvx.EXE as a zero-byte symbolic link:

C:\Users\<user>\AppData\Local\Microsoft\WinGet\Links\uvx.EXE
  → C:\Users\<user>\AppData\Local\Microsoft\WinGet\Packages\astral-sh.uv_Microsoft.Winget.Source_8wekyb3d8bbwe\uvx.exe

The start-server.cmd generated by windows-mcp install points to the symlink path. When Task Scheduler fires the InteractiveToken logon trigger, the user's shell environment is not yet fully initialized, and symlink resolution fails — producing an access violation in the spawned process.

Workaround

Replace the symlink path in start-server.cmd with the real executable path under WinGet\Packages\:

@echo off
setlocal

:: Guard: exit cleanly if another instance is already running on port 8000
netstat -ano | findstr "127.0.0.1:8000" >nul 2>&1
if %ERRORLEVEL% EQU 0 (
    echo windows-mcp already running on port 8000, exiting. >> "%USERPROFILE%\.windows-mcp\server.log"
    exit /b 0
)

:: Use real path, not WinGet symlink - symlinks fail in scheduled task InteractiveToken context
"C:\Users\<user>\AppData\Local\Microsoft\WinGet\Packages\astral-sh.uv_Microsoft.Winget.Source_8wekyb3d8bbwe\uvx.exe" windows-mcp serve --transport streamable-http --host 127.0.0.1 --port 8000 1>>"%USERPROFILE%\.windows-mcp\server.log" 2>>"%USERPROFILE%\.windows-mcp\server.error.log"

The port guard is also necessary because Claude Desktop may launch its own windows-mcp Extension process on the same port before the scheduled task fires — without the guard, both processes race for port 8000 and the task crashes.

Suggested Fix

windows-mcp install (or the equivalent setup command) should:

  1. Resolve the real executable path at install time (e.g., via (Get-Item $symlinkPath).Target) and write the resolved path into start-server.cmd rather than the symlink path.
  2. Include a port-availability guard in the generated start-server.cmd to handle the case where Claude Desktop's built-in Extension already owns the port.

Additional Notes

  • The issue is reproducible on domain-joined Windows 11 with service accounts where logon sessions may not fully initialize the shell environment before scheduled task triggers fire.
  • Task Scheduler Event ID 201 with result 0xC0000005 is the diagnostic signal — not a permissions issue, not a battery/power policy issue.
  • The same pattern likely affects any MCP server installed via WinGet and launched from a scheduled task.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    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