使用说明
portainer-stacks
An MCP server for managing Docker Compose stacks on a Portainer instance. Built against the Portainer 2.39.0 API spec. Exposes 12 typed tools for common stack and container operations plus a generic fallback for full API access.
For broader Portainer coverage beyond stack management, see the official portainer/portainer-mcp.
Why this server?
- Minimal and focused — purpose-built for stack management
- Windows, Linux & macOS (arm64 and x86_64)
- Single static binary — no runtime, no
node_modules, trivial to install from pre-built releases - Low memory footprint — a few MB vs Node's ~30-50MB baseline; ideal for a process that sits idle between tool calls
- Instant startup — no module loading delay; relevant since MCP hosts may spawn/kill the process per session
- MCP-native — includes tool annotations, prompts, and resources
Features
- List, inspect, create, update, and delete compose stacks
- Start, stop, and git-redeploy stacks
- Restart or recreate individual Docker containers (with optional image re-pull)
- List environments/endpoints
- Generic request tool for any Portainer API endpoint — if a dedicated tool doesn't cover your use case, the fallback
portainer_requesttool gives full access to the entire Portainer API - API key authentication (no login flow needed)
- Optional self-signed certificate support
Installation
Docker (easiest)
A multi-arch image (linux/amd64, linux/arm64) is published to GHCR. No local install step — just reference it from your MCP client config (see Usage).
docker pull ghcr.io/guthman/portainer-mcp:latest
Pin to a specific version for reproducibility, e.g. ghcr.io/guthman/portainer-mcp:0.4.1.
Pre-built binaries
Check Releases for pre-built binaries (Windows, Linux musl, macOS — both x86_64 and arm64).
From source
git clone https://github.com/Guthman/portainer-mcp.git
cd portainer-mcp
cargo build --release
The binary will be at target/release/portainer-stacks (or portainer-stacks.exe on Windows).
Verifying what you run
When you configure any MCP server, you're telling your AI assistant to run a binary on your machine with whatever permissions you give it. If that binary gets swapped out — a bad download, a compromised update, or something modifying it on disk — the assistant will run the replacement without question. Most MCP configurations don't have a built-in way to pin to a specific binary hash, so it's worth verifying the file yourself.
This applies to all MCP servers, not just this one.
Release binaries for this project include GitHub Artifact Attestations — signed provenance records that confirm a binary was built by this repo's CI from a specific commit. To verify a downloaded artifact:
gh attestation verify portainer-stacks-v*.tar.gz --owner Guthman
If verification passes, the binary is untampered and matches what the source code produced. It doesn't guarantee the code itself is safe — you should still review what you run — but it closes the gap between "code in the repo" and "binary on your machine."
Configuration
Set these environment variables:
| Variable | Required | Default | Description |
|---|---|---|---|
PORTAINER_API_KEY |
Yes | — | Portainer API key (how to create one) |
PORTAINER_URL |
No | http://localhost:9000 |
Portainer instance URL |
PORTAINER_INSECURE |
No | false |
Set to true to accept self-signed TLS certificates |
PORTAINER_ENV_DISPLAY |
No | masked |
How env var values appear in responses: masked (all hidden), filtered (sensitive redacted), full (all visible) |
PORTAINER_SENSITIVE_NAMES |
No | — | Comma-separated env var names to add as sensitive in filtered mode |
PORTAINER_VISIBLE_NAMES |
No | — | Comma-separated env var names to force visible in filtered mode |
Environment variable display modes
Stack objects and Docker container inspect output include environment variables that often contain secrets (passwords, API keys, tokens, connection strings). By default, all values are masked before they reach the LLM. The same mode applies to env var lists in portainer_request responses (Docker Config.Env, Swarm service specs, stack and edge stack env, Kubernetes container env); other fields in those responses are returned as-is.
| Mode | Behavior |
|---|---|
masked |
All env var values → [MASKED] |
filtered |
Names matching sensitive patterns → [REDACTED], others shown |
full |
All values in cleartext (use with caution) |
In filtered mode, names matching built-in patterns (PASSWORD, SECRET, TOKEN, etc.) and any names listed in PORTAINER_SENSITIVE_NAMES are redacted. Names in PORTAINER_VISIBLE_NAMES override both, so you can exempt specific variables. Priority: explicit visible > explicit sensitive > built-in pattern.
Use the configure-env-display prompt to scan your stacks and get personalized guidance.
Usage
Via Docker
The same config works for any MCP host (Claude Desktop, Claude Code, Cursor, VS Code, etc.). Docker must be installed and running on the machine where the MCP client launches the server.
{
"mcpServers": {
"portainer": {
"command": "docker",
"args": [
"run", "--rm", "-i",
"-e", "PORTAINER_URL",
"-e", "PORTAINER_API_KEY",
"-e", "PORTAINER_INSECURE",
"-e", "PORTAINER_ENV_DISPLAY",
"ghcr.io/guthman/portainer-mcp:latest"
],
"env": {
"PORTAINER_URL": "https://your-portainer:9443",
"PORTAINER_API_KEY": "your-api-key",
"PORTAINER_INSECURE": "true",
"PORTAINER_ENV_DISPLAY": "masked"
}
}
}
}
The -i flag is required (MCP speaks over stdio). --rm cleans up the container when the client disconnects. Forwarding env vars via -e NAME (without =value) pulls them from the env block above — keeping secrets out of args, where some hosts may log them.
With Claude Desktop
Add to your Claude Desktop MCP config (claude_desktop_config.json):
{
"mcpServers": {
"portainer": {
"command": "path/to/portainer-stacks",
"env": {
"PORTAINER_URL": "https://your-portainer:9443",
"PORTAINER_API_KEY": "your-api-key",
"PORTAINER_INSECURE": "true",
"PORTAINER_ENV_DISPLAY": "masked"
}
}
}
}
With Claude Code
Add to your .mcp.json:
{
"mcpServers": {
"portainer": {
"command": "path/to/portainer-stacks",
"env": {
"PORTAINER_URL": "https://your-portainer:9443",
"PORTAINER_API_KEY": "your-api-key",
"PORTAINER_INSECURE": "true",
"PORTAINER_ENV_DISPLAY": "masked"
}
}
}
}
Tools
| Tool | Description |
|---|---|
list_endpoints |
List environments/endpoints — call this first to get endpoint IDs |
list_stacks |
List all stacks, optionally filtered |
get_stack |
Get a single stack by ID |
get_stack_file |
Get the compose file content of a stack |
create_stack |
Create a new compose stack from file content |
update_stack |
Update a stack's compose file, env vars, or settings |
delete_stack |
Delete a stack |
start_stack |
Start a stopped stack |
stop_stack |
Stop a running stack |
redeploy_git_stack |
Redeploy a git-based stack (pull latest and redeploy) |
restart_container |
Restart a running Docker container |
recreate_container |
Recreate a container (destroy + create), optionally re-pulling its image |
portainer_request |
Generic API request for any Portainer endpoint |
Typical workflow
list_endpoints— get theendpoint_idfor your environmentlist_stacks— see your stacksget_stack_file— inspect a stack's compose fileupdate_stack/redeploy_git_stack— make changes
Roadmap
- Support for multiple Portainer API versions
- Automatic tool updates when new Portainer API versions are released
- Support for more auth methods or credential handling? Better prompts? Let me know!
License
MIT