Skip to content

Windows MCP SSH stdio transport closes after initialize when PROGRAMDATA is missing from inherited environment #3335

Description

@AbarnaaSree

Description

On Windows, an MCP server launched through the stdio client can terminate shortly after the initialize message when the spawned process is given the SDK's restricted inherited environment.

During investigation of Windows MCP + SSH behavior, I found that PROGRAMDATA is missing from DEFAULT_INHERITED_ENV_VARS in src/mcp/client/stdio.py.

Environment

  • Windows 11
  • MCP Python SDK
  • MCP server launched through stdio
  • SSH-based MCP server

Observed behavior

The MCP SSH subprocess can close shortly after the initialize message with a transport-closed error.

The same SSH setup works when launched manually and behaves differently when the expected Windows environment is available.

Investigation

The stdio client constructs a restricted environment for spawned processes.

PROGRAMDATA was not included in the Windows inherited environment list:

DEFAULT_INHERITED_ENV_VARS = (
    [
        "APPDATA",
        "HOMEDRIVE",
        "HOMEPATH",
        "LOCALAPPDATA",
        "PATH",
        "PATHEXT",
        "PROCESSOR_ARCHITECTURE",
        "SYSTEMDRIVE",
        "SYSTEMROOT",
        "TEMP",
        "USERNAME",
        "USERPROFILE",
    ]
    if sys.platform == "win32"
    else ...
)

Adding PROGRAMDATA allows the spawned Windows process to receive this standard environment variable.

Proposed fix

Add:

"PROGRAMDATA",

to the Windows DEFAULT_INHERITED_ENV_VARS list in:

src/mcp/client/stdio.py

Testing

I reproduced the Windows MCP + SSH behavior and investigated the restricted subprocess environment.

After adding PROGRAMDATA:

  • Verified that PROGRAMDATA is inherited by the spawned process.
  • Tested the restricted environment with Popen.
  • Ran the stdio client tests successfully: 25 passed, 8 skipped.
  • git diff --check passes.

I have prepared PR #3334 with this change.

Related issue

This investigation was motivated by MCP SSH transport closing immediately after initialize on Windows.

I would appreciate maintainer confirmation that this is the appropriate fix and whether this issue can be assigned to me so PR #3334 can be reopened.

Activity

  1. AbarnaaSree commented on Aug 19, 2026

    @AbarnaaSree
    Author

    I’d like to work on this issue. I’ve already prepared PR #3334 with the proposed fix. Could you please assign this issue to me so the PR can be reopened?

  2. added
    v2Affects the v2 line (2.x on main)
    v1Affects the v1.x maintenance line
    on Aug 19, 2026
  3. ohad6k commented on Sep 25, 2026

    @ohad6k

    I reproduced this on Windows 11 with Python 3.11 against main (f1b6589). The spawned server is missing six variables the parent has: COMSPEC, PROGRAMDATA, PROGRAMFILES, PROGRAMFILES(X86), PROGRAMW6432 and WINDIR. The TypeScript SDK added the same six in typescript-sdk#2043 on September 23, so the two SDKs now differ.

    The same line has a second Windows problem. With env={"Path": ...}, the spelling Windows itself uses, the child gets both PATH and Path in its environment block. A server started as python.exe directly still sees the override. A server started with uv run, a Node server, and cmd.exe all read the inherited PATH, and under uv run the pinned directory is gone from the server's PATH completely. So a pinned toolchain is silently ignored. I reported the TypeScript version in typescript-sdk#2859, and the fix is open as typescript-sdk#2874.

    I have a fix: add the six names, and on Windows drop an inherited key when an override matches it case-insensitively. POSIX is unchanged. It has three tests that go through stdio_client using the existing fake spawn seam. Two fail on main and pass with the change, and the third pins the POSIX behavior. tests/client/test_stdio.py passes on Windows. @AbarnaaSree found this, fixed PROGRAMDATA in #3334 (closed only for a missing issue link), and asked to be assigned above. If they still want it, I'm happy to hand over the repro and the extra five names. Otherwise I can build on their change with them as co-author on the commit, if you'd like to assign it to me. I used Claude Code to write the repro and the fix, and every result above comes from runs on my Windows machine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingv1Affects the v1.x maintenance linev2Affects the v2 line (2.x on main)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions