infrastructure-mcp
Infrastructure MCP Server
Give Claude Code, Cursor, and other AI agents safe access to your real infrastructure — without giving them raw SSH access.
A self-hosted Model Context Protocol server that exposes high-level, auditable tools (disk_usage(), docker_restart(), grafana_alerts(), ...) instead of execute(command). Every call is inventory-driven, structured, and logged — the agent reasons about infrastructure; this server is the only thing that actually touches it.
Status: early, actively developed. See Roadmap for what's implemented and what's coming.
Manage: ✓ Linux ✓ Docker ✓ Kubernetes ✓ Proxmox ✓ Grafana ✓ Cisco (MikroTik, UniFi, Prometheus/Loki, Home Assistant, and more are on the Roadmap — not shipped yet.)
Works with: ✓ Claude Code ✓ Cursor ✓ OpenAI Codex ✓ Gemini CLI ✓ Windsurf ✓ Hermes (MCP is an open, client-agnostic protocol — any spec-compliant client works; these are just the ones people actually use it with.)
Raw SSH access | Infrastructure MCP | |
Audit trail | ❌ | ✅ structured log per call ( |
Input validation | ❌ | ✅ every parameter validated, no shell interpolation |
Structured output | ❌ free text | ✅ JSON, every response |
Inventory-aware | ❌ agent picks the host | ✅ targets resolved by name/tag only |
Blast-radius control | ❌ | ✅ read-only by default, mutations require |
Credential exposure | ❌ agent sees them | ✅ credentials never leave the adapter layer |
Quick Start
git clone https://github.com/radumalica/infrastructure-mcp.git
cd infrastructure-mcp
go build -o bin/infrastructure-mcp ./cmd/server
cp configs/inventory.example.yaml configs/inventory.yaml # fill in your own targetsPoint your MCP client at the binary — see Connecting an AI Agent for Docker and remote-HTTP setups too:
{
"mcpServers": {
"infrastructure": {
"command": "/path/to/bin/infrastructure-mcp",
"args": ["-inventory", "/path/to/configs/inventory.yaml"]
}
}
}Example Conversation
You: My archive server feels slow, and I'm seeing errors in Grafana. Can you find out why?
Claude, using Infrastructure MCP:
1. cpu_usage(server: "archive") → 92% used, status: "warning"
2. docker_ps(server: "archive") → prometheus: "Restarting (1) 4 seconds ago"
3. docker_logs(server: "archive", container: "prometheus") → "panic: too many open files"
4. grafana_alerts(instance: "main") → firing: HighDiskUsage on archive:/
Claude: Root cause found — archive is at 91% disk usage, which is causing Prometheus
to crash-loop on startup ("too many open files" from a corrupted WAL it can't
compact into the remaining space). Free up space on `/` (or expand it), then run:
docker_restart(server: "archive", container: "prometheus", confirm: true)Four tool calls, one root cause, zero raw shell access. Every call above is inventory-driven, logged, and returns structured JSON — see Examples for the actual response shapes.
Current Features
Tools implemented and tested against the real MCP protocol so far:
Core (v0.1)
list_servers— list inventory targets, optionally filtered by tagrun_command— run an arbitrary command against an inventory targetuptime— uptime and load averagesdisk_usage— per-filesystem usage with warning/critical severitymemory_usage— memory utilization with warning/critical severity
Linux (v0.2)
failed_services— systemd units in the failed statecpu_usage— aggregate CPU utilization (Proxmox/KVM guest-time aware)reboot_required— pending-reboot detection (marker file + kernel comparison)running_processes— top processes by CPU usagejournal_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 imagesdocker_stats— point-in-time resource usage snapshotdocker_logs— recent combined stdout/stderr log linesdocker_restart— restart a container (destructive; requiresconfirm: true)
Kubernetes (v0.4)
kubectl_get_pods— list pods, optionally scoped to a namespacekubectl_logs— fetch a pod/container's log lineskubectl_events— recent cluster events, most recent firstkubectl_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 APIgrafana_dashboards— search dashboards by title text and/or taggrafana_annotations— annotations, optionally scoped to a time range and/or tagsgrafana_query— raw query against one datasource (PromQL/LogQL/SQL/...); response is a datasource-specific passthrough, not normalized
Proxmox (v0.6)
proxmox_nodes— list cluster nodes with status and resource usageproxmox_vms— list QEMU VMs and LXC containers on a nodeproxmox_tasks— recent tasks on a node, most recent firstproxmox_start_vm— start a VM/container (state-changing; requiresconfirm: true)proxmox_stop_vm— force-stop a VM/container (destructive; requiresconfirm: true)proxmox_snapshot— take a VM/container snapshot (state-changing; requiresconfirm: true)
Networking — Cisco (v0.7, partial: MikroTik/UniFi remain on the Roadmap)
cisco_backup— fetch the running configuration, as-iscisco_version— software version, hostname, and uptimecisco_interfaces— interface list with IP address, admin/link status, and line protocolcisco_inventory— hardware components (chassis, modules, PSUs) with PID/VID/serial numbercisco_logs— buffered syslog messages, optionally capped to the most recent N lines
Cross-cutting, since v0.1
Legacy network device support: Telnet transport, SSH legacy-crypto negotiation, transparent per-target protocol dispatch, SSH public-key auth for routers/switches
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 useDocker image (multi-arch, GHCR) + Docker Compose, with a self-check
-healthcheckmode for shell-less containersCI/CD: PR-gated build/vet/lint/race-tested-CI, automatic semantic versioning + changelog + image publish + Trivy scan on merge to
main— see CI/CD
See Roadmap for what's next.
Table of Contents
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 MetalThree layers, wired top-down:
MCP layer (
mcp/tools/,mcp/prompts/,mcp/resources/) — registers tools with the MCP SDK. Tools call adapters; they never talk to infrastructure directly.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.Foundation —
internal/inventory/(YAML config, validated, env-var expansion for secrets) andinternal/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.
Architectural decisions and the reasoning behind them are recorded as ADRs — see docs/adr/.
Technology Stack
Concern | Choice |
Language | Go 1.26+ |
MCP SDK | Official Model Context Protocol Go SDK |
Configuration | YAML ( |
Logging |
|
SSH |
|
HTTP |
|
Serialization |
|
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/
│ ├── prometheus/, loki/ # planned
│ ├── cisco/
│ ├── mikrotik/, unifi/ # planned
│ └── homeassistant/ # planned
├── mcp/
│ ├── tools/ # one file per MCP tool
│ ├── prompts/
│ └── resources/
├── configs/ # example inventory
├── docs/
│ ├── adr/ # architecture decision records
│ ├── TOOL_REFERENCE.md # every tool's params + example output
│ └── ADDING_AN_ADAPTER.md # how to add a new integration
├── examples/
│ ├── mcp-client-config/ # ready-to-adapt client JSON snippets
│ ├── inventory-snippets/ # focused single-concept YAML fragments
│ └── sample-tool-outputs/ # example JSON responses
├── scripts/
└── tests/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/radumalica/infrastructure-mcp.git
cd infrastructure-mcp
go build -o bin/infrastructure-mcp ./cmd/serverOr run via Docker
Pre-built multi-arch images (linux/amd64, linux/arm64) are published to GHCR on every merge to main (see CI/CD):
docker pull ghcr.io/radumalica/infrastructure-mcp:latestOr with Docker Compose, which wires up the inventory and SSH volume mounts for you — see 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 -dRun 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 testConfiguration
The server is entirely inventory-driven — copy the example inventory and fill in your own targets:
cp configs/inventory.example.yaml configs/inventory.yamlservers:
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:
# token is a Proxmox API token in "user@realm!tokenid=uuid" form
# (Datacenter > Permissions > API Tokens in the PVE UI). Ticket/cookie
# login is not supported.
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-adminSecrets 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.
More focused, single-concept inventory fragments (key-authenticated network devices, multi-instance services, ...) live under examples/inventory-snippets/.
Server flags
./bin/infrastructure-mcp \
-inventory configs/inventory.yaml \
-known-hosts ~/.ssh/known_hosts \
-insecure-ignore-host-key=false \
-transport stdioFlag | Default | Description |
|
| Path to the inventory YAML file |
|
| Path used to verify target SSH host keys |
|
| Skip host key verification entirely — lab/dev only, never production |
|
|
|
|
| Address to listen on when |
|
| Instead of starting the server, GET |
|
| Allow |
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 (/mcpendpoint, 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). 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://<host>: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://<host>: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 — never raw shell access, never credentials.
Ready-to-adapt client config files (stdio and remote-HTTP-via-mcp-remote) live under examples/mcp-client-config/.
Examples
Full request/response walkthroughs for a few representative tools, run
against the inventory shown in Configuration. Values are
synthetic — copy the shape, not the data. Every tool's shape is documented
in docs/TOOL_REFERENCE.md; runnable inventory
fragments and MCP client configs live under examples/.
Check disk space on a server:
disk_usage(server: "archive"){
"server": "archive",
"filesystems": [
{
"filesystem": "/dev/sda1",
"mount_point": "/",
"total_kb": 51475068,
"used_kb": 46853200,
"available_kb": 2003212,
"used_percent": 91,
"status": "critical",
"recommendation": "Free disk space immediately or expand capacity."
}
],
"timestamp": "2026-07-30T09:00:00Z"
}List containers on a Docker host:
docker_ps(server: "archive"){
"server": "archive",
"containers": [
{
"id": "a1b2c3d4e5f6",
"image": "grafana/grafana:11.2.0",
"status": "Up 3 weeks",
"state": "running",
"ports": "0.0.0.0:3000->3000/tcp",
"names": "grafana"
}
],
"timestamp": "2026-07-30T09:00:00Z"
}Restart a container — the confirm-gate pattern every mutating tool uses. The first call, without confirm, is a no-op that reports what would happen:
docker_restart(server: "archive", container: "grafana"){
"server": "archive",
"container": "grafana",
"status": "confirmation_required",
"message": "Set confirm: true to restart this container. No action was taken.",
"timestamp": "2026-07-30T09:00:00Z"
}Only the confirmed call actually restarts it:
docker_restart(server: "archive", container: "grafana", confirm: true){
"server": "archive",
"container": "grafana",
"status": "restarted",
"message": "Container restarted.",
"timestamp": "2026-07-30T09:00:05Z"
}Interfaces on a Cisco switch reached over legacy-crypto SSH (legacy_crypto: true in inventory — see ADR 0005):
cisco_interfaces(device: "old-core-sw"){
"device": "old-core-sw",
"interfaces": [
{ "interface": "Vlan100", "ip_address": "10.0.0.23", "ok": "YES", "method": "NVRAM", "status": "up", "protocol": "up" },
{ "interface": "GigabitEthernet1/0/1", "ip_address": "unassigned", "ok": "YES", "method": "unset", "status": "up", "protocol": "up" }
],
"timestamp": "2026-07-30T09:00:00Z"
}A firing alert from Grafana:
grafana_alerts(instance: "main"){
"instance": "main",
"alerts": [
{
"status": "firing",
"labels": { "alertname": "HighDiskUsage", "instance": "archive", "severity": "critical" },
"starts_at": "2026-07-30T08:00:00Z",
"fingerprint": "9f2a1c7e0b3d4e5f"
}
],
"timestamp": "2026-07-30T09:00:00Z"
}A tool call against an unreachable target — the structured error envelope, never a raw Go error:
uptime(server: "archive"){
"message": "dial tcp 10.0.0.5:22: connect: connection refused",
"recommendation": "Retry the request; if it persists, check network connectivity to the target.",
"retryable": true,
"category": "network"
}Tool Design Philosophy
Never expose raw infrastructure:
execute(command) # avoidPrefer 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 in logs
No secrets in prompts
No shell interpolation — any user-supplied value that reaches a remote command is validated against a strict whitelist pattern first
Validate all inputs
Whitelist inventory — no target is reachable unless it's named in the inventory
Least privilege
Read-only by default
Dangerous actions require explicit confirmation (e.g.
docker_restartrequiresconfirm: trueand is a no-op otherwise)-transport=httpis fail-closed: it refuses to start without a bearer token (MCP_HTTP_TOKEN) unless-allow-anonymous-httpis explicitly passed
Found a security issue? Please report it privately rather than opening a public issue — see Contributing.
Development
Testing standards
Unit tests target ~100% coverage for inventory, adapters, parsing, and validation
Integration tests use Testcontainers / Docker Compose (Ubuntu, Alpine, Grafana, Prometheus, Loki)
Every tool is exercised through a real MCP protocol round-trip (
mcp.NewInMemoryTransports()), not just unit-tested in isolation
Performance goals
SSH/Telnet connection pooling
Parallel execution
Context cancellation everywhere
Streaming responses
Timeout and retry support
Code conventions
Small packages, dependency injection, no globals, prefer interfaces
context.Contexteverywherelog/slogfor loggingAvoid reflection when possible
Functions under ~50 lines when practical
Structured errors, never raw ones
Document exported functions
Never expose credentials to the MCP layer
Never hardcode infrastructure — always resolve through inventory
Adding a new integration? See docs/ADDING_AN_ADAPTER.md for the full walkthrough.
CI/CD
Two GitHub Actions workflows, both required for anyone contributing:
ci.yml — required for every PR into main
Runs on every pull request and on push to main:
test —
go build ./...,go vet ./..., agofmt -lformatting check, andgo test ./... -race -coverlint —
golangci-lintagainst.golangci.yml(errcheck, staticcheck, govet, gosec, and more)
Branch protection on main must be configured by a repo admin (this is a repository setting, not something a workflow file can enforce) to require both the test and lint jobs to pass, and to require a pull request before merging — that gate only takes effect once the setting is turned on.
The release pipeline is designed to work with protected main: it creates and pushes only the release tag, creates the GitHub release from that tag, and opens a separate PR for CHANGELOG.md updates.
release.yml — runs on every merge to main
Version + tag — Cocogitto reads Conventional Commits since the last tag (config:
cog.toml) and computes the next semver. The workflow then creates and pushes thevX.Y.Ztag (no direct commit push tomain). A merge with no releasable commits (e.g. docs/chore-only) skips the rest of the pipeline.Build & scan — builds the Docker image and scans it with Trivy before publishing; a CRITICAL/HIGH finding blocks the release. Results are uploaded to the repo's Security tab either way.
Publish — once the scan is clean, builds and pushes the multi-arch (
linux/amd64,linux/arm64) image toghcr.io/<org>/infrastructure-mcp, taggedlatestandvX.Y.Z.Changelog PR — the workflow regenerates
CHANGELOG.mdand opens an automated PR so changelog updates still go through normal branch protection and review.
Commit messages therefore need to follow Conventional Commits (feat:, fix:, docs:, chore:, ...) — see Contributing.
Roadmap
Shipped: Linux · Docker · Kubernetes · Grafana · Proxmox · Cisco Not started: MikroTik · UniFi · Prometheus/Loki/Tempo/Alertmanager · Home Assistant · AI-composite tools · AWS/Azure/GCP/VMware/...
Implemented versions are listed under Current Features and tracked feature-by-feature in PROGRESS.md. Everything below is not started.
v0.7 — Networking (in progress: Cisco shipped, MikroTik/UniFi remain)
MikroTik: backup, interfaces, routes, firewall, system resources
UniFi: devices, clients, AP status, firmware, statistics
Note: Telnet transport and SSH legacy-crypto support for these vendors already shipped ahead of schedule in v0.1 — only the vendor-specific tools above remain.
v0.8 — Monitoring
Prometheus, Loki, Tempo, Alertmanager
v0.9 — Home Assistant
entitiesdevicesautomationssensorscameras
v1.0 — AI-oriented composite tools
Instead of calling 15 low-level tools, the agent calls one expert tool that composes them:
diagnose_linux()diagnose_docker()diagnose_kubernetes()diagnose_network()diagnose_storage()morning_brief()incident_summary()root_cause_analysis()backup_everything()check_infrastructure()
Further out
AWS, Azure, GCP, VMware, TrueNAS, Ceph, Synology, IPMI, Redfish, OpenStack, Nomad, Consul.
Contributing
Contributions are welcome — this is an early-stage project and there's a lot of surface area left on the roadmap above.
Fork the repo and create a feature branch.
Follow the existing package/adapter pattern: one isolated package per integration under
internal/, a matching set of tools undermcp/tools/, wired intocmd/server/main.go— seedocs/ADDING_AN_ADAPTER.mdfor the full walkthrough.Match the existing conventions: structured errors via
internal/toolerr, structured logging via thewithLoggingwrapper, inventory-driven targets, no shell interpolation of user input.Add tests — unit tests for adapters/parsing, and at least one test that exercises new tools through the real MCP protocol.
Run
go build ./... && go vet ./... && gofmt -l . && go test ./... -race -coverand, if you have it installed,golangci-lint run ./...before opening a PR — this is exactly whatci.ymlruns, and it's a required, non-optional check on every PR intomain(see CI/CD).Use Conventional Commits for your commit messages (
feat:,fix:,docs:,chore:,refactor:,test:,ci:, ...) — the version bump,CHANGELOG.md, and release on merge are generated automatically from them.Open a pull request describing what changed and why. For an architectural decision (not just "add tool #N following the existing pattern"), add an ADR under
docs/adr/.
For larger changes (a new adapter, a new version's worth of tools), consider opening an issue first to discuss the approach.
License
Licensed under the MIT License — free to use, modify, and distribute, including commercially.
A Note on How This Project Is Built
This project is vibe-coded: the vast majority of the implementation — code, tests, commit-by-commit progress — is written by AI (Claude Code), iterating directly against this README as the specification.
The design and architecture are human-authored by the repository owner: the layering (MCP → adapters → foundation), the inventory-driven philosophy, the tool-design rules, the security posture, and the version roadmap were all decided by a person before any code was written. AI implements against that spec; it doesn't set the direction.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/radumalica/infrastructure-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server