Skip to main content
AC
Agent Ops & Meta5.1 KBMIT licensed

windows-exec

Original, written for TechTide client work

Run Python scripts and PowerShell reliably through a Windows MCP that enforces a hard 60-second timeout: detached processes, file-based output, polling, and killing hung processes. Use before any Windows command that could exceed 10 seconds, or when a previous command timed out or hung. Not for Linux/macOS shells or for Gumroad UI flows (use gumroad-browser).

  • windows
  • exec

SKILL.md

Windows Execution Patterns

The Windows MCP PowerShell tool kills any call at 60 seconds, no exceptions, and leaves hung processes behind. The rule that fixes everything: detach the process and communicate via files, not stdout.

Paths below use <WORK_DIR> for your working directory on the Windows box (e.g. $env:USERPROFILE\work). Set it once and stay inside it.

What times out (all of these)

  • python script.py when the script or interpreter startup runs long
  • Start-Process -Wait and Start-Process -RedirectStandardOutput (both block)
  • Invoke-WebRequest to a slow host with no -TimeoutSec
  • Get-Process | Stop-Process against an already-hung process
  • Start-Sleep -Seconds 90

Pattern 1: long-running Python (primary)

  1. Write the script to a .py file via the filesystem MCP. Never long inline -c "..." strings.
  2. Delete the old output file first, so its presence is a trustworthy done-signal:
$out = "<WORK_DIR>\script_output.json"
if (Test-Path $out) { Remove-Item $out }
  1. Launch detached:
$proc = [System.Diagnostics.Process]::new()
$proc.StartInfo.FileName = "python"
$proc.StartInfo.Arguments = "<WORK_DIR>\my_script.py"
$proc.StartInfo.WindowStyle = "Minimized"
$proc.StartInfo.UseShellExecute = $true   # CRITICAL: redirecting stdout forces a blocking wait
$proc.Start() | Out-Null
Write-Host "PID: $($proc.Id)"
  1. The script writes its own results to a file, including errors:
import json
results = {}
try:
    # work
    results["status"] = "ok"
except Exception as e:
    results = {"status": "error", "error": str(e)}
with open(r"<WORK_DIR>\script_output.json", "w") as f:
    json.dump(results, f, indent=2)
  1. Poll with separate short calls, each under the 60s window:
Start-Sleep -Seconds 30
if (Test-Path "<WORK_DIR>\script_output.json") { Get-Content "<WORK_DIR>\script_output.json" }
else { Get-Process python -EA SilentlyContinue | Select-Object Id, CPU }

Repeat the poll as needed. Keep each sleep at 45s or less.

Pattern 2: quick commands (under 30s expected)

Run directly, but every web request gets -TimeoutSec under 20:

Invoke-RestMethod -Uri "<API_URL>" -Headers @{Authorization = "Bearer <TOKEN>"} -TimeoutSec 15

If it might be slow, it is not a Pattern 2 job. Use Pattern 1.

Pattern 3: killing hung processes

Stop-Process can hang on a hung process. taskkill is a direct OS call:

taskkill /F /IM python.exe        # by image name
taskkill /F /PID <PID>            # by PID
taskkill /F /T /PID <PID>         # process tree, last resort

Pattern 4: network I/O in Python

Any HTTP transfer that could exceed 5 seconds runs as a detached Python script (Pattern 1), stdlib http.client, timeout= always set on the connection. Reachability-check the host first with a 5-second HEAD request from PowerShell before committing to an upload.

Verification

After launching a detached process, poll once. Expect either the output file (read it, check status) or a live PID from Get-Process. Neither present means the launch failed silently: re-check the script path and rerun the launch command reading $proc output. When done, Get-Process python should list nothing you started; leftover PIDs get Pattern 3.

Completion checklist

  • [ ] No command issued that can block past 60s
  • [ ] Script written to file, not inline
  • [ ] Old output file deleted before launch
  • [ ] Output file read and status checked
  • [ ] No orphan processes left (verified, not assumed)

Any box unchecked: not done. Fix or say so.

Good vs bad

  • Bad: Start-Process python script.py -RedirectStandardOutput out.txt -Wait ("I need the output"). Blocks on the pipe, times out at 60s, leaves python.exe orphaned.
  • Good: detached launch with UseShellExecute = $true, script writes JSON to <WORK_DIR>, two 30s polls read the file.

Footguns

  • UseShellExecute = $false plus stdout redirection recreates the exact blocking behavior this skill exists to avoid. Keep it $true and use files.
  • An output file left over from the last run makes a still-running script look finished. Always delete before launch.
  • Invoke-WebRequest with no -TimeoutSec blocks on TCP to a dead host and eats the whole 60s window.
  • Browser-MCP file uploads: CDP file_upload may return {"code":-32000,"message":"Not allowed"} for local paths (browser sandbox, no path fixes it). Workaround: navigate to the local file:/// image, screenshot it to get an in-memory imageId, navigate to the target form, and pass the imageId to the browser MCP's upload_image with the file input's ref.

More in Agent Ops & Meta

All skills