mcp-devops-agent
Provides tools to inspect Docker Compose services (list services, read status and logs) and restart unhealthy containers within a scoped project.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-devops-agentCheck the health of all services, and restart any that are unhealthy."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-devops-agent
A working demo of the Model Context Protocol (MCP) used for AI-assisted DevOps: an MCP server that exposes a small, scoped, audited set of tools for inspecting and remediating a real (if small) app stack, plus a scripted "agent" that drives it through an actual incident from detection to fix.
This corresponds to catalog entry p46 -- "MCP-Based Demo (AI DevOps)" in the DevOps Study Hub: MCP fundamentals, secure context sharing, AI-system interactions, and controlled AI access. Every one of those shows up here as an actual mechanism, not just a slide:
Concept | Where it lives |
MCP fundamentals |
|
Secure context sharing |
|
AI-system interactions |
|
Controlled AI access |
|
The scenario
target-stack/ is a tiny three-service app: api (a health-checked HTTP
service), worker (a background job processor), and redis (the queue
between them). demo/scenario.py enqueues a "poison" job that hangs the
worker -- its process never crashes, it just stops making progress, so its
heartbeat goes stale and Docker marks it unhealthy. That's a realistic
incident shape: docker restart worker genuinely fixes it, because the
problem is a stuck process, not corrupted state.
demo/run_incident_demo.py then plays the part of an AI DevOps agent
connected to the MCP server: it lists services, notices worker is
unhealthy, reads its status and recent logs, and decides to restart it --
exactly the sequence a reasonable agent would follow. What happens next
depends on which role the server was launched with:
readonly:
restart_serviceis refused before Docker is ever touched. The agent explains why and stops, instead of trying to work around the refusal.operator: the restart is allowed, and the demo confirms the worker is genuinely healthy again afterward -- not just that the tool call "succeeded".
Run both:
make install
make stack-up
make demo-readonly # -> refused, worker stays unhealthy (correct)
make stack-down
make stack-up
make demo-operator # -> restart allowed, worker recovers (correct)
make stack-downor make demo to run both back to back. .github/workflows/ci.yml's
integration job runs exactly this, for real, on every push -- see
CI/CD below.
Related MCP server: Docker MCP Server
Security model
Three mechanisms, each doing one job:
RBAC, fixed at launch (
mcp_server/rbac.py,runtime.py). Every tool declares the minimum role it needs; the server's role is resolved once fromMCP_ROLEwhen the process starts and never changes for the life of that connection. An MCP client picks the role by how it launches the server (its own config'senvblock) -- the same mechanism real clients like Claude Desktop already use to configure a local server. The agent on the other end of the connection has no API for requesting more access; the human deciding which config to hand it is the actual access-control boundary.An append-only audit log (
mcp_server/audit.py, wired in via the@guardeddecorator inguard.py). Every tool call -- allowed or denied, succeeded or failed -- is written as one JSON line, timestamped and tagged with the role that made it, before the result ever reaches the agent.demo/run_incident_demo.py's two runs each leave their ownvar/audit-<role>.log; diff them and the readonly run's deniedrestart_servicecall is right there.A scoped, allow-listed Docker surface (
mcp_server/docker_cli.py). Every command is filtered to containers in this demo's own compose project (--filter label=com.docker.compose.project=...), and every tool that names a service validates it against a fixed allow-list and re-derives the real container from Docker's own service label -- never from a client-supplied string used directly. An agent driving this server cannot discover, let alone touch, anything outsideapi/worker/redis, regardless of what it asks for. Commands are run as argument lists, never through a shell, so there's no injection surface even before that validation runs.
Why a scripted agent, not a live model call
demo/run_incident_demo.py is a deterministic sequence of MCP tool
calls, not a live call to Claude or any other model. Two reasons:
CI has no secrets to leak or bill. The workflow that proves this demo actually works runs on every push, unauthenticated, with no API key -- consistent with every other project in this portfolio (no cloud credentials, no real costs,
terraform applyagainst LocalStack instead of AWS elsewhere in the series).The interesting part isn't which model made the call. It's whether the server-side access control and audit trail hold up regardless of what's on the other end of the MCP connection -- a real point a scripted "worst case, most literal-minded agent" actually demonstrates more clearly than a model that might reasonably decide not to push the refused action.
The scripted agent still runs the same tool calls a real one would (it's
a real MCP client session, not a mock), and its narration is printed as
it happens so the transcript reads like an agent's reasoning. Pointing
mcp_server/server.py at a real model with tool-use (Claude, or anything
else that speaks MCP) instead of demo/run_incident_demo.py is a drop-in
swap -- the server doesn't know or care who's driving it.
Repo layout
mcp_server/ the MCP server
server.py FastMCP entrypoint (stdio transport)
tools.py the 5 tools an agent can call
rbac.py Role enum + the require() check
guard.py the @guarded decorator: RBAC + audit for every tool
audit.py append-only JSONL audit log
docker_cli.py scoped `docker` CLI wrapper (no docker-py dependency)
runtime.py process-wide role + audit logger, resolved once
target-stack/ the app the MCP server manages
docker-compose.yml
api/ always-healthy service, just to give list_services more to say
worker/ the one that breaks (and gets fixed)
demo/ the scripted "agent" + incident harness
scenario.py triggers the incident, polls for health changes
run_incident_demo.py the MCP client session + narration + assertions
tests/ unit tests (mock docker_cli -- no daemon required)CI/CD
ci.yml:ruff(lint),mypy(types),pytest(unit tests against a mockeddocker_cli, no Docker needed), then theintegrationjob described above -- a realdocker compose up --build, a real incident, a real MCP session over stdio, for both roles, asserting the outcome each role should produce.security.yml:gitleaks(secrets),hadolint(both Dockerfiles),bandit(the Python that ships, not the tests), a Trivy filesystem scan, and dependency review on PRs.dependabot.yml: weekly, 7-day cooldown, across GitHub Actions, pip, and both Dockerfiles.
What a real deployment would add
This is a demo, scoped on purpose -- worth being explicit about what it deliberately leaves out rather than pretending it's production-ready:
More roles and finer scopes. Two roles (readonly/operator) prove the mechanism; a real system would likely scope per-service or per-action (e.g., "can restart
workerbut notapi"), and probably add a human-approval step for the operator role rather than granting it for an entire session.Transport. stdio is right for a locally-launched server; a server shared across a team would run over
streamable-http(whichmcp.server.fastmcp.FastMCPalso supports) behind real authentication, not just a launch-time environment variable.Tamper-evident audit storage. The audit log here is a local file for demo purposes; a real deployment would ship it to storage the agent (and ideally the human operator) can't edit after the fact.
This server cannot be deployed
Maintenance
Related MCP Connectors
- mcpOAuthcom.vibgrate
Query your team's drift, vulnerability, and upgrade data from any AI assistant. OAuth 2.1, 51 tools.
- emisarOAuthdev.emisar
Let AI operate servers without SSH. Choose actions, approve risky changes, and audit every step.
Operate Linux, macOS and Windows from your LLM. Every action runs through an auditable allowlist.
Fail-closed policy guardrails for AI agents running kubectl, terraform, helm, and argocd.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to manage Docker containers, deploy stacks, and monitor services across multiple Docker hosts from one centralized location. Supports container lifecycle management, Docker Compose operations, and infrastructure orchestration through natural language commands.6MIT
- AlicenseNot gradedqualityNot gradedmaintenanceEnables AI assistants to interact with Docker containers through safe, permission-controlled access to inspect, manage, and diagnose containers, images, and compose services with built-in timeouts and AI-powered analysis.-
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to autonomously check, diagnose, and recover Dockerized services through safe, tool-based ops without direct host shell access.MIT
- AlicenseAqualityBmaintenanceEnables AI assistants to manage Docker containers and Compose stacks through natural language, including lifecycle operations, logs, stats, and secure remote access.2114 npm3MIT