Description
Every invocation of the PowerShell tool briefly opens a visible console window on the host
machine. The window takes keyboard focus, which interrupts whatever the user is typing at that
moment. With an agent session running many shell calls in a row, this makes the machine
effectively unusable for anything else.
Steps to Reproduce
- Install Windows-MCP as an extension in Claude Desktop on Windows and let it connect.
- Start any session that uses the
PowerShell tool, and have the agent run a sequence of shell
calls (e.g. a few file listings in a row — anything that triggers several calls).
- While those calls are running, keep typing in a different application (editor, browser, chat).
Expected Behavior
The shell command runs in the background. The user keeps working; keyboard focus stays in the
application they are typing in.
Actual Behavior
For every tool call a console window is created for a fraction of a second and pulls focus away.
Keystrokes typed at that moment go to the transient console and are lost. With an agent making
many calls, focus is stolen repeatedly.
Environment
- Windows-MCP v0.7.2 (Claude Desktop extension, MSIX install)
- Windows 11, host used interactively while the MCP server works in the background
- Python: interpreter bundled with the extension venv
- MCP client: Claude Desktop
- Also verified against
main (v0.8.5) — see "Still present upstream" below
Root cause
In the process helper run_with_graceful_timeout, the child process is created with
CREATE_NEW_PROCESS_GROUP but without CREATE_NO_WINDOW:
src/windows_mcp/powershell/utils.py (v0.8.5; in v0.7.2 the same code lives in
src/windows_mcp/desktop/utils.py):
# Windows graceful-stop prerequisite: CREATE_NEW_PROCESS_GROUP is required
# so that send_signal(CTRL_BREAK_EVENT) targets the child process group
# rather than the current process (which would cause it to exit).
creationflags = kwargs.get("creationflags", 0)
creationflags |= subprocess.CREATE_NEW_PROCESS_GROUP
kwargs["creationflags"] = creationflags
When the parent process has no console of its own (which is the case for the extension host),
starting a console application without CREATE_NO_WINDOW makes Windows allocate a new console
for the child — hence the flashing window and the focus steal. capture_output=True redirects the
streams but does not suppress console allocation.
The two taskkill fallbacks in the same file have the same problem (they flash on the timeout
path).
Still present upstream
CREATE_NO_WINDOW does not appear anywhere in the repository — verified on a fresh clone of
main (v0.8.5):
$ grep -rn "CREATE_NO_WINDOW" --include=*.py . | wc -l
0
$ grep -rn "CREATE_NEW_PROCESS_GROUP" --include=*.py .
./src/windows_mcp/powershell/utils.py:96: # Windows graceful-stop prerequisite: ...
./src/windows_mcp/powershell/utils.py:100: creationflags |= subprocess.CREATE_NEW_PROCESS_GROUP
(The second grep is a control pattern: it matches, so the zero above reflects the code, not a bad
search expression.)
Suggested fix
Three lines, one file. CREATE_NO_WINDOW composes with CREATE_NEW_PROCESS_GROUP — the child
still gets its own process group, so CTRL_BREAK_EVENT and the graceful-stop path keep working;
only the console window stops being drawn.
creationflags = kwargs.get("creationflags", 0)
- creationflags |= subprocess.CREATE_NEW_PROCESS_GROUP
+ creationflags |= subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.CREATE_NO_WINDOW
kwargs["creationflags"] = creationflags
and, in both taskkill fallbacks:
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
check=False,
+ creationflags=subprocess.CREATE_NO_WINDOW,
)
Verification done locally
Applied the patch to v0.7.2 and confirmed:
- the file still compiles with the extension's own venv interpreter (
python -m py_compile, exit 0)
CREATE_NO_WINDOW occurrences: 3 (1 × Popen, 2 × taskkill)
- diff is exactly 4 changed lines, nothing else touched
- shell calls, captured stdout, exit codes and timeout handling behave as before
Happy to open a PR if that helps.
Description
Every invocation of the
PowerShelltool briefly opens a visible console window on the hostmachine. The window takes keyboard focus, which interrupts whatever the user is typing at that
moment. With an agent session running many shell calls in a row, this makes the machine
effectively unusable for anything else.
Steps to Reproduce
PowerShelltool, and have the agent run a sequence of shellcalls (e.g. a few file listings in a row — anything that triggers several calls).
Expected Behavior
The shell command runs in the background. The user keeps working; keyboard focus stays in the
application they are typing in.
Actual Behavior
For every tool call a console window is created for a fraction of a second and pulls focus away.
Keystrokes typed at that moment go to the transient console and are lost. With an agent making
many calls, focus is stolen repeatedly.
Environment
main(v0.8.5) — see "Still present upstream" belowRoot cause
In the process helper
run_with_graceful_timeout, the child process is created withCREATE_NEW_PROCESS_GROUPbut withoutCREATE_NO_WINDOW:src/windows_mcp/powershell/utils.py(v0.8.5; in v0.7.2 the same code lives insrc/windows_mcp/desktop/utils.py):When the parent process has no console of its own (which is the case for the extension host),
starting a console application without
CREATE_NO_WINDOWmakes Windows allocate a new consolefor the child — hence the flashing window and the focus steal.
capture_output=Trueredirects thestreams but does not suppress console allocation.
The two
taskkillfallbacks in the same file have the same problem (they flash on the timeoutpath).
Still present upstream
CREATE_NO_WINDOWdoes not appear anywhere in the repository — verified on a fresh clone ofmain(v0.8.5):(The second grep is a control pattern: it matches, so the zero above reflects the code, not a bad
search expression.)
Suggested fix
Three lines, one file.
CREATE_NO_WINDOWcomposes withCREATE_NEW_PROCESS_GROUP— the childstill gets its own process group, so
CTRL_BREAK_EVENTand the graceful-stop path keep working;only the console window stops being drawn.
creationflags = kwargs.get("creationflags", 0) - creationflags |= subprocess.CREATE_NEW_PROCESS_GROUP + creationflags |= subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.CREATE_NO_WINDOW kwargs["creationflags"] = creationflagsand, in both
taskkillfallbacks:stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False, + creationflags=subprocess.CREATE_NO_WINDOW, )Verification done locally
Applied the patch to v0.7.2 and confirmed:
python -m py_compile, exit 0)CREATE_NO_WINDOWoccurrences: 3 (1 × Popen, 2 × taskkill)Happy to open a PR if that helps.