Skip to main content
Glama
twine003
by twine003

Ops MCP Server

A self-hosted MCP server that gives an AI assistant (Claude Code, or any MCP-compatible client) live diagnostic and deployment access to a Windows or Linux machine over HTTP: shell/PowerShell commands, file exploration, git and Django operations, process/environment info, and IIS (Windows) or systemd (Linux) service control.

Two parallel implementations, same tool set otherwise:

Server

Installer

Windows

server_new.py

install_service.ps1 (NSSM service, or a scheduled task if NSSM isn't installed)

Linux

server_linux.py

install_linux.sh (systemd service, auto-creates its own venv)

Both installers are self-healing — safe to re-run any time on an already-installed machine. Re-running one:

  • kills a stuck process still holding the port,

  • syntax-checks the server script before touching the service (catches a corrupted/interrupted deploy with a clear error instead of installing something that crash-loops),

  • re-registers the service/task with current settings,

  • picks up a changed API key without a reboot,

  • and at the end prints the current API key, the reachable LAN IPs, and the live tool list (queried from the server itself, not a hardcoded doc) — so re-running with no arguments is also how you find out what's already configured.

On a Windows PC, server_new.py --desktop also serves the desktop "computer use" tools of desktop/ (screenshots, UI Automation, clicks and typing on the interactive desktop) — one server, one port, one key. See Desktop computer-use server below.

Quick start

Windows

# Copy this folder to a local disk first (e.g. C:\ops-mcp-server) - do not
# run it straight from a mapped network drive, see Troubleshooting below.
powershell -ExecutionPolicy Bypass -File install_service.ps1 -Port 8001 -ApiKey "a-long-random-key"

Linux

sudo ./install_linux.sh --port 8001 --api-key "a-long-random-key"

Omit -ApiKey/--api-key and the installer will reuse whatever key is already configured, or generate a new one and print it.

Connect from Claude Code

Add to .mcp.json:

{
  "mcpServers": {
    "ops-mcp-remote": {
      "type": "streamable-http",
      "url": "http://<machine-ip>:8001/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY"
      }
    }
  }
}

Related MCP server: PSKit

Available tools

Git — git_status, git_pull, git_log, git_branch

Django — django_collectstatic, django_migrate, django_check, django_manage

Web service — Windows: iis_status, iis_restart, iis_stop, iis_start · Linux: service_status, service_restart, service_stop, service_start (systemd unit name set via WEB_SERVICE_NAME)

Deployment — pip_install, full_deploy (git pull + pip install + collectstatic + migrate + restart web service)

File system — ls, cat, head, tail, find, grep, pwd, cd, file_info

System — server_info, env, ps, shell

Django and git tools are optional conveniences — the file system and system tools work standalone on any machine, with no Django project required.

Security

This server has no authentication unless you configure an API key — do that before exposing it beyond an isolated lab machine. shell is unrestricted command execution; treat the API key like a root password.

  • Both installers generate/print a key when none is configured. Pass one explicitly with -ApiKey/--api-key, or set it later:

    • Windows: [Environment]::SetEnvironmentVariable("MCP_API_KEY", "...", "Machine"), then re-run the installer so the service picks it up.

    • Linux: edit MCP_API_KEY= in /etc/ops-mcp/mcp.env, then systemctl restart ops-mcp.

  • The installers open the firewall for the port you choose (New-NetFirewallRule / ufw/firewalld) — nothing else.

  • shell()/django_manage() block an explicit list of destructive command patterns (rm -rf /, shutdown, flush, ...) as a safety net, not a security boundary — anyone with the API key can still run arbitrary code. Scope network access accordingly (VPN/LAN-only, not the open internet).

Configuration

Environment variables:

Variable

Default

Description

MCP_HOST

0.0.0.0

Host to bind to

MCP_PORT

8001

Port to listen on

MCP_API_KEY

(empty)

Bearer token for authentication

IIS_APP_POOL

(empty)

Windows only — required for iis_* tools

IIS_SITE_NAME

(empty)

Windows only — required for iis_* tools

WEB_SERVICE_NAME

(empty)

Linux only — systemd unit for service_* tools

Troubleshooting

"Impossible de charger... l'execution de scripts est desactivee" / "running scripts is disabled on this system" (PSSecurityException) when running install_service.ps1

This is Windows' default PowerShell execution policy (Restricted), not a problem with the installer:

powershell -ExecutionPolicy Bypass -File install_service.ps1

Service installs but never starts, right after installing from a mapped drive or \\server\share path

The Windows service (or scheduled task) runs as SYSTEM, which cannot see drives mapped in your logged-in session (N:\, W:\, ...) or shares that need your user credentials. Copy this folder to a local path first (e.g. C:\ops-mcp-server) and run the installer from there. (Linux has no equivalent problem — systemd services can read whatever's mounted at the OS level, e.g. NFS/CIFS in /etc/fstab.)

"Python est introuvable..." / pip not recognized, even though python seemingly "runs"

That's Windows' App Execution Alias stub in WindowsApps, not a real Python — it only opens the Microsoft Store. install_service.ps1 looks for a real interpreter on disk (ignoring PATH) and silently installs one from python.org if none is found; just re-run it. This only bites you if you run start_server.bat directly without having run the installer first, or if the machine has no internet access for the download.

Server won't start

  • Check if the port is already in use: netstat -an | findstr 8001 (Windows) / ss -ltnp | grep 8001 (Linux)

  • Check the firewall isn't blocking it

  • Windows: nssm status OpsMCP or Get-ScheduledTaskInfo -TaskName OpsMCP

  • Linux: systemctl status ops-mcp and journalctl -u ops-mcp -n 50 --no-pager

IIS/systemd commands fail

  • Windows: run the MCP server as Administrator, or grant the account IIS management rights

  • Linux: the systemd unit runs as root by default (edit install_linux.sh's generated unit if you want a lower-privilege user)

Git commands fail

  • Ensure git is on PATH, or set the full path in Config.GIT_PATH

Desktop computer-use server (Windows)

server_new.py --desktop mounts the tools of desktop/ into the same server. They give an AI assistant control of this PC's interactive desktop: multi-monitor screenshots (with change detection), the UI Automation / Win32 control tree, reading values, clicking, typing, dragging, scrolling and moving windows, designed so you can watch it work without your mouse and its actions fighting each other.

--desktop only works inside a user's session: a service runs in session 0, which has no access to the interactive desktop (the server refuses to start there). So on a PC it runs as a scheduled task at logon, inside your own session. Without --desktop (the service on a server) nothing changes. With --desktop the server-only deployment tools (full_deploy, git_pull, django_migrate, django_collectstatic, pip_install, iis_start/stop/restart) are not offered: on a PC they would touch the local checkout or database by mistake. desktop/desktop_server.py is no longer a server of its own: run directly (an old task), it starts server_new.py --desktop.

Agents must read desktop/AGENT_GUIDE.md: a summary goes out in the server's connection instructions and the usage_guide() tool returns the full text (edit the file, no restart needed).

Install (normal PowerShell, no admin needed)

powershell -ExecutionPolicy Bypass -File install_service.ps1 -Desktop

The installer (self-healing, safe to re-run):

  • creates/repairs desktop\.venv (needs Python >= 3.11 already installed; nothing is installed system-wide) and installs requirements-desktop.txt;

  • compiles the server and runs its unit tests before touching anything;

  • generates (or keeps) an API key in your user environment variable DESKTOP_MCP_API_KEY;

  • registers the scheduled task Desktop-MCP (server_new.py --desktop, at logon, your user, no stored password), starts it and checks /health;

  • prints the key, the port, the live tool list and the command to register it in Claude Code (it does not run it):

claude mcp add-json --scope project desktop-mcp '{"type":"http","url":"http://127.0.0.1:8011/mcp","headersHelper":"powershell -NoProfile -ExecutionPolicy Bypass -File C:/path/to/ops-mcp-server/desktop/mcp_headers.ps1"}'

headersHelper runs desktop/mcp_headers.ps1 on every connection: it reads the key from the user registry, so the key is never written into .mcp.json and a Claude session started before the variable existed still gets it (a ${DESKTOP_MCP_API_KEY} header would expand to empty, giving HTTP 401).

Options: -DesktopPort 8011 (default), -DesktopListenLan (listen on the LAN: needs an elevated shell for the firewall rule, and the server refuses to listen outside 127.0.0.1 without an API key), -DesktopRemove (unregister). By default it only listens on 127.0.0.1.

One cursor, three mechanisms

Windows has a single cursor per session, so every action uses the first mechanism that works and reports which one (mechanism) and whether the real cursor moved (cursor_moved):

  1. UI Automation (Invoke, Value, SelectionItem, Toggle, ExpandCollapse, Text, LegacyIAccessible): no cursor movement, no focus stealing;

  2. Win32 messages to the window (BM_CLICK, WM_SETTEXT, EM_REPLACESEL, WM_CHAR, WM_KEYDOWN/UP, CB_SETCURSEL, client-coordinate mouse messages): no cursor movement;

  3. SendInput (drawing, dragging, apps that ignore 1 and 2): moves the real cursor, which is saved and restored immediately.

Your mouse is never blocked by default. Optionally (set_option(freeze_user_mouse=true), or input.freeze_user_mouse_during_gestures in the config) your physical mouse is frozen only during a SendInput gesture.

How to stop it

  • The on-screen Stop button (top centre of the active monitor; only a physical click counts, and it never appears in screenshots);

  • a message to the assistant in the chat;

  • the hotkey Ctrl+Alt+Shift+F12 (stop_hotkey);

  • the pause() / resume() / status() tools.

Moving your mouse or typing does not stop or pause the assistant.

What you see

An orange "Claude" ghost cursor animates to each target before acting, and a soft border marks the monitor where it is working (grey while paused). Both are mandatory: every desktop tool call shows them and keeps them up while it runs, and they fade together a few seconds after the last call; they cannot be hidden (hide is refused, ghost.enabled / border.enabled are ignored). /health reports desktop, overlay_state and ghost_visible without lighting them. All overlay windows are click-through, never take focus and are excluded from every screen capture (WDA_EXCLUDEFROMCAPTURE). Colours and sizes: the screen_border tool or the border / ghost sections of the config.

Watching through RustDesk, AnyDesk or TeamViewer? Those tools stream a screen capture, so by default they never show the cursor, the border, the Stop button or the bubbles. Set "remote_visible": true in %LOCALAPPDATA%\desktop-mcp\config.json and restart the Desktop-MCP task: everything stays visible remotely (and also appears in Claude's own screenshots, which then skip drawing the cursor on top).

Comment bubbles

callout(text, ...) shows a bubble next to what the assistant is pointing at (a rectangle, a point or a UI element, outlined while the bubble is open), for guided tours, explanations and questions. It has an OK button and, with ask=True, a text field for your answer; wait=True returns accepted / answered (+ answer) / dismissed / replaced / stopped, or pending after timeout (then callout_wait(id) or callout_close(id)). Only your physical click and keys count: the bubble's window is always denied by the safety policy, so the assistant can neither read nor click it.

Safety and configuration

  • Password fields are never typed into or read.

  • deny_processes (password managers, UAC, lock screen...) and deny_window_title_regex are never controlled, not even for reading; add your own (e.g. a browser window with a banking session).

  • allow_write_processes: "*" = any app not denied; list exe names to make everything else read-only.

  • Every action is appended to an audit log (%LOCALAPPDATA%\desktop-mcp\audit.jsonl).

  • Defaults are in desktop/config.json; put your own overrides in %LOCALAPPDATA%\desktop-mcp\config.json (same keys, merged). No secrets in either file.

License

MIT

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    A
    maintenance
    Enables AI agents to securely manage and execute commands on remote Windows servers via WinRM, including PowerShell execution, system information retrieval, and service management.
    6
    39 PyPI
    6
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Neural-safe PowerShell automation server for AI agents, enabling file management, git workflows, system inspection, and command execution with a 5-tier safety pipeline.
    24 PyPI
    MIT
  • A
    license
    B
    quality
    C
    maintenance
    Enables AI tools to perform server operations such as log inspection, system monitoring, code management, Nginx and certificate management, with support for local and remote SSH modes and built-in security controls.
    23
    70 npm
    MIT