DevOps
FreeNot checkedA production-ready MCP (Model Context Protocol) server that serves an internal DevOps knowledge base — skills, instructions, prompts, and policies for Python, A
About
A production-ready MCP (Model Context Protocol) server that serves an internal DevOps knowledge base — skills, instructions, prompts, and policies for Python, Ansible, Terraform, Docker, Kubernetes, and CI/CD — to AI assistants over Streamable HTTP.
README
A production-ready Model Context Protocol (MCP) server that serves DevOps knowledge base — skills, instructions, prompts, and policies for Python, Ansible, Terraform, Docker, Kubernetes, CI/CD, and general engineering — to any MCP-compatible AI client (Claude Desktop, VS Code, Cursor, ...).
1. Overview
The server reads versioned Markdown files (with YAML frontmatter) from
content/ and exposes them to AI assistants via 4 MCP tools over a
Streamable HTTP transport, deployed as a Docker container.
Why MCP? It gives AI assistants a controlled, auditable, read-only window into your DevOps standards so generated code, pipelines, and infra always align with internal policy — without copy-pasting documentation into prompts.
2. Architecture
┌─────────────────────┐ HTTPS / X-API-Key ┌─────────────────────────────┐
│ MCP Client │ ───────────────────────────▶ │ Starlette ASGI app │
│ (Claude / VS Code) │ │ ├─ /health │
└─────────────────────┘ │ └─ /mcp (FastMCP mount) │
│ APIKeyMiddleware │
└──────────────┬───────────────┘
│
▼
┌─────────────────────────────┐
│ mcp_server.tools (FastMCP) │
│ @tool fetch_devops_content│
│ @tool list_devops_* │
│ @tool get_devops_*_by_id │
└──────────────┬───────────────┘
│
▼
┌─────────────────────────────┐
│ src/services │
│ └─ ContentService │
│ ├─ ContentLoader (fs) │
│ └─ ContentFilter │
└──────────────┬───────────────┘
│
▼
┌────────────────────────┐
│ content/*.md │
│ (frontmatter + body) │
└────────────────────────┘
3. Prerequisites
- Python 3.11+
- Poetry 1.7+ (or pip)
- Docker Engine 24+ and Docker Compose v2 (for containerized deployment)
- An API key value of your choosing (any non-empty string) for
MCP_API_KEY
4. Local Setup
# 1. Clone
git clone <repo-url> hbiz-devops-mcp
cd hbiz-devops-mcp
# 2. Environment
cp .env.example .env
# Edit .env — at minimum set MCP_API_KEY to a non-empty secret
# 3. Install dependencies
poetry install
# OR
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
# 4. Run
poetry run python main.py
# OR
python main.py
The server listens on http://localhost:8000 by default.
- Health:
GET http://localhost:8000/health - MCP:
POST http://localhost:8000/mcp(withX-API-Key: <your-key>)
5. Docker
cp .env.example .env
# Edit .env — set MCP_API_KEY and adjust MCP_PORT as needed
# Build the image
docker build -t hbiz-devops-mcp:latest .
# Start the container
docker compose up -d
# Verify
curl http://localhost:8001/health
# {"status":"healthy","server":"hBiz-DevOps MCP Server","version":"1.0.0"}
# View live logs
docker logs hbiz-devops-mcp -f
The server is exposed on the port defined by MCP_PORT (default 8001); the MCP endpoint is
http://localhost:8001/mcp.
Stop:
docker compose down
6. VS Code MCP Configuration
Create .vscode/mcp.json in any consumer workspace (this file is committed
on purpose — it contains no secrets, only the server URL):
{
"servers": {
"hbiz-devops": {
"type": "http",
"url": "http://localhost:8000/mcp",
"headers": {
"X-API-Key": "${env:HBIZ_MCP_API_KEY}"
}
}
}
}
Then export the key in your shell (or VS Code user env):
export HBIZ_MCP_API_KEY="<your-key>"
For a production deployment, replace http://localhost:8000/mcp with
https://<your-host>/mcp.
7. Environment Variables
Server
| Variable | Default | Description |
|---|---|---|
DOCKER_IMAGE |
hbiz-devops-mcp:latest |
Docker image name used in docker-compose.yml |
ENVIRONMENT |
local |
local / staging / production |
MCP_API_KEY |
(required) | API key clients must send as X-API-Key |
MCP_SERVER_NAME |
hBiz-DevOps MCP Server |
Display name surfaced to MCP clients |
MCP_HOST |
0.0.0.0 |
Bind address |
MCP_PORT |
8000 |
Bind port (exposed on host) |
CONTENT_DIR |
content |
Path (relative or absolute) to content root |
Logging
| Variable | Default | Description |
|---|---|---|
LOG_LEVEL |
INFO |
DEBUG / INFO / WARNING / ERROR |
LOG_FORMAT |
%(asctime)s - %(name)s - %(levelname)s - %(message)s |
Python logging format string |
LOG_FILE_ENABLED |
false |
Set true to also write logs to a file |
LOG_FILE_PATH |
logs/mcp_logs.log |
Path to the log file (created automatically) |
LOG_FILE_MAX_BYTES |
10485760 |
Max file size before rotation (default 10 MB) |
LOG_FILE_BACKUP_COUNT |
5 |
Number of rotated backup files to keep |
All variables can be overridden via .env or process environment.
8. MCP Tools
| Tool | Args | Returns |
|---|---|---|
fetch_devops_content |
query: str, content_type?: str, tags?: list[str] |
Matching items rendered as markdown |
list_devops_categories |
(none) | Sorted list of categories |
list_devops_content |
category?: str, content_type?: str |
Markdown table of metadata |
get_devops_content_by_id |
content_id: str |
Single item rendered as markdown |
All tools are read-only (readOnlyHint: true, destructiveHint: false).
9. Adding New Content
Create a Markdown file under
content/<category>/<type>/<id>.mdwhere:<category>∈python | ansible | terraform | docker | kubernetes | cicd | general<type>∈skills | instructions | prompts | policies | rules
Add the required frontmatter:
--- id: kebab-case-unique-id title: Human-Readable Title type: skill # skill | instruction | prompt | policy | rule category: python # must match the folder name tags: - python # first tag should be the category - clean-arch version: "1.0" # quoted string --- # Title Body markdown here...Restart the server (content is loaded once at startup).
id values must be globally unique across the whole tree — duplicates
will fail loading with ContentLoadError.
10. Project Structure
hbiz-devops-mcp/
├── main.py # Uvicorn entry point
├── pyproject.toml # Poetry deps
├── requirements.txt # pip-installable mirror
├── Dockerfile
├── .dockerignore
├── docker-compose.yml
├── .env.example # Template — copy to .env
├── config/
│ └── settings.py # Pydantic settings
├── src/
│ ├── core/
│ │ ├── content_loader.py # Filesystem → ContentItem[]
│ │ ├── content_filter.py # Pure filtering logic
│ │ └── exceptions.py
│ ├── models/
│ │ └── content.py # ContentMeta, ContentItem, ContentType
│ ├── services/
│ │ └── content_service.py # Caches + exposes content
│ └── utils/
│ ├── auth.py # APIKeyMiddleware
│ └── logger.py # stdout + optional rotating file handler
├── mcp_server/
│ ├── app.py # Starlette + FastMCP wiring
│ └── tools.py # @mcp.tool definitions
├── content/ # The DevOps knowledge base
│ ├── python/ ansible/ terraform/ docker/ kubernetes/ cicd/ general/
├── logs/ # Created at runtime (gitignored)
│ └── mcp_logs.log # Only present when LOG_FILE_ENABLED=true
└── tests/
├── conftest.py
├── core/
└── services/
11. Dependency Management
Source of truth:
pyproject.toml(Poetry).requirements.txtis regenerated for environments without Poetry:poetry export -f requirements.txt -o requirements.txt --without-hashesPin major + minor (
^1.0). Update viapoetry updateand run the full test suite before committing the new lockfile.Security:
poetry run pip-audit(orsafety check) on every PR.
12. Logging
By default logs are written to stdout only and are visible via:
# Local run
python main.py
# Docker
docker logs hbiz-devops-mcp
docker logs hbiz-devops-mcp -f # follow/live
Enable file logging
Set the following in .env and restart the server:
LOG_FILE_ENABLED=true
LOG_FILE_PATH=logs/mcp_logs.log
LOG_FILE_MAX_BYTES=10485760 # 10 MB per file
LOG_FILE_BACKUP_COUNT=5 # keep last 5 rotated files
The logs/ directory is created automatically. Files rotate once they reach
LOG_FILE_MAX_BYTES and up to LOG_FILE_BACKUP_COUNT backups are retained
(mcp_logs.log, mcp_logs.log.1, … mcp_logs.log.5). Both stdout and file
logging are active simultaneously when file logging is enabled.
logs/is gitignored — log files are never committed to source control.
13. Testing with Postman
The MCP server speaks JSON-RPC 2.0 over HTTP POST. Every request goes to
http://localhost:8001/mcp with the X-API-Key header set.
Required headers (all requests)
| Header | Value |
|---|---|
Content-Type |
application/json |
Accept |
application/json, text/event-stream |
X-API-Key |
your MCP_API_KEY value |
Step 1 — Verify the server is up
GET http://localhost:8001/health
Expected response:
{"status": "healthy", "server": "hBiz-DevOps MCP Server", "version": "1.0.0"}
Step 2 — Initialize the MCP session
POST http://localhost:8001/mcp
Body:
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": { "name": "postman", "version": "1.0" }
}
}
Copy the
mcp-session-idvalue from the response headers — you must include it asmcp-session-id: <value>in all subsequent requests.
Step 3 — List available tools
POST http://localhost:8001/mcp
Add header: mcp-session-id: <value from step 2>
Body:
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {}
}
Step 4 — Call a tool
List all categories
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "list_devops_categories",
"arguments": {}
}
}
Search content
{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "fetch_devops_content",
"arguments": {
"query": "docker",
"content_type": "skill"
}
}
}
List content by category
{
"jsonrpc": "2.0",
"id": 5,
"method": "tools/call",
"params": {
"name": "list_devops_content",
"arguments": {
"category": "python"
}
}
}
Fetch a specific item by ID
{
"jsonrpc": "2.0",
"id": 6,
"method": "tools/call",
"params": {
"name": "get_devops_content_by_id",
"arguments": {
"content_id": "python-clean-architecture"
}
}
}
Step 5 — Verify auth rejection
Send any request without the X-API-Key header (or with a wrong value).
Expected response: HTTP 401
{"detail": "Unauthorized"}
14. Unit Tests
# Run all tests
poetry run pytest -v
# With coverage
poetry run pytest --cov=src --cov-report=term-missing
# A single file
poetry run pytest tests/core/test_content_loader.py -v
Tests use sample markdown fixtures created in tmp_path — they do not
read the production content/ tree.
Installing DevOps
This server has no published package — it is built from source. Open the repository and follow its README.
▸ github.com/IranUdesha/DevOps-MCP-ServerFAQ
Is DevOps MCP free?
Yes, DevOps MCP is free — one-click install via Unyly at no cost.
Does DevOps need an API key?
No, DevOps runs without API keys or environment variables.
Is DevOps hosted or self-hosted?
Self-hosted: the server runs locally on your machine via the install command above.
How do I install DevOps in Claude Desktop, Claude Code or Cursor?
Open DevOps on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.
Related MCPs
GitHub
PRs, issues, code search, CI status
by GitHubFilesystem
Secure file operations with configurable access controls.
Memory
Knowledge graph-based persistent memory system.
Template MCP Server
A CLI tool to create a new Model Context Protocol server project with TypeScript support, dual transport options, and an extensible structure
by mcpdotdirectCompare DevOps with
Not sure what to pick?
Find your stack in 60 seconds
Author?
Embed badge for your README
Browse similar
All development MCPs
