Skip to main content
Glama
Am6er

mcp-pwsh

by Am6er

mcp-pwsh

A persistent, pooled PowerShell MCP server. It gives an MCP client (Claude Desktop, or any Model Context Protocol host) a pool of long-lived, named PowerShell 7 sessions with clean stdin → stdout I/O — like keeping several real consoles open, instead of firing one-shot commands.

Why

A naive "run a PowerShell command" tool spawns a fresh process every call, so state does not survive (cwd, variables, imported modules), and the output comes back wrapped in host noise. mcp-pwsh instead keeps one real pwsh process per named session:

  • Persistent state — cwd, variables, $env:, and imported modules survive between calls within a session.

  • Clean output — a bootstrap loop reads commands with [Console]::In.ReadLine() (no prompt, no echo) and delimits each result with a runtime marker, so you get exactly the command's output plus its exit code — no PS> noise.

  • Named session pool (lanes) — each session is an isolated lane with its own process, state, and lock. Different sessions run independently, so two tasks can work at once without corrupting each other's state.

  • No escaping games — commands are sent base64-encoded over one line, so quoting, newlines, and multi-line scripts just work.

Related MCP server: PowerShell MCP Server

Install

pip install mcp-pwsh

Requires Python 3.9+ and PowerShell 7 (pwsh). On Windows the default pwsh location is auto-detected; otherwise pwsh is looked up on PATH.

Configure in an MCP client

Using the installed console script (recommended):

{
  "mcpServers": {
    "pwsh": {
      "command": "mcp-pwsh"
    }
  }
}

Or via the module (useful to pin a specific interpreter):

{
  "mcpServers": {
    "pwsh": {
      "command": "C:\\path\\to\\python.exe",
      "args": ["-m", "mcp_pwsh"]
    }
  }
}

Tools

Tool

Purpose

pwsh(command, session, timeout=60)

Run a command in the named session; returns clean output + exit code. session is your chat's stable id — reuse it for every call.

pwsh_list()

List live sessions with owner id, pid, busy/idle, idle time, creation time, and last command.

pwsh_close(session)

Kill and remove one session. Call it when your task is done to free the lane.

pwsh_reset(session)

Restart one session's process (state cleared) — use if a command hung.

pwsh_kill_all()

Fallback: remove every session.

Discipline: pick one stable session id per chat, reuse it, and pwsh_close it when finished. Use pwsh_list to see lingering sessions and pwsh_kill_all only as a last resort.

Configuration (environment variables)

Variable

Default

Meaning

MCP_PWSH_EXE

auto-detected pwsh

Path to the PowerShell 7 executable.

MCP_PWSH_CWD

user home directory

Starting working directory for new sessions.

MCP_PWSH_TIMEOUT_SEC

60

Default per-command timeout (seconds).

How it works

Each session launches pwsh running a small bootstrap loop (passed via -EncodedCommand). The loop reads "<id> <base64-command>" lines from stdin, runs the command in a persistent scope, and prints <<<MCPEND:<id>:<exitcode>>>>. Because the full marker is never present in the input, it can never false-match, and because input is read (not typed into an interactive prompt) there is no echo or prompt to strip. The bootstrap also restores a sane PATHEXT/ComSpec in case the host launched the process with a stripped environment, so native executables run normally.

Self-test

Runs a local check of session isolation, listing, close, and kill-all (needs pwsh available; no MCP host required):

python -m mcp_pwsh --selftest

License

GPL-2.0

Related MCP Connectors

Related MCP Servers