An MCP (Model Context Protocol) server that connects to a remote Portainer instance, giving any MCP-compatible AI assistant or IDE full control over your Docker environments, stacks, containers, images, networks, and volumes.
- Environments — list and inspect Docker environments registered in Portainer
- Stacks — create (from compose string or Git repo), update, redeploy, start, stop, delete
- Containers — list, inspect, start, stop, restart, remove, fetch logs, run commands, read stats
- Images — list, pull, remove
- Networks — list, create, remove
- Volumes — list, create, remove
- Users & Teams — list, create, delete (admin)
- Registries — list configured registries
- System — status, version, settings
Works with Claude Desktop, VS Code (Continue, Copilot Chat), Cursor, Windsurf, Zed, and any other MCP-compatible client.
-
Python 3.11+
-
uv — provides the
uvxcommand used in all install options belowcurl -LsSf https://astral.sh/uv/install.sh | shIf you see
ENOENTor "command not found: uvx" errors in your MCP client, this is the fix.
PORTAINER_URL=https://portainer.example.com \
PORTAINER_API_KEY=ptr_xxxx \
uvx mcp-portainer# Install
pip install mcp-portainer
# or: uv add mcp-portainer
# Run
PORTAINER_URL=https://portainer.example.com \
PORTAINER_API_KEY=ptr_xxxx \
mcp-portainercp .env.example .env && $EDITOR .env
docker compose up -dThe server listens at http://localhost:8000/mcp using the streamable-http transport.
All configuration is via environment variables (or a .env file in the working directory).
| Variable | Required | Default | Description |
|---|---|---|---|
PORTAINER_URL |
✅ | — | Portainer base URL, e.g. https://portainer.example.com |
PORTAINER_API_KEY |
✅* | — | API key (preferred). Generate in Portainer → User settings → Access tokens |
PORTAINER_USERNAME |
✅* | — | Username (if not using API key) |
PORTAINER_PASSWORD |
✅* | — | Password (if not using API key) |
PORTAINER_VERIFY_SSL |
true |
Set false for self-signed certificates |
|
PORTAINER_TIMEOUT |
30 |
HTTP request timeout in seconds |
*One of PORTAINER_API_KEY or PORTAINER_USERNAME+PORTAINER_PASSWORD is required.
Getting a Portainer API key: Portainer → top-right menu → My account → Access tokens → Add access token.
claude mcp add portainer -s user \
-e PORTAINER_URL=https://portainer.example.com \
-e PORTAINER_API_KEY=ptr_xxxxxxxxxxxxxxxxxxxx \
-- uvx --from git+https://github.com/jasonarends/mcp-portainer mcp-portainer-s user makes it available across all your projects. uvx fetches and runs the package automatically — no manual install required.
Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"portainer": {
"command": "uvx",
"args": ["--from", "git+https://github.com/jasonarends/mcp-portainer", "mcp-portainer"],
"env": {
"PORTAINER_URL": "https://portainer.example.com",
"PORTAINER_API_KEY": "ptr_xxxxxxxxxxxxxxxxxxxx"
}
}
}
}Or edit .claude/settings.json in your project:
{
"mcpServers": {
"portainer": {
"command": "uvx",
"args": ["--from", "git+https://github.com/jasonarends/mcp-portainer", "mcp-portainer"],
"env": {
"PORTAINER_URL": "https://portainer.example.com",
"PORTAINER_API_KEY": "ptr_xxxxxxxxxxxxxxxxxxxx"
}
}
}
}Add to .continue/config.json:
{
"mcpServers": [
{
"name": "portainer",
"command": "uvx",
"args": ["--from", "git+https://github.com/jasonarends/mcp-portainer", "mcp-portainer"],
"env": {
"PORTAINER_URL": "https://portainer.example.com",
"PORTAINER_API_KEY": "ptr_xxxxxxxxxxxxxxxxxxxx"
}
}
]
}Add to ~/.cursor/mcp.json:
{
"mcpServers": {
"portainer": {
"command": "uvx",
"args": ["--from", "git+https://github.com/jasonarends/mcp-portainer", "mcp-portainer"],
"env": {
"PORTAINER_URL": "https://portainer.example.com",
"PORTAINER_API_KEY": "ptr_xxxxxxxxxxxxxxxxxxxx"
}
}
}
}Add to ~/.codeium/windsurf/mcp_config.json:
{
"mcpServers": {
"portainer": {
"command": "uvx",
"args": ["--from", "git+https://github.com/jasonarends/mcp-portainer", "mcp-portainer"],
"env": {
"PORTAINER_URL": "https://portainer.example.com",
"PORTAINER_API_KEY": "ptr_xxxxxxxxxxxxxxxxxxxx"
}
}
}
}When running via docker compose up -d, the server exposes http://localhost:8000/mcp using the streamable-http transport. Configure your client to connect to this URL instead of spawning a subprocess.
Example for Claude Desktop:
{
"mcpServers": {
"portainer": {
"url": "http://localhost:8000/mcp"
}
}
}| Tool | Description |
|---|---|
portainer_status |
Portainer version, instance ID, and status |
portainer_settings |
Current Portainer settings (admin only) |
| Tool | Key parameters |
|---|---|
list_environments |
— |
get_environment |
environment_id |
| Tool | Key parameters |
|---|---|
list_stacks |
environment_id (optional filter) |
get_stack |
stack_id |
create_stack |
environment_id, name, compose_content, env_vars |
create_stack_from_git |
environment_id, name, repo_url, compose_path, branch |
update_stack |
stack_id, environment_id, compose_content, env_vars, prune, pull_image |
redeploy_stack |
stack_id, environment_id, pull_image, prune |
start_stack |
stack_id |
stop_stack |
stack_id |
delete_stack |
stack_id, environment_id |
| Tool | Key parameters |
|---|---|
list_containers |
environment_id, all_containers |
get_container |
environment_id, container_id |
start_container |
environment_id, container_id |
stop_container |
environment_id, container_id, timeout |
restart_container |
environment_id, container_id |
remove_container |
environment_id, container_id, force, remove_volumes |
get_container_logs |
environment_id, container_id, tail, timestamps |
exec_command |
environment_id, container_id, command, user |
get_container_stats |
environment_id, container_id |
| Tool | Key parameters |
|---|---|
list_images |
environment_id |
pull_image |
environment_id, image, tag |
remove_image |
environment_id, image_id, force |
| Tool | Key parameters |
|---|---|
list_networks |
environment_id |
create_network |
environment_id, name, driver |
remove_network |
environment_id, network_id |
| Tool | Key parameters |
|---|---|
list_volumes |
environment_id |
create_volume |
environment_id, name, driver |
remove_volume |
environment_id, volume_name, force |
| Tool | Key parameters |
|---|---|
list_users |
— |
create_user |
username, password, role |
delete_user |
user_id |
list_teams |
— |
create_team |
name |
| Tool | Key parameters |
|---|---|
list_registries |
— |
list_webhooks |
— |
git clone https://github.com/jasonarends/mcp-portainer
cd mcp-portainer
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"pytestTests use respx to mock HTTP calls — no live Portainer instance required.
- Update
__version__inmcp_portainer/__init__.py - Commit and push
git tag v0.x.y && git push origin v0.x.y
The docker-publish workflow builds and pushes the Docker image to GHCR automatically.