Skip to main content
Glama
nunombispo

dokku-mcp

by nunombispo
README.md
# dokku-mcp

FastMCP server that lets Cursor, Claude Desktop, and other MCP clients inspect and manage a [Dokku](https://dokku.com/) host over SSH — structured tools, shared session, read-only defaults, and explicit guardrails so “convenient” does not become unrestricted shell access.

Stay in the agent chat (“is `api` running, and show the last fifty log lines”) instead of SSH-and-paste.

## Features

- **Read-only by default** (`DOKKU_MCP_MODE=read-only`)
- Shared `asyncssh` session (not a new connection per tool call)
- Structured parsers for Dokku CLI output (prefer `--format json` when available)
- App allowlist / denylist
- Config secrets masked unless `reveal_secrets=True`
- Mutating tools require `read-write` mode; restart / scale / set-config also need `confirm=True`
- No free-form `dokku_exec` — one Dokku command, one tool, one parser
- `destroy_app` is **not** included

## Install

```bash
# From source (development)
uv sync

# Run from this checkout without a global install
uv run dokku-mcp
```

When published to PyPI: `uvx dokku-mcp` or `pip install dokku-mcp`.

## Quick start

1. Confirm Dokku SSH works the forced-command way (this is what the server uses):

   ```bash
   ssh dokku@your-host apps:list
   ```

2. Copy [`.env.example`](.env.example) or set the same variables in your MCP client `env` block.

3. Add the server to Cursor or Claude Desktop (see below), reload MCP, and ask: “What apps are on my Dokku host?”

If the `dokku` server does not appear under MCP tools, check the MCP panel logs — bad host, key path, or a failed `uv` start show up there before any chat error.

## Configuration

| Variable | Default | Description |
|---|---|---|
| `DOKKU_HOST` | *(required)* | Dokku SSH hostname |
| `DOKKU_SSH_USER` | `dokku` | SSH user (Dokku convention) |
| `DOKKU_SSH_KEY_PATH` | *(agent/defaults)* | Path to private key |
| `DOKKU_SSH_PORT` | `22` | SSH port |
| `DOKKU_MCP_MODE` | `read-only` | `read-only` or `read-write` |
| `DOKKU_APP_ALLOWLIST` | empty | Comma-separated apps; empty = all |
| `DOKKU_APP_DENYLIST` | empty | Comma-separated apps always blocked |

Dokku’s SSH user uses a forced command: this server sends plugin invocations such as `apps:list`, **not** `dokku apps:list`.

## Tools

### Read-only

| Tool | Dokku command | Notes |
|---|---|---|
| `list_apps` | `apps:list` | Filtered by allow/deny lists |
| `app_report` | `ps:report <app>` | Process / running state |
| `app_config` | `config:export <app>` | Secrets masked by default |
| `app_logs` | `logs <app> -n <n>` | Default 50 lines, hard max 500 |
| `app_domains` | `domains:report <app>` | VHOST report |
| `app_url` | derived | HTTPS URLs from domains |

### Mutating (gated)

| Tool | Dokku command | Guards |
|---|---|---|
| `create_app` | `apps:create` | `read-write` + name validation |
| `set_config` | `config:set` | `read-write` + `confirm=True` (values never echoed back) |
| `restart_app` | `ps:restart` | `read-write` + `confirm=True` |
| `scale_app` | `ps:scale` | `read-write` + `confirm=True` |

`confirm` must be passed explicitly on the tool call — it is never inferred from chatty assent.

### Example prompts

| You type | Tools |
|---|---|
| “What apps are on my Dokku host?” | `list_apps` |
| “Is `api` running, and what’s its public URL?” | `app_report`, `app_url` |
| “Show the last 50 log lines for `api`.” | `app_logs` |
| “Which env keys does `api` have? Don’t show secrets.” | `app_config` |

## MCP client setup

### Cursor

Add to `~/.cursor/mcp.json` or the project `.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "dokku": {
      "command": "uv",
      "args": [
        "run",
        "--directory",
        "/absolute/path/to/dokku-mcp-server",
        "dokku-mcp"
      ],
      "env": {
        "DOKKU_HOST": "dokku.example.com",
        "DOKKU_SSH_USER": "dokku",
        "DOKKU_SSH_KEY_PATH": "/home/YOU/.ssh/id_ed25519",
        "DOKKU_MCP_MODE": "read-only"
      }
    }
  }
}
```

### Cursor / Claude Desktop

Same `env` block with:

```json
"command": "uvx",
"args": ["dokku-mcp"]
```

See [`examples/cursor_mcp.json`](examples/cursor_mcp.json) and [`examples/claude_desktop_config.json`](examples/claude_desktop_config.json).

### Local entrypoints

```bash
uv run dokku-mcp
python -m dokku_mcp
fastmcp run dokku_mcp.server:mcp
```

## Development

```bash
uv sync --all-extras
uv run pytest -m "not integration" -q

# Live smoke test against your Dokku box:
DOKKU_HOST=... DOKKU_SSH_KEY_PATH=... uv run pytest -m integration -q
```

Layout: `src/dokku_mcp/` — `server.py`, `ssh.py`, `config.py`, `parsers.py`, and `tools/` (one module per Dokku concern).

## Security notes

- Prefer `read-only` for day-to-day LLM access.
- Use allowlists on shared hosts; denylist always wins.
- Prefer an SSH agent or default `IdentityFile` over embedding private key paths when you can.
- Treat `reveal_secrets=True` and `read-write` as elevated privileges.
- There is no open-ended shell tool; that is intentional.

## Contributing

Issues and PRs welcome — especially fixture captures from real Dokku versions and extra read-only reports that fit the “one command, one tool, one parser” rule.

## License

MIT — see [LICENSE](LICENSE).

TDQS

A3.9/5.0

Scored across 10 tools

Disambiguation4/5

Most tools are clearly distinct: list_apps is the only app-level listing, app_report/app_config/app_logs/app_domains/app_url each target a different aspect of an app, and the write tools (create_app, set_config, restart_app, scale_app) have distinct actions. The only mild overlap is app_domains vs app_url, since app_url is a convenience wrapper over domains:report, but the descriptions make the relationship clear.

Naming Consistency4/5

The naming pattern is mostly consistent: read operations use app_* (app_report, app_config, app_logs, app_domains, app_url) plus list_apps, and write operations use verb_app (create_app, set_config, restart_app, scale_app). The minor inconsistency is that list_apps breaks the app_* pattern and set_config/restart_app/scale_app use verb_app rather than app_verb, but the overall convention is still predictable.

Tool Count5/5

10 tools is well-scoped for a Dokku MCP server. The read-only surface covers the most common inspection needs (list, report, config, logs, domains, URL), and the write surface covers the core app lifecycle operations (create, config, restart, scale). No tool feels redundant or unnecessary.

Completeness4/5

The tool surface covers the primary Dokku workflows: listing/creating apps, inspecting app state/config/logs/domains, updating config, restarting, and scaling. Obvious gaps include app deletion, app destruction, and process management (e.g. ps:scale is covered but ps:restart is not), but these are reasonable omissions for a safe MCP server and agents can work around them.

Maintenance

ActivityMaintained
ResponsivenessNo issues