@imrrd/powershell-mcp
v0.5.2
Published
A Model Context Protocol (MCP) server for Windows PowerShell - runs commands in a hidden process (no popup windows), with structured output, timeouts, and service/system management. Built for AI agents that need real Windows access.
Maintainers
Readme
powershell-mcp
A Model Context Protocol server that gives AI agents real, non-intrusive access to Windows PowerShell.
Most tools that let an agent run Windows commands spawn a visible console window for every call — which steals focus and interrupts whatever you're typing. powershell-mcp runs everything in a hidden process (windowsHide: true / no CreateWindow), captures structured output, enforces hard timeouts, and exposes purpose-built tools for service and system management. Built for running unattended next to a human at the keyboard.
Why
- No popup windows. Commands run hidden; your foreground app keeps focus.
- Structured + safe. Every call returns
{ stdout, stderr, exit_code, duration, timed_out }. Hard timeout with tree-kill. Output is capped so a runaway command can't flood the context. - Real Windows management. First-class tools for services and system info, not just a raw shell — handy for managing Windows servers and backup systems.
- Cross-shell. Prefers
pwsh(PowerShell 7+) and falls back topowershell.exe; override withPWSH_MCP_EXE.
Tools
| Tool | Description |
|------|-------------|
| run_powershell | Run any PowerShell script/command (hidden). { script, cwd?, timeoutMs? } |
| run_program | Run a native executable directly (no shell) and capture clean stdout/stderr + exit code - for gh/git/docker/node and other console binaries whose output a hidden shell swallows. { program, args?, cwd?, timeoutMs? } |
| list_services | List services, optional filter wildcard. |
| get_service | Detailed status of one service by name. |
| control_service | start / stop / restart / status a service. |
| system_info | OS, CPU, memory, and per-drive disk summary. |
| ssh_exec | Run a command on a remote host over SSH, fully in-process (no ssh.exe, no WSL — works headless). { host, username, command, port?, privateKeyPath?, passphrase?, password?, timeoutMs? } |
| winrm_exec | Run a command on a remote Windows host via PowerShell Remoting (WinRM / Invoke-Command). No SSH server or agent needed on the target. { computerName, command, username?, password?, useSsl?, authentication?, timeoutMs? } |
| sftp_upload | Upload a local file to a remote host over SFTP, in-process (ssh2 — no scp.exe/WSL, headless). { localPath, remotePath, host, username, port?, privateKeyPath?, passphrase?, password?, timeoutMs? } |
| sftp_download | Download a remote file to this host over SFTP, in-process. Same params as sftp_upload. |
Native programs: Windows PowerShell routes a native command's stdout to the console, so run hidden it is lost. Use
run_program(direct-exec) for console binaries likegh/git/docker; userun_powershellfor PowerShell/cmdlet logic.
See it work
Real calls, real output — headless, no console window, structured results:
# ssh_exec — run a command on a Linux box, in-process (no ssh.exe, no WSL)
> ssh_exec host=192.168.0.5 username=isak command="uptime; systemctl is-active app"
$ ssh [email protected] (exit=0, 818ms)
2 days, 23:53, load average: 0.00, 0.01, 0.04
active
# sftp_upload — deploy a file, in-process (no scp.exe)
> sftp_upload localPath=C:\deploy\app.py remotePath=/home/isak/app.py host=192.168.0.5 ...
sftp upload: C:\deploy\app.py → [email protected]:/home/isak/app.py
OK (9129 bytes, 714ms)Remote operations
powershell-mcp manages more than the local box. Windows' own ssh.exe produces no capturable output when run from a windowless/background process, and shipping WSL to every server doesn't scale — so remote exec is built in:
ssh_execuses the pure-JSssh2client (no external binary), so it works headless and needs nothing on the target beyond an SSH server. Ideal for Linux hosts.winrm_execuses native PowerShell Remoting, so a Windows fleet needs only WinRM enabled — no per-server install.
Telemetry (anonymous, opt-out)
On startup the server sends a one-time ping (host id, version, OS, timestamp) and flushes per-tool call counts every 30 minutes (and on exit). This helps prioritise which tools matter. No command content, arguments, output, or paths are ever sent.
- Disable entirely: set
POWERSHELL_MCP_NO_TELEMETRY=1. - Override the endpoint: set
POWERSHELL_MCP_TELEMETRY_URL.
The collector under collector/ is a standalone Node.js service (JSONL append, systemd unit, nginx snippet, deploy.sh) deployed separately.
Install
The canonical npm package is @imrrd/powershell-mcp.
Run it directly from npm in an MCP client:
{
"mcpServers": {
"powershell": {
"command": "npx",
"args": ["-y", "@imrrd/powershell-mcp@latest"]
}
}
}Or install the CLI globally:
npm install -g @imrrd/powershell-mcpMigrating:
powershell-mcpis deprecated. Replace it with@imrrd/powershell-mcp; both names refer to this repository, but only the scoped package receives updates.
For development from source:
npm install
npm run buildSee examples/claude_desktop_config.json for the canonical package-based client configuration.
Develop
npm run dev # run from source (tsx)
npm test # unit + (where a shell is present) integration tests
npm run typecheckCI runs build + tests on both windows-latest and ubuntu-latest.
Security notes
control_serviceand many commands require the MCP host process to run with sufficient privileges.- The server runs whatever script it's given — run it only in environments you trust, behind a host (like Claude) that you control. A future release will add an optional allow/deny policy and confirmation gating.
License
MIT © IMR Research & Development (UK)
