You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Windows stdio resolves .ps1 then fails to spawn it #3496
On Windows, get_windows_executable_command() already looks up .ps1 (alongside .cmd / .bat / .exe). The spawn path then passes that path to CreateProcess / anyio.open_process([command, *args]).
Windows cannot execute a .ps1 file directly. The lookup therefore advertises a working command that later fails with WinError 193 (not a valid Win32 application) or FileNotFoundError.
Expected: a command that resolves to something.ps1 is launched through PowerShell (powershell / pwsh -NoProfile -NonInteractive -ExecutionPolicy Bypass -File <script> ...args).
Actual: the SDK finds the .ps1 and 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.which resolves to .ps1).
I used an LLM to help draft this report after reproducing the failure locally.
Example Code
importasynciofrompathlibimportPathfrommcp.client.stdioimportStdioServerParameters, stdio_client# On Windows, CreateProcess cannot run a .ps1 path returned by# get_windows_executable_command().params=StdioServerParameters(command=str(Path(rC:\path\to\server.ps1)))
asyncdefmain():
asyncwithstdio_client(params) asstreams:
print(streams)
asyncio.run(main())
Confirmed on Windows 10 (PowerShell): a .ps1 path cannot be started via CreateProcess / subprocess.run([ps1_path]) 鈥?fails with OSError: [WinError 193] (not a valid Win32 application). That matches the report: get_windows_executable_command() can return a .ps1 from shutil.which, then stdio spawn fails.
Expected shape for a fix: when the resolved command ends with .ps1, launch via pwsh/powershell with -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 / stdio spawn 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_process so a resolved .ps1 command launches through pwsh/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 .ps1 fails exactly as you describe — this is the same call create_windows_process makes:
>>> 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 PATHEXT is .COM;.EXE;.BAT;.CMD;.VBS;... — no .PS1. So shutil.which("server") does not resolve a bare name to a .ps1, and the explicit shutil.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 — which shutil.which returns 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 need PATHEXT customized 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-oss arrives as two intact argv entries), and stdin/stdout piping works. -File is enough; no -Command quoting needed.
pwsh is not guaranteed to exist. This machine has only Windows PowerShell 5.1 (System32\WindowsPowerShell�1.0\powershell.exe), so a launcher should prefer pwsh and fall back, with a clear error naming what it looked for when neither resolves.
Approach I would take: detect .ps1 at the spawn boundary — after resolution, before anyio.open_process — and wrap the argv there, so both the anyio path and the FallbackProcess path 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.
Thanks @dltsum 鈥?your #3497 shape (rewrite argv in create_windows_process through pwsh/powershell -File, covering both anyio and FallbackProcess) matches the WinError 193 path I reproduced. Happy to defer to that PR once assigned; no competing work from me.
The failure here isn't specific to the SDK. CreateProcess can't execute a .ps1 file, so subprocess.Popen([r"C:\path\server.ps1"]) raises WinError 193 in 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 as command, 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 .py or .js script:
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 pwsh and powershell for 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 .ps1 support anywhere. The only mention is that get_windows_executable_command(), an internal helper, includes .ps1 in the list of extensions it probes when looking up a command:
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.
Activity
FOWEPJF255 commentedon 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.dltsum commentedon Sep 13, 2026
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!
chengwudi1 commentedon Sep 13, 2026
Reproduced on Windows 11 (Python 3.12,
main). Direct spawn of a.ps1fails exactly as you describe — this is the same callcreate_windows_processmakes: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.
FOWEPJF255 commentedon Sep 14, 2026
Thanks @dltsum 鈥?your #3497 shape (rewrite argv in
create_windows_processthroughpwsh/powershell -File, covering both anyio and FallbackProcess) matches the WinError 193 path I reproduced. Happy to defer to that PR once assigned; no competing work from me.maxisbey commentedon Sep 16, 2026
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: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
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.
AI Disclaimer