An MCP (Model Context Protocol) server that exposes Komodo — a Docker/container management and deployment system — to AI assistants.
- 35 tools across read / execute / write categories
- Streamable HTTP transport (default) per the latest MCP spec, plus legacy SSE and stdio
- Per-session
McpServer— concurrent clients are isolated - Official
komodo_client2.1.1 for the upstream API contract - Bearer-token auth with constant-time compare; loopback-only fallback when no token is configured
- DNS-rebinding defense via a default
Hostallow-list and an optionalOriginallow-list - Helmet security headers; CORS allow-list
- Pino structured logs with
Authorization/X-Api-Secret/X-Api-Keyredaction - Graceful shutdown on
SIGTERM/SIGINT - Strict input schemas: bounded
tail,terms,compose_contents, andcontents; update configs reject keys beginning with API-key, API-secret, password, secret, webhook-secret, or token variants - Shared upstream adapter with Undici pooling, an absolute timeout, a 16 MiB response cap,
p-limitconcurrency control, and secret redaction; requests are not retried
git clone https://github.com/nicolasestrem/komodo-mcp.git
cd komodo-mcp
export KOMODO_ADDRESS=http://host.docker.internal:9120
export KOMODO_API_KEY='YOUR_KEY'
export KOMODO_API_SECRET='YOUR_SECRET'
export MCP_AUTH_TOKEN="$(openssl rand -hex 32)"
docker compose up -dPlain docker compose automatically loads the local-development override and passes these environment variables into the container. The container binds 0.0.0.0:3113 internally but docker-compose.yml publishes the port only to host loopback (127.0.0.1:3113:3113). Docker requests still cross a container-network boundary, so use the generated bearer token. Front the service with a reverse proxy when you need network access.
Local npm process / loopback (no token needed when MCP_AUTH_TOKEN is unset):
{
"mcpServers": {
"komodo": {
"type": "http",
"url": "http://127.0.0.1:3113/mcp"
}
}
}Docker (token required):
{
"mcpServers": {
"komodo": {
"type": "http",
"url": "http://127.0.0.1:3113/mcp",
"headers": { "Authorization": "Bearer ${MCP_AUTH_TOKEN}" }
}
}
}For a networked deployment, replace the URL with the TLS endpoint exposed by your reverse proxy.
npm install
npm run build
npm start # default streamable HTTP on 127.0.0.1:3113
npm run dev:sse # legacy SSE transport with a dev token| Variable | Description | Default |
|---|---|---|
KOMODO_ADDRESS |
Komodo Core URL (http(s) only; trailing slash normalized); also reads from KOMODO_ADDRESS_FILE |
required |
KOMODO_API_KEY |
API key — also reads from KOMODO_API_KEY_FILE for Docker secrets |
required |
KOMODO_API_SECRET |
API secret — also reads from KOMODO_API_SECRET_FILE |
required |
KOMODO_TIMEOUT_MS |
Absolute per-request timeout | 30000 |
KOMODO_MAX_CONCURRENCY |
In-flight request semaphore | 8 |
KOMODO_MAX_RESPONSE_BYTES |
Maximum upstream response body size | 16777216 (16 MiB) |
| Variable | Description | Default |
|---|---|---|
MCP_TRANSPORT |
streamable (default), sse (legacy), or stdio |
streamable |
MCP_PORT |
HTTP listener port | 3113 |
MCP_BIND_HOST |
Host to bind on | 127.0.0.1 (use 0.0.0.0 inside Docker) |
MCP_AUTH_TOKEN |
Bearer token (also MCP_AUTH_TOKEN_FILE). When unset, only loopback callers are admitted. |
unset |
MCP_ALLOWED_ORIGINS |
Comma-separated Origin allow-list (browser CSRF defense). Empty = no Origin enforcement. |
unset |
MCP_ALLOWED_HOSTS |
Comma-separated Host-header allow-list (DNS-rebinding defense). |
127.0.0.1,localhost |
MCP_MAX_SESSIONS |
Maximum simultaneous HTTP sessions; invalid values fall back to the default | 100 |
MCP_SESSION_IDLE_TIMEOUT_MS |
Idle lifetime for HTTP sessions before cleanup | 1800000 (30 minutes) |
LOG_LEVEL |
Pino log level | info |
- Open the Komodo web UI
- Go to Settings → API Keys
- Click Create API Key, copy the key and secret
This server holds Komodo admin credentials. A successful tool call can deploy code, prune systems, or destroy stacks on every Komodo-managed host. Treat it as privileged.
The defaults aim for "secure by accident":
- Bind is
127.0.0.1unless explicitly opened. MCP_AUTH_TOKENis required for any non-loopback caller.- The default
Hostallow-list blocks DNS rebinding even if a browser is tricked into reaching the loopback port;MCP_ALLOWED_ORIGINSadds optional browser Origin enforcement. - All requests, including loopback, go through Helmet + CORS.
- Errors emitted to MCP clients have the API key/secret scrubbed from upstream bodies.
For non-loopback deployments use a reverse proxy that terminates TLS, set a random MCP_AUTH_TOKEN (openssl rand -hex 32), and configure MCP_ALLOWED_HOSTS/MCP_ALLOWED_ORIGINS for your domain. See docs/DEPLOYMENT.md and docs/RUNBOOK.md.
35 tools are registered from a single declarative table in src/tools/registry.ts. Read tools carry readOnlyHint and idempotentHint; start/stop/restart and pull operations carry idempotentHint; prune/destroy/delete and komodo_write_stack_contents carry destructiveHint. Exact operation mappings and schemas are in docs/API.md.
| Tool | Description |
|---|---|
komodo_list_servers |
List all connected servers |
komodo_list_stacks |
List stacks with state |
komodo_list_deployments |
List deployments |
komodo_get_stack |
Stack details |
komodo_get_stack_log |
Stack deployment logs |
komodo_get_container_log |
Container logs (tail 1–5000, default 100) |
komodo_list_containers |
Containers on a server |
komodo_inspect_container |
Inspect a container |
komodo_get_system_stats |
CPU/memory/disk for a server |
komodo_list_images |
Docker images |
komodo_list_networks |
Docker networks |
komodo_list_volumes |
Docker volumes |
komodo_get_alerts |
Komodo alerts |
komodo_search_logs |
Search container logs (1–20 terms, max 256 chars each) |
komodo_get_stack_services |
Stack services summary (requires stack) |
| Tool | Hint | Description |
|---|---|---|
komodo_deploy_stack |
non-idempotent | Deploy/redeploy a stack |
komodo_start_stack |
idempotent | Start a stopped stack |
komodo_stop_stack |
idempotent | Stop a running stack |
komodo_restart_stack |
idempotent | Restart a stack |
komodo_destroy_stack |
destructive | Stop and remove |
komodo_pull_stack |
idempotent | Pull latest images |
komodo_start_container |
idempotent | Start a container |
komodo_stop_container |
idempotent | Stop a container |
komodo_restart_container |
idempotent | Restart a container |
komodo_prune_images |
destructive | Prune unused images |
komodo_prune_networks |
destructive | Prune unused networks |
komodo_prune_system |
destructive | Full Docker system prune |
| Tool | Hint | Description |
|---|---|---|
komodo_create_stack |
non-idempotent | Create a stack |
komodo_update_stack |
non-idempotent | Update stack config (rejects secret-like keys) |
komodo_delete_stack |
destructive | Delete a stack |
komodo_write_stack_contents |
destructive | Overwrite compose contents; requires stack, file_path, and contents (max string length 256,000) |
komodo_create_server |
non-idempotent | Add a server |
komodo_update_server |
non-idempotent | Update server config (rejects secret-like keys) |
komodo_delete_server |
destructive | Remove a server |
komodo_rename_stack |
non-idempotent | Rename a stack |
- "List all my Komodo stacks"
- "Show me the logs for the nginx stack"
- "Restart the wordpress stack"
- "What containers are running on my server?"
- "Deploy the staging stack"
npm install
npm run lint # biome
npm run build # tsc
npm test # node:test suites (auth/secret/tool/format/smoke)
npm run dev # tsc --watchsrc/
├── index.ts # Express factory, transports, auth/Origin/Host gates, SIGTERM
├── server.ts # createServer({ client? }) — DI-friendly factory
├── komodo-client.ts # Official client adapter: shared Undici pool, timeout/size/concurrency guards
└── tools/
├── registry.ts # Declarative TOOLS table + registerAll(server, client)
└── utils.ts # formatResult + toolHandler (errors → MCP isError)
See docs/ARCHITECTURE.md for diagrams and the request-flow walk-through.
The server uses the official komodo_client 2.1.1 package for the Komodo Core API. It sends JSON POST requests to /read/<Operation>, /write/<Operation>, or /execute/<Operation>, with the operation parameters as the request body and API-key headers:
curl -X POST http://your-komodo:9120/read/ListStacks \
-H "Content-Type: application/json" \
-H "X-Api-Key: YOUR_KEY" \
-H "X-Api-Secret: YOUR_SECRET" \
-d '{}'Streamable HTTP and legacy SSE each create a separate McpServer per session while sharing one upstream adapter. Idle sessions are closed after MCP_SESSION_IDLE_TIMEOUT_MS; requests carrying an unknown session ID receive HTTP 404. CORS exposes the mcp-session-id response header to allowed browser clients.
MIT — see LICENSE.
Dependency note: the official komodo_client package declares GPL-3.0. Redistribution scenarios should receive a separate licensing review; this note is not a legal conclusion.