Repository navigation
Windows stdio resolves .ps1 then fails to spawn it #3496
Description
Activity
- addedv2Affects the v2 line (2.x on main)Affects the v2 line (2.x on main)v1Affects the v1.x maintenance lineAffects the v1.x maintenance line
on Sep 13, 2026 Confirmed on Windows 10 (PowerShell): a
.ps1path cannot be started viaCreateProcess/subprocess.run([ps1_path])鈥?fails withOSError: [WinError 193](not a valid Win32 application). That matches the report:get_windows_executable_command()can return a.ps1fromshutil.which, thenstdiospawn fails.Expected shape for a fix: when the resolved command ends with
.ps1, launch viapwsh/powershellwith-NoProfile -NonInteractive -ExecutionPolicy Bypass -File <script>and forward args.AI-assisted note: I reproduced the Win32 spawn failure locally and read
get_windows_executable_command/stdiospawn path; happy to take an assigned PR if maintainers want an outside fix.Hi! I'd like to work on this issue — could you assign it to me?
I have a complete fix ready in PR #3497 (auto-closed per the assignment rule; it should reopen on its own once assigned). The fix rewrites the argv in
create_windows_processso a resolved.ps1command launches throughpwsh/powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File, with a clear FileNotFoundError when no PowerShell host is on PATH. Both the anyio.open_process path and the SelectorEventLoop FallbackProcess path are covered.Verified on Windows 11 with a new integration test (real .ps1 echo server doing a JSON-RPC round trip via stdio_client): fails without the fix with the exact reported WinError 193, passes with it. Thanks!
Reproduced on Windows 11 (Python 3.12,
main). Direct spawn of a.ps1fails exactly as you describe — this is the same callcreate_windows_processmakes:>>> subprocess.Popen([r"...\echo-ok.ps1"]) OSError: [WinError 193] %1 is not a valid Win32 application.One refinement to the trigger, since it decides which code path a fix has to cover: on a default Windows the built-in
PATHEXTis.COM;.EXE;.BAT;.CMD;.VBS;...— no.PS1. Soshutil.which("server")does not resolve a bare name to a.ps1, and the explicitshutil.which(f"{command}.ps1")in the extension loop does not either (it appends PATHEXT again, finds nothing). The case that does resolve is a command that already carries a directory component —C:\path\server.ps1, or./server.ps1— whichshutil.whichreturns as-is via its dirname branch, never reaching the extension list. That is the reproduction I can drive end to end; the bare-name variant in the report would needPATHEXTcustomized to include.PS1.Two things I checked while working out the fix shape:
powershell -NoProfile -NonInteractive -File <script> <args...>forwards arguments correctly, including ones that look like switches (--model gpt-ossarrives as two intact argv entries), and stdin/stdout piping works.-Fileis enough; no-Commandquoting needed.pwshis not guaranteed to exist. This machine has only Windows PowerShell 5.1 (System32\WindowsPowerShell�1.0\powershell.exe), so a launcher should preferpwshand fall back, with a clear error naming what it looked for when neither resolves.
Approach I would take: detect
.ps1at the spawn boundary — after resolution, beforeanyio.open_process— and wrap the argv there, so both the anyio path and theFallbackProcesspath pick it up without touching the resolver's return contract.Disclosure: AI-assisted — I used an agent to probe spawn behaviour on this machine and draft this; I ran the commands above and can explain them. Reporter has first call if they want this one.
The failure here isn't specific to the SDK.
CreateProcesscan't execute a.ps1file, sosubprocess.Popen([r"C:\path\server.ps1"])raisesWinError 193in any Python program, which is what the report's own example shows. The case that reproduces end to end is passing an explicit script path ascommand, and that amounts to asking the OS to execute a file it can't execute.The way to run a PowerShell script as a stdio server is to name the interpreter, the same as for a
.pyor.jsscript:StdioServerParameters( command="powershell", # or "pwsh" args=["-NoProfile", "-NonInteractive", "-File", r"C:\path\to\server.ps1"], )
I don't think the SDK should do that wrapping itself. It would have to pick between
pwshandpowershellfor you and decide on flags like-ExecutionPolicy Bypass, which overrides a security setting on the machine. Those choices belong to whoever configures the server.On the point that the SDK "advertises"
.ps1: it doesn't document or claim.ps1support anywhere. The only mention is thatget_windows_executable_command(), an internal helper, includes.ps1in the list of extensions it probes when looking up a command:python-sdk/src/mcp/os/win32/utilities.py
Line 74 in 6affe5c
for ext in [".cmd", ".bat", ".exe", ".ps1"]: That entry is a leftover from the original Windows stdio work and could never have launched anything. We may drop it, but it isn't a promise to run PowerShell scripts.
Closing as not planned.
A note on the thread itself: several comments here are agent-written confirmations of each other, plus a request to be assigned a PR that had already been opened. That doesn't add signal, and it isn't how issues get assigned here (see CONTRIBUTING.md). If you hit a problem yourself, a report of what you ran and what happened is always welcome. A chorus of "reproduced, happy to take this" on someone else's issue isn't.
Initial Checks
Release line
2.x (current stable)
Description
On Windows,
get_windows_executable_command()already looks up.ps1(alongside.cmd/.bat/.exe). The spawn path then passes that path toCreateProcess/anyio.open_process([command, *args]).Windows cannot execute a
.ps1file directly. The lookup therefore advertises a working command that later fails withWinError 193(not a valid Win32 application) orFileNotFoundError.Expected: a command that resolves to
something.ps1is launched through PowerShell (powershell/pwsh -NoProfile -NonInteractive -ExecutionPolicy Bypass -File <script> ...args).Actual: the SDK finds the
.ps1and then cannot start it.I hit this on Windows while starting a stdio MCP server whose command is a PowerShell script (or a bare name that
shutil.whichresolves to.ps1).I used an LLM to help draft this report after reproducing the failure locally.
Example Code
Direct spawn of the same path also fails:
Launching via PowerShell works:
Python & MCP Python SDK
Python 3.12 on Windows 10/11
MCP Python SDK 2.2.0 (
modelcontextprotocol/python-sdkmain as of this report)