AgentStack
MCP verified MIT Self-run

Infrastructure Mcp

mcp-radumalica-infrastructure-mcp · by radumalica

A self-hosted MCP server that lets AI agents (Claude, Cursor, Hermes, OpenAI Agents, ...) safely operate real infrastructure — Linux servers, Docker, Kubernetes, Proxmox, Grafana, network gear, and more — through high-level, auditable tools instead of raw shell access.

No reviews yet
0 installs
0 views
view→install

Install

$ agentstack add mcp-radumalica-infrastructure-mcp

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution Used
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README — it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/mcp-radumalica-infrastructure-mcp)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
today

Declared compatibility

Claude CodeClaude DesktopCursorWindsurf

Compatibility is declared by the source manifest. End-to-end runtime verification is coming — see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps — measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Infrastructure Mcp? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Infrastructure MCP Server

A self-hosted Model Context Protocol server that lets AI agents (Claude, Cursor, Hermes, OpenAI Agents, ...) safely operate real infrastructure — Linux servers, Docker, Kubernetes, Proxmox, Grafana, network gear, and more — through high-level, auditable tools instead of raw shell access.

> Status: early, actively developed. See [Roadmap](#roadmap) for what's implemented and what's coming.


Table of Contents

  • [Why](#why)
  • [Goals](#goals)
  • [Non-Goals](#non-goals)
  • [Architecture](#architecture)
  • [Current Features](#current-features)
  • [Installation](#installation)
  • [Configuration](#configuration)
  • [Connecting an AI Agent](#connecting-an-ai-agent)
  • [Tool Design Philosophy](#tool-design-philosophy)
  • [Security](#security)
  • [Development](#development)
  • [CI/CD](#cicd)
  • [Roadmap](#roadmap)
  • [Contributing](#contributing)
  • [License](#license)
  • [A Note on How This Project Is Built](#a-note-on-how-this-project-is-built)

Why

AI agents are increasingly asked to help operate infrastructure — check disk space, restart a container, look at recent errors. The naive way to enable this is to hand the agent raw SSH/shell access. That's fast to build and dangerous to run: no audit trail, no input validation, no blast-radius control, and every agent has to independently learn how to safely operate every kind of system.

Infrastructure MCP Server is the alternative: a single, self-hosted service that exposes meaningful, structured, inventory-driven tools (disk_usage(), docker_restart(), grafana_alerts()) instead of execute(command). Agents reason about infrastructure; this server is the only thing that actually touches it.

Goals

  • Self-hosted
  • Production-ready
  • Secure by default
  • Extensible
  • Vendor agnostic
  • Infrastructure as Code friendly
  • Stateless where possible
  • Structured outputs only
  • Human-auditable
  • Open Source

Non-Goals

The project is not intended to become:

  • another SSH wrapper
  • another Ansible replacement
  • another monitoring system
  • another Kubernetes operator

Instead, it provides AI-friendly infrastructure primitives built on top of those things.


Architecture

                 AI Agent
      (Hermes / Claude / Cursor)

                    │
                    │ MCP
                    ▼

        Infrastructure MCP Server

                    │

        ┌───────────┴────────────┐

        │                        │

    Infrastructure APIs      SSH / Telnet Layer

        │                        │

        ▼                        ▼

 Proxmox                  Linux Servers

 Grafana                  Cisco / MikroTik

 Prometheus               UniFi

 Docker                   Home Assistant

 Kubernetes               Storage / Bare Metal

Three layers, wired top-down:

  1. MCP layer (mcp/tools/, mcp/prompts/, mcp/resources/) — registers tools with the MCP SDK. Tools call adapters; they never talk to infrastructure directly.
  2. Adapter layer (internal/linux/, internal/docker/, internal/proxmox/, internal/grafana/, ...) — one isolated package per integration, all implementing a common interface. Authentication (SSH keys, API tokens, kubeconfig) lives inside adapters and is never exposed to AI agents.
  3. Foundationinternal/inventory/ (YAML config, validated, env-var expansion for secrets) and internal/ssh / internal/telnet (pooled connections to targets, including legacy devices with obsolete crypto or no SSH at all).

Everything is inventory-driven. No IP addresses, hostnames, or credentials appear in tool implementations — targets are resolved by name/tag from the inventory YAML.

Technology Stack

| Concern | Choice | |---|---| | Language | Go 1.26+ | | MCP SDK | Official Model Context Protocol Go SDK | | Configuration | YAML (go-playground/validator) | | Logging | log/slog (structured, JSON) | | SSH | golang.org/x/crypto/ssh | | HTTP | net/http | | Serialization | encoding/json | | Testing | Go testing, Testcontainers, Docker Compose | | CI | GitHub Actions |

Project Structure

infrastructure-mcp/
├── cmd/
│   └── server/            # entry point (stdio MCP transport)
├── internal/
│   ├── inventory/          # YAML config, validation, secret expansion
│   ├── ssh/                 # pooled SSH connectivity
│   ├── telnet/               # legacy device transport
│   ├── remote/                # SSH/Telnet dispatch by target protocol
│   ├── linux/                  # Linux diagnostics adapter
│   ├── docker/                  # Docker CLI-over-SSH adapter
│   ├── toolerr/                  # structured error contract
│   ├── kubernetes/
│   ├── grafana/
│   ├── proxmox/                    # planned
│   ├── prometheus/, loki/            # planned
│   ├── cisco/, mikrotik/, unifi/      # planned
│   └── homeassistant/                  # planned
├── mcp/
│   ├── tools/               # one file per MCP tool
│   ├── prompts/
│   └── resources/
├── configs/                # example inventory
├── docs/
├── examples/
├── scripts/
└── tests/

Current Features

Tools implemented and tested against the real MCP protocol so far:

Core (v0.1)

  • list_servers — list inventory targets, optionally filtered by tag
  • run_command — run an arbitrary command against an inventory target
  • uptime — uptime and load averages
  • disk_usage — per-filesystem usage with warning/critical severity
  • memory_usage — memory utilization with warning/critical severity

Linux (v0.2)

  • failed_services — systemd units in the failed state
  • cpu_usage — aggregate CPU utilization (Proxmox/KVM guest-time aware)
  • reboot_required — pending-reboot detection (marker file + kernel comparison)
  • running_processes — top processes by CPU usage
  • journal_errors — recent systemd journal entries at error priority+
  • kernel_version — running kernel release

Docker (v0.3)

  • docker_ps — list containers (running or all)
  • docker_images — list local images
  • docker_stats — point-in-time resource usage snapshot
  • docker_logs — recent combined stdout/stderr log lines
  • docker_restart — restart a container (destructive; requires confirm: true)

Kubernetes (v0.4)

  • kubectl_get_pods — list pods, optionally scoped to a namespace
  • kubectl_logs — fetch a pod/container's log lines
  • kubectl_events — recent cluster events, most recent first
  • kubectl_describe — structured pod summary (spec/status highlights + recent events)
  • kubectl_nodes — node list with readiness, roles, and capacity

Grafana (v0.5)

  • grafana_alerts — currently firing/resolved alert instances from the alerting API
  • grafana_dashboards — search dashboards by title text and/or tag
  • grafana_annotations — annotations, optionally scoped to a time range and/or tags
  • grafana_query — raw query against one datasource (PromQL/LogQL/SQL/...); response is a datasource-specific passthrough, not normalized

Cross-cutting, since v0.1

  • Legacy network device support: Telnet transport, SSH legacy-crypto negotiation, transparent per-target protocol dispatch
  • Structured error contract on every tool (message, recommendation, retryable, category)
  • Structured per-execution logging (tool, user, target, duration, result, error)
  • Host-key verification fail-closed by default, with explicit opt-outs for lab use
  • Dual MCP transport: stdio (local subprocess clients) and Streamable HTTP (remote clients), from the same binary — HTTP is fail-closed by default, requiring a bearer token (MCP_HTTP_TOKEN) unless explicitly opted out for lab use
  • Docker image (multi-arch, GHCR) + Docker Compose, with a self-check -healthcheck mode for shell-less containers
  • CI/CD: PR-gated build/vet/lint/race-tested-CI, automatic semantic versioning + changelog + image publish + Trivy scan on merge to main — see [CI/CD](#cicd)

See [Roadmap](#roadmap) for what's next.


Installation

Prerequisites

  • Go 1.26 or newer (bumped from 1.24 in v0.4 — required by k8s.io/client-go's current release train)
  • SSH access (key- or password-based) to the servers/devices you want to manage
  • An MCP-capable client (Claude Desktop, Claude Code, Cursor, or any other MCP client)

Build from source

git clone https://github.com//infrastructure-mcp.git
cd infrastructure-mcp
go build -o bin/infrastructure-mcp ./cmd/server

Or run via Docker

Pre-built multi-arch images (linux/amd64, linux/arm64) are published to GHCR on every merge to main (see [CI/CD](#cicd)):

docker pull ghcr.io//infrastructure-mcp:latest

Or with Docker Compose, which wires up the inventory and SSH volume mounts for you — see [docker-compose.yaml](docker-compose.yaml):

cp configs/inventory.example.yaml inventory.yaml   # fill in your real targets
cp .env.example .env                                # fill in real secrets, never commit
docker compose up -d

Run tests

go build ./...              # build
go vet ./...                # static checks
go test ./... -race -cover  # all tests, race detector, coverage
go test ./internal/linux -run TestName   # a single test

Configuration

The server is entirely inventory-driven — copy the example inventory and fill in your own targets:

cp configs/inventory.example.yaml configs/inventory.yaml
servers:
  archive:
    hostname: 10.0.0.5
    user: hermes
    key: ~/.ssh/archive
    tags:
      - linux
      - ethereum

  pve01:
    hostname: 10.0.0.2
    user: hermes
    proxyjump: jumpbox        # reached only via another inventory target
    tags:
      - proxmox

routers:
  core:
    hostname: 10.0.0.10
    vendor: cisco
    user: admin
    password: ${CORE_ROUTER_PASSWORD}   # resolved from the environment at load time

  legacy-edge:                # no SSH server at all — Telnet only
    hostname: 10.0.0.12
    vendor: cisco
    user: admin
    password: ${LEGACY_EDGE_PASSWORD}
    protocol: telnet

switches:
  ancient-sw:                 # obsolete SSH key exchange/cipher/MAC only
    hostname: 10.0.0.21
    vendor: cisco
    user: admin
    password: ${ANCIENT_SW_PASSWORD}
    legacy_crypto: true

# Every category, including single-instance-feeling ones like Grafana and
# Proxmox, is a map keyed by name — so a second Grafana or a second Proxmox
# cluster is just another key, not a schema change.
grafana:
  main:
    url: https://grafana.lab.local
    token: ${GRAFANA_TOKEN}

proxmox:
  lab:
    url: https://pve.lab.local:8006
    token: ${PROXMOX_TOKEN}

# All auth (client cert, bearer token, exec plugin, ...) lives inside the
# kubeconfig file itself — nothing is inlined into the inventory.
kubernetes:
  home:
    kubeconfig: /etc/infrastructure-mcp/kubeconfig-home
    context: home-admin

Secrets are never written in plaintext. Any ${VAR} in the YAML is resolved from the process environment at load time, and load fails closed if the variable is unset — real credentials are never committed to the repo.

Server flags

./bin/infrastructure-mcp \
  -inventory configs/inventory.yaml \
  -known-hosts ~/.ssh/known_hosts \
  -insecure-ignore-host-key=false \
  -transport stdio

| Flag | Default | Description | |---|---|---| | -inventory | configs/inventory.yaml | Path to the inventory YAML file | | -known-hosts | $HOME/.ssh/known_hosts | Path used to verify target SSH host keys | | -insecure-ignore-host-key | false | Skip host key verification entirely — lab/dev only, never production | | -transport | stdio | stdio (local subprocess clients) or http (remote Streamable HTTP clients) | | -http-addr | :8080 | Address to listen on when -transport=http | | -healthcheck | false | Instead of starting the server, GET -healthcheck-url and exit 0/1 — used as the container HEALTHCHECK | | -allow-anonymous-http | false | Allow -transport=http to serve without a bearer token — lab/dev only, every tool becomes reachable to anyone who can reach the port |

Host key verification is fail-closed by default: an unrecognized target's connection is refused unless it's in known_hosts or you explicitly opt into insecure mode. -transport=http is fail-closed the same way: it refuses to start unless the MCP_HTTP_TOKEN environment variable is set (see below) or -allow-anonymous-http is passed explicitly.


Connecting an AI Agent

Infrastructure MCP Server supports two transports from the same binary, so it works with both local-subprocess and remote-network MCP clients:

  • stdio (default) — the client spawns the binary itself and talks over its stdin/stdout. This is what most desktop IDEs and agent CLIs (Claude Desktop, Claude Code, Cursor) expect.
  • Streamable HTTP (-transport http) — the server listens on the network (/mcp endpoint, plus /healthz) and any remote MCP client connects over HTTP. Use this when the server runs elsewhere (a container, a VM, behind a reverse proxy) rather than on the same machine as the client.

stdio (local)

{
  "mcpServers": {
    "infrastructure": {
      "command": "/path/to/bin/infrastructure-mcp",
      "args": ["-inventory", "/path/to/configs/inventory.yaml"]
    }
  }
}

Remote HTTP

-transport=http requires a bearer token — /mcp exposes every registered tool, including run_command, so the server refuses to start without one (fail-closed, like host key verification above). Generate one and set it as MCP_HTTP_TOKEN:

export MCP_HTTP_TOKEN=$(openssl rand -hex 32)
./bin/infrastructure-mcp -inventory configs/inventory.yaml -transport http -http-addr :8080

(or docker compose up -d, which does this by default once MCP_HTTP_TOKEN is set in .env — see [Installation](#installation)). Every request to /mcp must then send Authorization: Bearer $MCP_HTTP_TOKEN; /healthz stays open for container/load-balancer health checks. -allow-anonymous-http disables this requirement — lab/dev only, never expose an anonymous instance to anything but localhost.

Point any remote-MCP-capable client at http://:8080/mcp with that header set. For clients that only support stdio, bridge with mcp-remote or an equivalent local proxy:

{
  "mcpServers": {
    "infrastructure": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "http://:8080/mcp", "--header", "Authorization: Bearer ${MCP_HTTP_TOKEN}"]
    }
  }
}

Put the HTTP endpoint behind TLS and network-level access control (a reverse proxy, VPN, or private network) before exposing it beyond localhost — the server itself does not add its own transport-layer authentication.

Once connected, over either transport, the agent sees only the tools listed under [Current Features](#current-features) — never raw shell access, never credentials.


Tool Design Philosophy

Never expose raw infrastructure:

execute(command)     # avoid

Prefer meaningful, structured actions:

check_disk()
check_memory()
docker_list()
grafana_alerts()
backup_switch()

Every tool:

  • validates its inputs
  • never panics
  • returns structured JSON
  • includes severity where relevant
  • includes a recommendation on warning/critical states
  • includes a timestamp
{
  "status": "warning",
  "server": "archive",
  "disk_usage": 91,
  "recommendation": "Free disk space or expand storage.",
  "timestamp": "2026-07-23T19:10:00Z"
}

Logging

Every tool execution produces a structured log line with:

tool, user, duration, target, result, error

Error Handling

Tools never return a raw Go error. Every failure is shaped as:

message, recommendation, retryable, category

(category is one of not_found, auth, network, timeout, invalid_input, internal.)


Security

  • No secrets

Source & license

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

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

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.