portainer-mcp
Integrates with Docker through Portainer, enabling container lifecycle management, image pulling, volume and network operations, and swarm awareness for cluster management.
Offers Laravel-specific debugging tools such as reading Laravel error logs and executing artisan tinker commands within containers of a stack.
Provides tools to manage Portainer environments, including deploying and updating stacks, managing containers, images, volumes, and networks, executing commands, analyzing logs, and inspecting endpoints.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@portainer-mcpdeploy the nginx stack from the docker-compose.yml"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Portainer MCP Server
An MCP (Model Context Protocol) server that gives AI assistants — Claude, Copilot, Cursor, and others — 41 tools to manage Portainer container environments: deploy and update stacks, manage containers/images/volumes/networks, exec commands, analyze logs, debug Laravel apps, and inspect endpoints — all through natural language.
For LLM agents: This server connects via stdio transport. Every tool returns JSON. All mutating operations are audit-logged. Credentials are passed via environment variables, never hardcoded.
Why Use This
Natural language DevOps — Ask your AI assistant to deploy a stack, check container logs, or pull an image.
Swarm-aware — Automatically detects Docker Swarm clusters and uses the correct API.
Safe by default — Input validation, path traversal protection, sensitive field filtering, and force-remove disabled by default.
Works everywhere — Claude Desktop, Claude Code, Cursor, Windsurf, VS Code, Continue.dev.
Quick Start
1. Install
pip install portainer-mcpOr from source:
git clone https://github.com/ginkida/portainer-mcp.git
cd portainer-mcp
pip install -e .2. Configure your AI client
Pick your client below, paste the config, and replace the placeholder values with your Portainer credentials.
Client Configuration
Claude Desktop
File: ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows)
{
"mcpServers": {
"portainer": {
"command": "python3",
"args": ["-m", "portainer_mcp.server"],
"env": {
"PORTAINER_URL": "https://your-portainer:9443",
"PORTAINER_USERNAME": "admin",
"PORTAINER_PASSWORD": "your-password",
"PORTAINER_VERIFY_SSL": "false"
}
}
}
}Claude Code
File: .mcp.json in your project root (project-scope) or ~/.claude.json (user-scope)
{
"mcpServers": {
"portainer": {
"type": "stdio",
"command": "python3",
"args": ["-m", "portainer_mcp.server"],
"env": {
"PORTAINER_URL": "https://your-portainer:9443",
"PORTAINER_USERNAME": "admin",
"PORTAINER_PASSWORD": "${PORTAINER_PASSWORD}",
"PORTAINER_VERIFY_SSL": "false"
}
}
}
}Or via CLI:
claude mcp add portainer -- python3 -m portainer_mcp.serverCursor
File: ~/.cursor/mcp.json (global) or .cursor/mcp.json (project)
{
"mcpServers": {
"portainer": {
"command": "python3",
"args": ["-m", "portainer_mcp.server"],
"env": {
"PORTAINER_URL": "https://your-portainer:9443",
"PORTAINER_USERNAME": "admin",
"PORTAINER_PASSWORD": "your-password",
"PORTAINER_VERIFY_SSL": "false"
}
}
}
}Windsurf
File: ~/.codeium/windsurf/mcp_config.json
{
"mcpServers": {
"portainer": {
"command": "python3",
"args": ["-m", "portainer_mcp.server"],
"env": {
"PORTAINER_URL": "https://your-portainer:9443",
"PORTAINER_USERNAME": "admin",
"PORTAINER_PASSWORD": "your-password",
"PORTAINER_VERIFY_SSL": "false"
}
}
}
}VS Code (GitHub Copilot)
File: .vscode/mcp.json in your workspace
{
"servers": {
"portainer": {
"type": "stdio",
"command": "python3",
"args": ["-m", "portainer_mcp.server"],
"env": {
"PORTAINER_URL": "${input:portainer-url}",
"PORTAINER_USERNAME": "${input:portainer-username}",
"PORTAINER_PASSWORD": "${input:portainer-password}",
"PORTAINER_VERIFY_SSL": "false"
}
}
},
"inputs": [
{ "type": "promptString", "id": "portainer-url", "description": "Portainer base URL" },
{ "type": "promptString", "id": "portainer-username", "description": "Portainer username" },
{ "type": "promptString", "id": "portainer-password", "description": "Portainer password", "password": true }
]
}Continue.dev
File: ~/.continue/config.yaml or .continue/config.yaml
mcpServers:
- name: portainer
type: stdio
command: python3
args:
- -m
- portainer_mcp.server
env:
PORTAINER_URL: "https://your-portainer:9443"
PORTAINER_USERNAME: "admin"
PORTAINER_PASSWORD: "your-password"
PORTAINER_VERIFY_SSL: "false"Environment Variables
Variable | Required | Default | Description |
| Yes | — | Portainer base URL (e.g. |
| Yes | — | Portainer username |
| Yes | — | Portainer password |
| No |
| Default endpoint ID for container/image/stack operations |
| No |
| Set to |
Tools
All 41 tools are listed below with their parameters and descriptions. Every tool returns JSON.
Authentication
Tool | Description |
| Check connection and authentication status. Returns version and instance ID. |
Endpoints (Environments)
Tool | Description |
| List all environments. Returns id, name, type, url, status. |
| Get endpoint details (sensitive fields like TLS certs are filtered). |
Stacks
Tool | Description |
| List all stacks with id, name, type, status, endpoint_id. |
| Get stack details including the docker-compose file content. |
| Deploy a new stack. Auto-detects Swarm vs standalone. |
| Update a stack. Omit compose_content to redeploy existing. |
| Delete a stack. |
| Start a stopped stack. |
| Stop a running stack. |
Containers
Tool | Description |
| List containers. Set |
| Get detailed container info. |
| Start a stopped container. |
| Stop a running container. |
| Restart a container. |
| Remove a container. |
| Get container logs. |
| Server-side regex over logs. Returns only matching lines (with optional context) — saves bandwidth on noisy logs. |
| Point-in-time CPU%, memory, network and block I/O stats (not a stream). |
| Run a shell command inside a running container and return its stdout/stderr + exit code. Audit-logged. |
| Concurrent scan of every running container in a stack for HTTP 4xx/5xx, exceptions, fatal/critical levels, panics, OOM, PHP errors, etc. |
| Read |
| Execute PHP via |
Images
Tool | Description |
| List images with tags and sizes. |
| Get detailed image info. Accepts |
| Pull an image. |
| Remove an image. |
Volumes
Tool | Description |
| List Docker volumes. |
| Get detailed volume info. |
| Create a volume. |
| Remove a volume. |
Networks
Tool | Description |
| List Docker networks with driver, scope and attached container count. |
| Get detailed network info. |
| Create a network. |
| Remove a network. |
| Attach a container to a network. |
| Detach a container from a network. |
System
Tool | Description |
| OS, CPU, memory, container/image counts, swarm state. |
| Per-category disk usage (containers, images, volumes, build cache) with reclaimable size. |
Users
Tool | Description |
| List all Portainer users with id, username, role. |
| Get user details. Sensitive fields (password hash, TFA material, tokens) are filtered out. |
Example Workflows
Deploy a new service:
"Deploy a stack called 'redis' with Redis 7 on port 6379"
The agent will call portainer_stack_deploy(name="redis", compose_content="...") with the generated compose YAML.
Debug a failing container:
"Why is the nginx container crashing?"
The agent will call portainer_containers_list() to find the container, then portainer_container_logs(container_id) to inspect the logs.
Update an existing stack:
"Update the arena-etl stack to use the new image tag v2.1"
The agent will call portainer_stack_inspect(stack_id) to get the current compose file, modify the image tag, then portainer_stack_update(stack_id, compose_content).
Security
JWT auth with proactive refresh (7h TTL, Portainer default is 8h),
asyncio.Lock-guarded re-authentication for safe concurrent use, and 401/403-CSRF retry fallback.CSRF handling for Portainer 2.39+ — Referer +
X-CSRF-Tokenare sent only on mutating methods; CSRF token is harvested fromX-CSRF-Tokenresponse headers and refreshed automatically.SSL verification enabled by default. Only disable for self-signed certificates.
Input validation — container IDs, image references (incl. digests), stack names, volume/network names are regex-validated before any API call. Path traversal (
..) is blocked.Sensitive field filtering —
endpoint_inspectstrips TLS certificates, Azure credentials and security settings;user_inspectwhitelists safe fields and hides password/TFA material.Audit logging — every mutating operation (deploy, delete, remove, pull, start, stop, exec, tinker) is logged to stderr with parameters.
No hardcoded credentials — all secrets come from environment variables. Optional
X-Registry-Authfor private-registry image pulls is passed in via parameter, never persisted.Container removal —
forcedefaults tofalseto prevent accidental deletion of running containers.Log/exec size limits — output is capped at 100K characters to prevent memory exhaustion.
Development
git clone https://github.com/ginkida/portainer-mcp.git
cd portainer-mcp
pip install -e ".[dev]"Run locally:
export PORTAINER_URL=https://your-portainer:9443
export PORTAINER_USERNAME=admin
export PORTAINER_PASSWORD=your-password
python3 -m portainer_mcp.serverLint and type-check:
ruff check src/
mypy src/Requirements
Python 3.10+
A running Portainer instance (CE or Business Edition)
Portainer API access (default port 9443)
License
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
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/ginkida/portainer-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server