Skip to main content
Glama

winops

An MCP agent that diagnoses Windows service and process faults through read-only, least-privilege PowerShell endpoints — and cannot change anything without a human saying yes.

CI

The problem

The easy way to build an ops agent is to hand an LLM a PowerShell session and a system prompt saying "be careful." That gives a model that read an untrusted event log the ability to run anything, and makes "be careful" the only thing between a hallucination and a production outage.

winops assumes the model will eventually be wrong or manipulated, and makes that survivable: it constrains what the agent can even reach, and separately gates the few things that change state.

Related MCP server: DiagBridge MCP

Two independent safety layers

1. The agent can only reach six functions. A PowerShell JEA endpoint runs as a RestrictedRemoteServer (NoLanguage mode) exposing exactly six named, parameter-validated functions. Not Invoke-Expression, not raw cmdlets, not the filesystem — regardless of what the agent asks for.

Read-only

Mutating (approval-gated)

Get-WinOpsServiceStatus

Restart-WinOpsService

Get-WinOpsFailedServices

Stop-WinOpsProcess

Get-WinOpsProcessInfo

Get-WinOpsRecentErrors

2. The two mutating tools always pause for a human. They're marked always_require_approval on the MCP tool, so the agent can investigate freely and forever, but acting requires an explicit yes.

Neither layer trusts the other. A bug in the approval logic still leaves the blast radius at "restart one named service"; a mis-scoped JEA role still can't act without a human. See docs/decisions/0002.

There's a third, quieter layer: invoke.ps1 takes the function name as a PowerShell [ValidateSet(...)], so even a fully compromised MCP server can only pick one of six branches — it can't inject a command name.

Architecture

flowchart LR
  CLI[winops CLI] --> Agent[Microsoft Agent Framework]
  Agent -->|MCPStdioTool| MCP[MCP server]
  MCP --> Client[jea.client]
  Client -->|powershell.exe, ValidateSet| Session[Invoke-Command]
  Session -->|PSRemoting, NoLanguage| JEA[(JEA endpoint)]

Full write-up in docs/architecture.md.

Quickstart

One-time setup, in an elevated PowerShell (registers the JEA endpoint, creates the WinOpsOperators group, enables PSRemoting):

.\jea\register.ps1

Verify the endpoint is really constrained before trusting it:

# should work
Invoke-Command -ConfigurationName WinOps -ComputerName localhost -ScriptBlock { Get-WinOpsFailedServices }
# should fail - proof the session is locked down
Invoke-Command -ConfigurationName WinOps -ComputerName localhost -ScriptBlock { Get-ChildItem C:\ }

Then:

cp .env.example .env          # add OPENAI_API_KEY (or AZURE_ENDPOINT)
uv sync
uv run winops diagnose-service Spooler

The agent investigates with the read-only tools, then proposes a fix and waits for y/n before anything runs.

Project status

Area

State

JEA role files, MCP server, agent, CLI

Built, 13 tests passing

MCP tool + approval-loop logic

Tested with fakes (no API key needed)

The JEA endpoint itself

Not yet runregister.ps1 needs elevation

Real LLM calls

Not yet run — no API key in the build environment

Fault-injection evals, audit log, replay

Not built (M4–M6)

The $using: parameter passing in invoke.ps1 is the documented way to send values into a NoLanguage session, but it hasn't been exercised against a live endpoint here — that's the first thing to check after register.ps1.

Why Windows services, not Hyper-V

This was originally scoped around Hyper-V VM faults. Hyper-V isn't available on Windows Home editions at all — a hard SKU-level restriction, not something a package manager can install around — so the managed domain moved to services and processes. Everything that makes the project interesting (MCP, JEA least privilege, approval-gated mutation) is unchanged; retargeting to Hyper-V means writing a new WinOpsHelpers.psm1 with Get-VM/Restart-VM and a matching role capability file. Nothing in jea/client.py, mcp_server.py, or agent.py mentions services by name. See docs/decisions/0001.

Safety and limitations

  • The agent cannot see or touch the filesystem, the registry, other services' configuration, or anything else outside those six functions.

  • Every JEA session is transcribed to C:\ProgramData\WinOps\Transcripts.

  • Approval is per-call and never remembered; there's no "approve all".

  • Tested against a local endpoint. Managing a remote host means pointing JEA_COMPUTER_NAME at it and dealing with WinRM auth, which isn't set up here.

License

MIT

Related MCP Connectors

Related MCP Servers