Skip to content

A WSL-Launched MCP Server on Windows Doesn't Inherit Claude Desktop's env Block Unless You Also Set WSLENV

🛑 Problem

An MCP server works fine on macOS with an env block in claude_desktop_config.json supplying its credentials/config — API keys, mailbox credentials, whatever the server needs. The identical env block, on a Windows machine where the same server is launched via wsl.exe (because the server itself is a Linux binary or Python script), fails immediately on startup with an error that looks like the env vars were never set at all — e.g., for a mail server:

Error: InvalidInput("No IMAP accounts configured. Set MAIL_IMAP_<ID>_HOST/USER/PASS.
mail-mcp startup error: missing HOST.")

🔬 Root Cause

Claude Desktop's mcpServers.<name>.env block sets environment variables in the process it directly spawns. On macOS/Linux that spawned process is the MCP server — the vars land exactly where the server reads them.

On Windows, when command is wsl or wsl.exe, Claude Desktop's spawned process is WSL itself, running on the Windows side. wsl.exe then starts a fresh Linux shell/process inside the WSL2 VM to actually run the server — and that Linux process does not automatically inherit environment variables from the Windows-side parent. The env block's variables exist in the Windows process that launched wsl.exe, and get dropped at that boundary.

A machine with no WSL hop (macOS launching the server directly) never hits this, which is why it's easy to assume the config is portable when it isn't.

🩹 Fix

WSL2 has a specific mechanism for this exact case: the WSLENV variable, read by wsl.exe on the Windows side, which tells it which named Windows environment variables to pass through into the Linux environment it starts.

Add WSLENV to the same env block, listing every variable name that needs to cross the boundary, colon-separated:

{
  "mcpServers": {
    "myserver": {
      "command": "wsl",
      "args": ["...", "path/to/server"],
      "env": {
        "MY_VAR_ONE": "...",
        "MY_VAR_TWO": "...",
        "WSLENV": "MY_VAR_ONE:MY_VAR_TWO"
      }
    }
  }
}

WSLENV itself is just a list of names — it doesn't carry values. Each named variable must also actually be present in the same env block (or otherwise set in the Windows environment) for WSL to have something to pass through.

After editing the config, a full Claude Desktop restart is required (system tray → Quit, not just closing the window) — the mcpServers config is only read at process start.

Verify before assuming the restart fixed it: run the server binary directly inside a WSL shell with the same variables set, and confirm it reports discovering them, before trusting a live Claude Desktop session:

MY_VAR_ONE=... MY_VAR_TWO=... /path/to/server --diagnose  # or whatever the server's own startup log shows

🧭 Generalizable

This applies to any MCP server (or any program at all) launched from Windows through wsl/wsl.exe as the command. It does not apply to native-Windows executables launched directly (e.g., a uv.exe-run Python server, or any .exe invoked without a WSL hop) — those inherit the Windows env block normally, with no WSLENV needed. If a Windows machine has a mix of WSL-launched and native-Windows MCP servers in the same config, only the WSL-launched ones need this treatment.

  • 30-Areas/MajorInfrastructure/Devices/MajorRig.md § Incident Log — the original diagnosis, for a mail MCP server