# Winreach Mcp

> WinReach — give your AI agent its own Windows machine. Remote PowerShell, file transfer, screenshots, and full desktop control (computer use) over HTTP, secured with per-user keys, roles, command policies, mTLS, and an audit log. A Model Context Protocol (MCP) server for Windows.

- **Type:** MCP server
- **Install:** `agentstack add mcp-ghoui-winreach-mcp`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [GhouI](https://agentstack.voostack.com/s/ghoui)
- **Installs:** 0
- **Category:** [AI & ML](https://agentstack.voostack.com/c/ai-and-ml)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [GhouI](https://github.com/GhouI)
- **Source:** https://github.com/GhouI/winreach-mcp

## Install

```sh
agentstack add mcp-ghoui-winreach-mcp
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# WinReach MCP

[](https://github.com/GhouI/winreach-mcp/actions/workflows/ci.yml)
[](LICENSE)
[](https://modelcontextprotocol.io)
[](https://learn.microsoft.com/powershell/)

**Give your AI agent its own Windows machine.** Remote PowerShell, file transfer, screenshots, and full desktop control (computer use — mouse & keyboard) over HTTP — secured with per-user keys, roles, command policies, mTLS, and an audit log.

WinReach is a TypeScript [Model Context Protocol](https://modelcontextprotocol.io) server that runs on a Windows host and exposes it to AI agents over Streamable HTTP. Where most Windows MCP servers run locally over stdio on the user's own machine, **WinReach is remote-first and secured**: Codex, Claude Code, and other MCP-capable agents can run PowerShell, move files, capture the screen, and drive the mouse and keyboard on a Windows box from anywhere — each with its own bearer key, role, and command policy, every action written to an audit log, and a one-command Cloudflare tunnel to publish it with no inbound firewall hole.

```text
Agent or MCP client  ->  WinReach MCP over HTTP  ->  Windows (PowerShell + desktop)
```

## Demo

[](assets/winreach-demo.mp4)

Connect an agent over HTTP, run remote PowerShell, drive the Windows desktop, and keep every action behind per-user keys — in about 30 seconds.

▶ **[Watch the full-quality MP4](assets/winreach-demo.mp4)** · built with [Remotion](https://www.remotion.dev/) (source and re-render steps in [video/](video/README.md)).

There's also a vertical **4:5 LinkedIn cut** with burned-in captions — [assets/winreach-linkedin.mp4](assets/winreach-linkedin.mp4) (cover: [assets/winreach-linkedin-cover.png](assets/winreach-linkedin-cover.png), captions: [assets/winreach-linkedin.srt](assets/winreach-linkedin.srt)).

## Why WinReach?

Most coding agents are comfortable in terminals, but Windows RDP is a GUI-first environment. WinReach gives agents a clean command surface instead:

- Run PowerShell commands on a Windows host from any MCP client.
- Keep persistent PowerShell sessions when variables, cwd, or imported modules matter.
- Publish the server to your agent in one command with a built-in Cloudflare tunnel — no fixed public IP, no inbound firewall hole.
- Use bearer-token auth and provider firewalls instead of exposing raw RDP workflows.
- Test locally with a diagnostic client before connecting a real agent.
- Avoid IIS: WinReach is a standalone Node HTTP server.

## Install & Connect (npx)

No clone, no build. WinReach ships as an npm package with a `winreach-mcp` binary.

**1. Generate a token:**

```powershell
npx winreach-mcp gen-token
# -> a fresh random bearer token, e.g. 9V4P3Am6EjKsMJVetyrl-s60NVcP_MmDvsP_gFgcq3U
```

**2a. Local, zero-config (stdio) — fastest path.** Any stdio MCP client can launch WinReach directly on the Windows machine. No HTTP, no tunnel, no token wrangling (an ephemeral admin token is minted if you don't set one):

```jsonc
{
  "mcpServers": {
    "winreach": {
      "command": "npx",
      "args": ["-y", "winreach-mcp", "--stdio"]
    }
  }
}
```

**2b. Remote (HTTP + bearer token).** Run the server on the Windows host and connect agents over the network (optionally via the built-in Cloudflare tunnel):

```powershell
$env:WINREACH_TOKEN = "PASTE_TOKEN_FROM_STEP_1"
npx winreach-mcp            # add --tunnel to publish a public Cloudflare URL
```

Copy-paste snippets for **Claude Code, Claude Desktop, Codex, and Cursor** (both local stdio and remote HTTP forms), plus the Claude Desktop one-click extension, are in **[docs/CONNECT.md](docs/CONNECT.md)**.

The `winreach-mcp` CLI:

| Command | What it does |
| --- | --- |
| `npx winreach-mcp` (or `start`) | Start the Streamable-HTTP MCP server (default; add `--tunnel` for a public URL). |
| `npx winreach-mcp --stdio` | Run over the local stdio transport for an MCP client that launches it. |
| `npx winreach-mcp gen-token` | Print a fresh random bearer token. |

## Tooling

WinReach exposes up to eight MCP tools (the last three are opt-in):

| Tool | Purpose |
| --- | --- |
| `powershell_execute` | Run one isolated PowerShell command. |
| `powershell_open_session` | Start a persistent PowerShell session. |
| `powershell_send` | Send a command to a persistent session. |
| `powershell_close_session` | Close a persistent session. |
| `powershell_list_sessions` | List active sessions. |
| `take_screenshot` | Capture the current screen of the Windows host as an image. Off by default (see below). |
| `computer_use` | Control the desktop like a human — move/click the mouse, type text, press key chords, and scroll — via Win32 `SendInput`. Coordinates match `take_screenshot`. Off by default; admin/operator roles recommended (see below). |
| `file_upload` | Write a file to the host, inside the configured file root. Off by default (see below). |
| `file_download` | Read a file from the host (optionally moving it) as base64. Off by default (see below). |

Command results include:

```json
{
  "commandId": "uuid",
  "stdout": "hello\r\n",
  "stderr": "",
  "exitCode": 0,
  "durationMs": 143,
  "truncated": false
}
```

`take_screenshot` captures the full virtual desktop (all monitors) and returns an
MCP image block (base64 PNG by default, `jpeg` optional) alongside a metadata
block:

```json
{
  "commandId": "uuid",
  "success": true,
  "format": "png",
  "mimeType": "image/png",
  "width": 1920,
  "height": 1080,
  "bytes": 82344,
  "durationMs": 96
}
```

Screen capture needs an active interactive desktop session. If WinReach runs in
a non-interactive service context (Windows session 0), the capture fails and the
tool returns `success: false` with the PowerShell error instead of an image.

Because screen capture reads whatever is on the desktop, it is a
read/exfiltration capability the command policy cannot express, so it is
**disabled by default**. Enable it with `WINREACH_ALLOW_SCREENSHOT=1`, and
optionally restrict it to specific principal roles with `WINREACH_SCREENSHOT_ROLES`.
When disabled (or when the caller's role is not permitted) the tool is not
exposed to that principal at all. Captures are written to a **server-owned**
directory (`WINREACH_SCREENSHOT_DIR`) — callers cannot choose the path — and are
automatically deleted after `WINREACH_SCREENSHOT_RETENTION_HOURS` (default `8`)
so they do not accumulate on the host.

### File transfer

`file_upload` and `file_download` move files between the agent and the Windows
host over MCP (base64-encoded, SHA-256 returned for integrity). They are **off by
default** and only registered once you set a sandbox root:

```powershell
$env:WINREACH_FILE_ROOT = "C:\winreach-files"
```

- **Every path is relative to `WINREACH_FILE_ROOT`.** Absolute paths, `..`
  traversal, and symlinks that escape the root are rejected, so a transfer can
  never touch files outside the sandbox. Setting the root is what enables the
  tools.
- `file_upload` refuses to overwrite unless `overwrite: true`, and creates
  parent directories inside the root as needed.
- `file_download` returns the file as base64; set `deleteSource: true` to **move**
  it (the server copy is deleted after a successful read).
- Files larger than `WINREACH_MAX_FILE_BYTES` (default 75 MB) are rejected in
  either direction.
- Every transfer is written to the audit log (tool, principal, server path, size).

## Quickstart

Requirements:

- Node.js 24 or newer
- npm
- Windows PowerShell (`powershell.exe`) or PowerShell 7 (`pwsh`)

Run WinReach locally:

```powershell
git clone https://github.com/GhouI/winreach-mcp.git
cd winreach-mcp
npm install
$env:WINREACH_TOKEN = "dev-token"
npm run dev
```

The server defaults to:

```text
http://127.0.0.1:7573/mcp
```

Use the diagnostic client from another terminal:

```powershell
$env:WINREACH_TOKEN = "dev-token"
npm run client -- list-tools
npm run client -- exec Write-Output hello
```

Target more than one WinReach server with `WINREACH_URLS`:

```powershell
$env:WINREACH_TOKEN = "shared-token"
$env:WINREACH_URLS = "http://win-1:7573/mcp,http://win-2:7573/mcp"
npm run client -- exec hostname
```

## Public Access With One Command

When you set up WinReach on a server and want an agent to reach it from anywhere, enable the built-in [Cloudflare quick tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/do-more-with-tunnels/trycloudflare/). WinReach downloads `cloudflared` on first use (no Cloudflare account needed), opens a tunnel, and prints a ready-to-paste agent config.

```powershell
$env:WINREACH_TOKEN = "replace-with-a-long-random-token"
$env:WINREACH_TUNNEL = "cloudflare"
npm run dev
```

Output:

```text
WinReach MCP listening at http://127.0.0.1:7573/mcp
Cloudflare tunnel ready: https://random-words.trycloudflare.com
Public MCP endpoint: https://random-words.trycloudflare.com/mcp

Connect an agent with this public endpoint (the bearer token is still required):

Claude Code:
  claude mcp add --transport http winreach https://random-words.trycloudflare.com/mcp --header "Authorization: Bearer "
...
```

You can also enable it ad hoc with a flag:

```powershell
npm run dev -- --tunnel
```

Notes:

- `cloudflared` connects to WinReach over loopback, so tunnel mode needs **no** `0.0.0.0` bind and **no** inbound firewall rule. WinReach tells `cloudflared` to rewrite the forwarded `Host` header to `127.0.0.1` so the server's built-in DNS-rebinding protection keeps working.
- Tunnel mode publishes a remote-command server to the public internet, protected **only** by the bearer token. Use a long random `WINREACH_TOKEN` (32+ characters); WinReach warns at startup if the token looks weak.
- Quick-tunnel hostnames are random and **change every restart**. Re-paste the printed URL into your agent, or move to a [named Cloudflare tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/) for a stable hostname.
- The bearer token is still enforced over the tunnel. Treat the public URL as sensitive and rotate the token after demos.
- Auto-install downloads and runs the official `cloudflared` binary from GitHub releases over HTTPS. If you need a verified/pinned binary, set `WINREACH_TUNNEL_AUTOINSTALL=0` and point `WINREACH_CLOUDFLARED_PATH` at a `cloudflared` you installed and checked yourself.

## Connect Agents

Copy-paste snippets for Claude Code, Claude Desktop, Codex, and Cursor (local
stdio and remote HTTP forms) plus the one-click Claude Desktop extension:
**[docs/CONNECT.md](docs/CONNECT.md)**.

Full setup guide: [Install WinReach and Connect Agents](docs/INSTALL_AND_AGENT_USAGE.md)

Feature overview: [WinReach Features](docs/FEATURES.md)

Codex `~/.codex/config.toml`:

```toml
[mcp_servers.winreach]
url = "http://WINDOWS_SERVER_IP:7573/mcp"
bearer_token_env_var = "WINREACH_TOKEN"
tool_timeout_sec = 120
default_tools_approval_mode = "prompt"
enabled = true
```

Claude Code:

```powershell
claude mcp add --transport http winreach http://WINDOWS_SERVER_IP:7573/mcp `
  --header "Authorization: Bearer YOUR_TOKEN"
```

Also see:

- [Codex config example](examples/codex-config.toml)
- [Claude Code MCP JSON example](examples/claude.mcp.json)

## Remote Windows Deployment

On the Windows host:

```powershell
git clone https://github.com/GhouI/winreach-mcp.git
cd winreach-mcp
npm install
$env:WINREACH_TOKEN = "replace-with-a-long-random-token"
$env:WINREACH_HOST = "0.0.0.0"
$env:WINREACH_PORT = "7573"
npm run dev
```

Open the Windows firewall for the MCP port:

```powershell
New-NetFirewallRule `
  -DisplayName "WinReach MCP 7573" `
  -Direction Inbound `
  -Protocol TCP `
  -LocalPort 7573 `
  -Action Allow
```

Restrict your cloud firewall so TCP `7573` is reachable only from trusted IP addresses. If you only need agent access (not raw port access), prefer [tunnel mode](#public-access-with-one-command) and keep the bind on `127.0.0.1`.

## Configuration

Prefer a UI? The [`setup-web`](setup-web/) app (Next.js) generates the env vars,
a `start.ps1`, a Windows firewall rule for your allowed IPs, and agent-connect
snippets from a form — see [setup-web/README.md](setup-web/README.md).

WinReach reads `WINREACH_*` variables.

| Variable | Default | Description |
| --- | --- | --- |
| `WINREACH_TOKEN` | required* | Bearer token for a single full-access admin. *Required unless `WINREACH_PRINCIPALS` is set. |
| `WINREACH_PRINCIPALS` | empty | JSON array of per-user principals: `[{"name","role","token"\|"tokenEnv"\|"tokenHash","allow":[],"deny":[],"tools":[]}]`. Enables per-user authorization. A key may be plaintext (`token`/`tokenEnv`) or a SHA-256 hex `tokenHash` (a presented token authenticates when its SHA-256 matches — so an external store never needs the plaintext). `tools` optionally limits a principal to specific tool names (omit for all). |
| `WINREACH_COMMAND_ALLOWLIST` | empty | Regex allowlist (comma-separated or JSON array). If non-empty, only matching commands run. |
| `WINREACH_COMMAND_DENYLIST` | empty | Regex denylist (comma-separated or JSON array). Matching commands are blocked; deny wins over allow. |
| `WINREACH_AUDIT_LOG` | empty | Path to an append-only JSONL audit log of every tool call. |
| `WINREACH_TLS_CERT` | empty | PEM certificate path. Set with `WINREACH_TLS_KEY` to serve HTTPS in-app. |
| `WINREACH_TLS_KEY` | empty | PEM private key path for TLS. |
| `WINREACH_TLS_KEY_PASSPHRASE` | empty | Passphrase for an encrypted TLS key. |
| `WINREACH_TLS_CLIENT_CA` | empty | PEM CA bundle to verify client certificates. Enables mutual TLS (requires TLS). |
| `WINREACH_ALLOW_SCREENSHOT` | `0` | Set to `1` to enable the `take_screenshot` tool. Disabled by default. |
| `WINREACH_SCREENSHOT_ROLES` | empty | Comma-separated principal roles allowed to capture. Empty means any authenticated principal (when enabled). |
| `WINREACH_SCREENSHOT_DIR` | temp subdir | Server-owned directory captures are written to. Callers cannot override it. |
| `WINREACH_SCREENSHOT_RETENTION_HOURS` | `8` | Captures older than this are deleted (on startup and before each capture). Set `0` to keep them indefinitely. |
| `WINREACH_ALLOW_COMPUTER_USE` | `0` | Set to `1` to enable the `computer_use` desktop-input tool. Disabled by default. GUI control bypasses the command policy, so grant it to trusted roles only. |
| `WINREACH_COMPUTER_USE_ROLES` | empty | Comma-separated roles allowed to drive the desktop. Empty means any authenticated principal (when enabled). |
| `WINREACH_COMPUTER_USE_MAX_ACTIONS_PER_SEC` | `10` | Per-principal rate cap on input actions. |
| `WINREACH_COMPUTER_USE_KEY_DENYLIST` | empty | Comma-separated regexes for key chords to block (e.g. `win\+r`). A speed bump, not a boundary. |
| `WINREACH_COMPUTER_USE_HALT_FILE` | empty | If this file exists, all `computer_use` actions are refused — an operator kill switch. |
| `WINREACH_COMPUTER_USE_AUDIT_TEXT` | `0` | Set to `1` to record raw typed text in the audit log. Off by default (length + truncated hash only). |
| `WINREACH_FILE_ROOT` | empty | Sandbox root for `file_upload`/`file_download`. Setting it enables the tools; all transfer paths resolve within it. |
| `WINREACH_MAX_FILE_BYTES` | `78643200` | Maximum bytes per transferred file (both directions). Default 75 MB. |
| `WINREACH_URL` | `http://127.0.0.1:7573/mcp` | Diagnostic client URL for one WinReach server. |
| `WINREACH_URLS` | empty | Diagnostic client comma-separated URLs for multiple servers using `WINREACH_TOKEN`. |
| `WINREACH_TARGETS` | empty | Diagnostic client JSON array for named servers and per-target token env vars. |
| `WINREACH_HOST` | `127.0.0.1` | Bind host. Use `0.0.0.0` only behind a firewall or tunnel. |
| `WINREACH_PORT` | `7573` | Bind port. |
| `WINREACH_ENDPOINT_PATH` | `/mcp` | MCP endpoint path. |
| `WINREACH_ALLOWED_ORIGINS` | empty | Comma-separated allowed `Origin` values. |
| `WINREACH_SHELL_PATH` | auto | Explicit `pwsh` or `powershell.exe` path. |
| `WINREACH_CWD` | process cwd | Default working directory. |
| `WINREACH_TIMEOUT_MS` | `30000` | Default c

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [GhouI](https://github.com/GhouI)
- **Source:** [GhouI/winreach-mcp](https://github.com/GhouI/winreach-mcp)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-ghoui-winreach-mcp
- Seller: https://agentstack.voostack.com/s/ghoui
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
