[BUG] Windows: uv-based (`uvx`) MCP servers fail to start — child `uv` inherits a handle into its own build temp dir, so the build can't clean up (`os error 32`); identical command succeeds when spawned outside Claude's process tree

Open 💬 0 comments Opened Jun 16, 2026 by ArkTarusov

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Any MCP server that builds its environment at launch via uvx/uv (e.g. uvx mcp-atlassian) fails to start on Windows when spawned by Claude. The uv build aborts while removing a wheel's temporary .data directory with os error 32 ("being used by another process"); the build never finishes, the server process exits, and the MCP connection closes — so the server's tools never register.

The decisive finding: the exact same command succeeds when it is NOT a child of Claude's process tree. Running it detached via WMI, or in a normal terminal, or via Cursor/Codex (which use the identical uvx mcp-atlassian) — all succeed. So this is not a uv bug, not antivirus, and not a config issue: it is specific to how Claude spawns child processes on Windows. It appears to leave handles inheritable, so a descendant uv process inherits an open handle into the very temp build directory it then tries to delete.

This blocks every uv-bootstrapped Python MCP server on Windows (also seen in #38266 with ipykernel/pywin32).

What Should Happen?

uvx mcp-atlassian should build its environment and the server should connect — exactly as it does in a normal terminal and in Cursor/Codex with the identical command.

Error Messages/Logs

# From the MCP server log:
# %LOCALAPPDATA%\claude-cli-nodejs\Cache\<project>\mcp-logs-plugin-atlassian-mcp-atlassian\*.jsonl

Starting connection with timeout of 30000ms
Server stderr: error: Failed to install: jmespath-1.1.0-py3-none-any.whl (jmespath==1.1.0)
  Caused by: failed to remove directory `C:\Users\<user>\AppData\Local\uv\cache\builds-v0\.tmpXXXXXX\Lib\site-packages\jmespath-1.1.0.data`: The process cannot access the file because it is being used by another process. (os error 32)
Connection failed after 2501ms: MCP error -32000: Connection closed
Connection failed: MCP error -32000: Connection closed

Steps to Reproduce

  1. On Windows, configure any uv-bootstrapped Python MCP server, e.g. in the plugin/MCP config:

``json
{ "mcpServers": { "mcp-atlassian": { "command": "uvx", "args": ["mcp-atlassian"] } } }
``

  1. Ensure the uv environment is not already cached (cold build) — e.g. after uv has pruned its cache, or run uv cache clean mcp-atlassian / delete %LOCALAPPDATA%\uv\cache\environments-v2.
  2. Start Claude. The server fails to connect; tools never appear.
  3. Check the server log under %LOCALAPPDATA%\claude-cli-nodejs\Cache\<project>\mcp-logs-*\*.jsonl — it shows the os error 32 removal failure followed by MCP error -32000: Connection closed.
  4. Contrast (proves the cause): run the identical command yourself and it succeeds:

```powershell
# In a normal terminal -> SUCCESS ("Installed 117 packages ... mcp-atlassian, version 0.21.1")
uvx mcp-atlassian --version

# Detached from Claude's process tree via WMI -> SUCCESS
Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{ CommandLine = 'cmd /c "uvx mcp-atlassian --version > out.txt 2> err.txt"' }
``
The same command spawned directly by Claude's shell/harness -> FAILS with
os error 32`.

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.178 (Claude Code) - Claude for Windows app 1.12603.1

Platform

Anthropic API

Operating System

Windows

Terminal/Shell

Other

Additional Information

Diagnostic evidence

1. Decisive experiment — detach from Claude's process tree and it works.
The identical uvx mcp-atlassian --version:

  • spawned directly by Claude's shell/harness → FAILS (os error 32), every time;
  • spawned detached via WMI (Invoke-CimMethod Win32_Process Create, outside the caller's handle-inheritance chain) → SUCCEEDS (Installed 117 packages in ~650ms ... mcp-atlassian, version 0.21.1);
  • run in a normal terminal → SUCCEEDS (Installed 117 packages in ~900ms).

2. Ruled out by experiment (none is the cause):

| Suspect | Test | Result |
|---|---|---|
| Windows Defender | uv cache already excluded (Add-MpPreference -ExclusionPath %LOCALAPPDATA%\uv\cache) | still fails |
| uv version | upgraded 0.7.5 → 0.11.21 (latest) | no change |
| Claude shell sandbox | ran with sandbox bypassed | still fails |
| Concurrent uv process | sampled Win32_Process for uv.exe/uvx.exe during the failing run | only the run's own 2 PIDs (uvx + its uv child); no foreign uv |
| Env vars / cache dir | TEMP/TMP/UV_* normal; default cache path | not a factor |

3. The lock is transient and held by a non-enumerable process.
After the failure the leftover temp .data directory is deletable manually ~1 s later (Remove-Item succeeds), and Restart Manager (rstrtmgr) cannot enumerate the holder — consistent with a handle held briefly by another process in Claude's own tree during the build, then released. The system has only Windows Defender (no third-party EDR).

Root cause (hypothesis)

When Claude spawns a child process on Windows, handles appear to be created inheritable. A descendant uv build worker inherits an open handle into its temp build directory; when uv removes a wheel's temp .data dir, the removal fails with sharing violation os error 32 because another process in Claude's tree still holds the inherited handle. Outside that tree (WMI-spawned / normal terminal) there is no such handle, so the build succeeds. This likely compounds with the double-spawn behavior in #53134 (two managers each launching the same server → a second concurrent uv build into the same target).

Why Cursor / Codex are unaffected

They run the identical uvx mcp-atlassian, but their MCP processes are not children of Claude's process tree, so no Claude-owned inherited handle blocks uv's cleanup.

Workaround

Avoid building at launch — point the server at a pre-built binary so uv does no temp-dir create/delete when Claude spawns it:

# Run ONCE in a normal terminal (NOT via Claude), where the build succeeds:
uv tool install mcp-atlassian
{ "command": "mcp-atlassian", "args": [] }   // resolved via the ~/.local/bin shim on PATH

(If the bare command doesn't resolve in Claude's spawn environment, use the absolute shim path, e.g. C:\Users\<user>\.local\bin\mcp-atlassian.exe — cf. the "uv not on PATH" note in #38266.) Matches the accepted workaround in #38266 (pre-build the env before Claude launches it).

Suggested fix

Note: the suggestions below were drafted by an AI assistant (Claude) during diagnosis, not by the reporter — starting points for maintainers, not a verified solution.
  • When spawning MCP server child processes on Windows, create them without inheritable handles (mark Claude-owned handles non-inheritable / O_CLOEXEC-equivalent) so a descendant uv/build process can't inherit a lock into its own working directory.
  • Don't send the MCP initialize handshake until the server signals readiness, and ensure a single manager owns each server spawn (see #53134), to avoid racing/duplicating the uv build.

Related issues

  • #38266 — Windows: Python-based MCP extensions fail to connect due to uv venv build race condition (same os error 32; closed as "invalid", not fixed)
  • #53134 — Windows: MCP servers spawned twice at startup (directMcpHost + LocalMcpServerManager) (open; double-spawn regression ~2 Apr 2026)

View original on GitHub ↗