Enterprise Infrastructure & Metrics MCP Server

Enterprise Infrastructure & Metrics MCP Server

Provides read-only access to host system metrics (CPU, memory, disk), Docker container health/logs, and sandboxed log file analysis via MCP tools, enabling AI agents to monitor enterprise infrastructure safely.

Category
Visit Server

README

Enterprise Infrastructure & Metrics MCP Server

A production-grade Model Context Protocol (MCP) server, written in native async Python, that gives an LLM agent (Claude Desktop, Claude Code, or any MCP-compatible host) safe, read-only introspection into:

  • Host system resources — live CPU (per-core), memory, and disk metrics via psutil
  • Docker containers — list, inspect health/state, and tail logs via the Docker SDK
  • Application/system logs — sandboxed tail-and-filter of log files, with strict path-traversal protection

Built as a portfolio project demonstrating enterprise MCP server engineering: strict Pydantic v2 contracts, structured (never-raise) error handling, async-safe wrapping of blocking I/O, and an explicit, auditable security boundary.


Architecture

┌──────────────────────────┐        stdio (JSON-RPC 2.0)        ┌───────────────────────────────────────────┐
│   MCP Host                │ <---------------------------------> │   enterprise-mcp-server (this project)     │
│   (Claude Desktop /       │        subprocess, stdin/stdout      │                                             │
│    Claude Code / other)   │                                      │   ┌─────────────────────────────────────┐ │
└──────────────────────────┘                                      │   │  server.py  (FastMCP app)            │ │
                                                                    │   │  - registers 3 tools                 │ │
                                                                    │   │  - stdio transport                   │ │
                                                                    │   │  - logging -> stderr ONLY            │ │
                                                                    │   └───────────────┬─────────────────────┘ │
                                                                    │                    │ validated Pydantic    │
                                                                    │                    ▼ input models          │
                                                                    │   ┌─────────────────────────────────────┐ │
                                                                    │   │  tools/                              │ │
                                                                    │   │  ├─ system_metrics.py  (psutil)      │ │
                                                                    │   │  ├─ docker_manager.py  (docker SDK)  │ │
                                                                    │   │  └─ log_analyzer.py    (sandboxed FS)│ │
                                                                    │   └───────────────┬─────────────────────┘ │
                                                                    │                    │ asyncio.to_thread     │
                                                                    │                    │ (never blocks loop)   │
                                                                    │   ┌────────────────▼─────────────────────┐│
                                                                    │   │  utils/                               ││
                                                                    │   │  ├─ security.py  (path sanitization)  ││
                                                                    │   │  ├─ errors.py    (structured JSON)    ││
                                                                    │   │  └─ retry.py     (jittered backoff)   ││
                                                                    │   └────────────────────────────────────────│
                                                                    └───────────────┬───────────────┬───────────┘
                                                                                     │               │
                                                                     ┌───────────────▼───┐   ┌────────▼──────────┐
                                                                     │  Host OS           │   │  Docker daemon     │
                                                                     │  /proc, psutil     │   │  /var/run/         │
                                                                     │  sandboxed log dir │   │  docker.sock       │
                                                                     └────────────────────┘   └────────────────────┘

Request lifecycle: MCP host → JSON-RPC tools/call over stdin → FastMCP parses & validates arguments against the tool's Pydantic input model → tool function executes, wrapping every blocking call (psutil, Docker SDK, file I/O) in asyncio.to_thread → result serialized to JSON (success payload or structured ToolError — the function never raises past this boundary) → written to stdout as the JSON-RPC response.


The three tools

Tool Purpose Mutates host state?
get_system_metrics CPU (aggregate + per-core), memory (RAM + swap), disk (usage + I/O counters) No
manage_docker_containers List containers, inspect health/config, tail container logs No — read-only by design
analyze_local_logs Tail + filter a log file inside a sandboxed root directory No — read-only, sandboxed

Security boundaries

This project treats the LLM as an untrusted caller operating a read-only monitoring surface, not an operator with host control. Three concrete boundaries enforce that:

  1. manage_docker_containers exposes no lifecycle verbs. The Docker SDK and daemon support starting, stopping, restarting, executing commands in, and removing containers. None of that is wired up. Only list_containers, inspect_container, and get_container_logs exist as actions — an LLM cannot use this server to take down a container or run arbitrary commands inside one, even if prompted to.

  2. analyze_local_logs is sandboxed to a single, explicit root directory (MCP_LOG_ROOT_DIR, default /var/log), enforced in utils/security.py. Every requested path is:

    • stripped of leading / and .. segments (blocks absolute-path override),
    • joined onto the resolved root,
    • resolved again with Path.resolve() (collapses remaining .. segments and follows symlinks, closing the symlink-escape vector),
    • and finally checked with Path.is_relative_to() against the resolved root before any file is opened.

    A request for ../../etc/shadow, /etc/shadow, or a symlink inside the sandbox that points outside it is rejected with a structured PATH_TRAVERSAL_BLOCKED error — never a Python traceback, and never a silent read.

  3. Secrets are never echoed back. inspect_container returns env_var_count (an integer), not the environment variables themselves, since container env vars routinely contain credentials and API keys.

  4. Every response size is bounded. MCP_MAX_LOG_LINES hard-caps log tails server-side regardless of what a caller requests, and Docker log tails are capped at 500 lines — both protect the LLM's context window and prevent a single tool call from returning gigabytes of data.


Reliability & engineering standards

  • Never-raise tool boundary: every tool function wraps its entire body in try/except and returns a structured JSON ToolError (utils/errors.py) on failure — malformed input, a missing container, a down Docker daemon, or a permissions error all produce a well-formed, LLM-parseable payload instead of crashing the server process.
  • Async-safe by construction: psutil, the docker SDK, and file I/O are all synchronous/blocking under the hood. Every call site wraps them in asyncio.to_thread so a slow disk read or a stalled Docker socket cannot stall the event loop and starve other concurrent tool calls.
  • Jittered exponential backoff (utils/retry.py) around Docker daemon calls, since a momentarily busy socket is a transient condition worth retrying — capped at MCP_TOOL_RETRY_ATTEMPTS attempts.
  • Strict Pydantic v2 contracts (schemas.py) for every tool's input and output. FastMCP derives the JSON Schema exposed to the LLM host directly from the input models, so the tool's documented contract and its runtime validation can never drift apart.
  • stdout is sacred: the stdio transport uses stdout exclusively for JSON-RPC frames. All logging is configured to write to stderr (server.py) — a stray print() or misconfigured logger on stdout would silently corrupt the protocol stream for every connected host.

Project layout

enterprise-mcp-server/
├── pyproject.toml
├── README.md
├── .env.example
├── src/
│   └── enterprise_mcp_server/
│       ├── __init__.py
│       ├── server.py          # FastMCP app, tool registration, stdio entrypoint
│       ├── config.py          # Env-driven settings + security boundary (log root)
│       ├── schemas.py         # Pydantic v2 input/output contracts for all tools
│       ├── tools/
│       │   ├── system_metrics.py
│       │   ├── docker_manager.py
│       │   └── log_analyzer.py
│       └── utils/
│           ├── security.py    # Path-traversal sanitization
│           ├── errors.py      # Structured ToolError contract
│           └── retry.py       # Jittered exponential backoff
└── tests/
    ├── test_security.py
    └── test_system_metrics.py

Setup

Prerequisites

  • Python 3.11+
  • Docker (optional — only required for manage_docker_containers; the other two tools work without it)

Install

git clone https://github.com/<your-username>/enterprise-mcp-server.git
cd enterprise-mcp-server

python3 -m venv .venv
source .venv/bin/activate   # Windows: .venv\Scripts\activate

pip install -e ".[dev]"

Configure (optional)

Copy .env.example to .env and adjust as needed, or export directly:

export MCP_LOG_ROOT_DIR=/var/log          # sandbox root for analyze_local_logs
export MCP_MAX_LOG_LINES=1000             # hard ceiling on lines returned
export MCP_DOCKER_TIMEOUT_SECONDS=10      # Docker daemon socket timeout
export MCP_TOOL_RETRY_ATTEMPTS=3          # retry attempts for transient failures

Run standalone (for smoke-testing)

enterprise-mcp-server
# or
python -m enterprise_mcp_server.server

The process will sit waiting for JSON-RPC frames on stdin — this is expected; it's designed to be launched by an MCP host, not run interactively. Use the MCP Inspector (below) for interactive testing.

Test with MCP Inspector

npx @modelcontextprotocol/inspector enterprise-mcp-server

This opens a browser UI where you can call each tool directly and inspect the JSON Schema FastMCP generated from schemas.py.

Run the test suite

pytest -v

Claude Desktop configuration

Add the following to your Claude Desktop MCP config file (~/Library/Application Support/Claude/claude_desktop_config.json on macOS, %APPDATA%\Claude\claude_desktop_config.json on Windows):

{
  "mcpServers": {
    "enterprise-infra-metrics": {
      "command": "/absolute/path/to/enterprise-mcp-server/.venv/bin/enterprise-mcp-server",
      "args": [],
      "env": {
        "MCP_LOG_ROOT_DIR": "/var/log",
        "MCP_MAX_LOG_LINES": "1000",
        "MCP_DOCKER_TIMEOUT_SECONDS": "10"
      }
    }
  }
}

Note: Claude Desktop launches this as a subprocess with a minimal environment, so command must be the absolute path to the virtualenv's console script (not a bare enterprise-mcp-server, which relies on PATH being inherited — it usually isn't).

Restart Claude Desktop, and the hammer icon in the composer should show get_system_metrics, manage_docker_containers, and analyze_local_logs as available tools.


Example interactions

"Is my machine under memory pressure right now?" → calls get_system_metrics(scope="memory")

"Are any of my Docker containers unhealthy?" → calls manage_docker_containers(action="list_containers"), then inspect_container on anything with a non-healthy status

"Check nginx/access.log for the last hour's errors" → calls analyze_local_logs(relative_log_path="nginx/access.log", severity_filter="error_and_above")


License

MIT

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured