Skip to main content
Glama
mhprol

win-cli-mcp-server

by mhprol
README.md
# win-cli-mcp-server

Hardened [MCP server](https://modelcontextprotocol.io/) for Windows CLI and SSH interactions. Provides controlled access to **PowerShell**, **CMD**, **Git Bash**, and **remote systems via SSH** from MCP clients like [Claude Desktop](https://claude.ai/download).

## Fork Lineage

This is a maintained, hardened fork:

**[SimonB97/win-cli-mcp-server](https://github.com/SimonB97/win-cli-mcp-server)** (original, development stalled)
-> **[delorenj/super-win-cli-mcp-server](https://github.com/delorenj/super-win-cli-mcp-server)** (super-win-cli variant)
-> **this repo** (bug fixes, hardening, dependency updates)

The original project is no longer actively maintained. This fork fixes critical bugs, updates dependencies, and is used in production daily.

## What This Fork Fixes

### Critical

- **GUI window popups** -- Added `windowsHide: true` to `child_process.spawn()`. Shell windows no longer flash on screen during MCP execution.
- **SSH event listener leak** -- Reconnection cycles accumulated duplicate handlers on the ssh2 `Client` instance, causing memory leaks. Fixed by creating a fresh `Client` on each reconnect and using `.once()` for connection-scoped events.
- **SSH stderr silently dropped** -- When stdout had content, stderr was discarded (`output || errorOutput`). Now both streams are combined.
- **Silent config fallback on BOM** -- UTF-8 BOM in `config.json` caused `JSON.parse()` to throw, silently falling back to restrictive defaults. BOM is now stripped before parsing.

### High

- **Dead dependency removed** -- `@modelcontextprotocol/server-memory-dynamic` pointed to `file:../servers/src/memory` (author's local dev path). Removed.
- **SSH agent auth support** -- Config validation required `password` or `privateKeyPath`. If neither was specified, the entire config load failed. Now optional -- ssh2 falls back to ssh-agent automatically.
- **SIGTERM handler** -- Only SIGINT triggered cleanup. When the parent process sends SIGTERM (common when Claude Desktop restarts), SSH connections now close gracefully.
- **MCP SDK updated** -- Jumped from v1.0.1 to v1.29.0 (28 versions of bug fixes, security patches, protocol improvements). Zero breaking changes.
- **npm audit clean** -- All known vulnerabilities resolved.

### Cleanup

- **Dead code removed** -- `resolveCommandPath`, `isPathAllowed`, `validateWorkingDirectory`, `normalizeWindowsPath` (exported but never imported). Unused imports (`exec`, `promisify`) also removed.
- **`@types/ssh2` moved to devDependencies** -- Type packages don't belong in production deps.
- **Output size cap** -- Shell output is now capped at 1MB to prevent OOM on commands that dump large outputs. Truncated output includes a notice.

## Installation

Add to your `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "win-cli": {
      "command": "node",
      "args": [
        "C:/path/to/win-cli-mcp-server/dist/index.js",
        "--config",
        "C:/path/to/win-cli-mcp-server/config.json"
      ]
    }
  }
}
```

Or clone and set up:

```bash
git clone https://github.com/mhprol/win-cli-mcp-server.git
cd win-cli-mcp-server
npm install
npm run build
```

## Configuration

Copy `config.example.json` to `config.json` and customize:

```json
{
  "security": {
    "maxCommandLength": 50000,
    "blockedCommands": [],
    "blockedArguments": [],
    "allowedPaths": [],
    "restrictWorkingDirectory": false,
    "logCommands": true,
    "maxHistorySize": 2000,
    "commandTimeout": 600,
    "enableInjectionProtection": false
  },
  "shells": {
    "powershell": {
      "enabled": true,
      "command": "C:\\Program Files\\PowerShell\\7\\pwsh.exe",
      "args": ["-NoProfile", "-NoLogo", "-NonInteractive", "-Command"],
      "blockedOperators": []
    },
    "cmd": {
      "enabled": true,
      "command": "cmd.exe",
      "args": ["/c"],
      "blockedOperators": []
    },
    "gitbash": {
      "enabled": true,
      "command": "C:\\Program Files\\Git\\bin\\bash.exe",
      "args": ["--norc", "-c"],
      "blockedOperators": []
    }
  },
  "ssh": {
    "enabled": true,
    "defaultTimeout": 30,
    "maxConcurrentSessions": 5,
    "keepaliveInterval": 10000,
    "keepaliveCountMax": 3,
    "readyTimeout": 20000,
    "connections": {
      "my-server": {
        "host": "192.168.1.100",
        "port": 22,
        "username": "user",
        "privateKeyPath": "C:\\Users\\you\\.ssh\\id_ed25519"
      }
    }
  }
}
```

**SSH authentication priority**: explicit key > password > ssh-agent (automatic).

**Config notes**:
- File must be valid JSON without BOM (UTF-8, no BOM). Most editors default to this.
- `config.json` is gitignored to protect credentials. Use `config.example.json` as template.
- Shell `command` paths should point to the actual executable (e.g., `pwsh.exe` for PS7, not `powershell.exe` for PS5.1).

## Tools

| Tool | Description |
|------|-------------|
| `execute_command` | Run a command in PowerShell, CMD, or Git Bash |
| `get_command_history` | Retrieve history of executed commands |
| `ssh_execute` | Execute a command on a configured remote host |
| `ssh_disconnect` | Close an SSH connection |

## Security

This server provides **direct access to your system's command line and remote systems via SSH**. The default configuration is intentionally open for trusted single-user environments. For shared or exposed setups:

- Enable `restrictWorkingDirectory` and set `allowedPaths`
- Populate `blockedCommands` and `blockedArguments`
- Enable `enableInjectionProtection`
- Set `blockedOperators` per shell
- Use key-based SSH auth, never store passwords in config

## Credits

- **[Simon Benedict](https://github.com/SimonB97)** -- Original `win-cli-mcp-server` author
- **[delorenj](https://github.com/delorenj)** -- `super-win-cli` fork with extended config
- Hardening, bug fixes, and maintenance by **[Matt Prol](https://github.com/mhprol)**

## License

MIT -- see [LICENSE](LICENSE).

TDQS

A3.7/5.0

Scored across 4 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: local execution, history retrieval, remote execution, and remote disconnection. There is no overlap or ambiguity between the four tools.

Naming Consistency4/5

Names are mostly predictable: local tools use simple verb_noun (execute_command, get_command_history), while SSH tools consistently use the ssh_ prefix (ssh_execute, ssh_disconnect). The slight inconsistency is that ssh_execute uses 'execute' while the local counterpart is 'execute_command' with an extra noun.

Tool Count5/5

Four tools is well-scoped for a Windows CLI MCP server. Each tool serves a core function without unnecessary bloat.

Completeness4/5

The local command execution and history retrieval cover the primary workflow. SSH execute and disconnect provide a basic remote capability, though there is no explicit ssh_connect (likely implied by ssh_execute) and no way to manage connections beyond disconnecting.

Maintenance

ActivityInactive
ResponsivenessNo issues