feat: add Docker support for all MCP servers

- Create individual Dockerfiles for each of the 7 MCP servers
- Add docker-compose.yml for orchestrating all services
- Create build-docker.sh script for easy container building
- Add comprehensive Docker deployment documentation (DOCKER.md)
- Update main README with Docker installation instructions
- Add .env.example template for environment configuration
- Configure each server with dedicated ports (3000-3006)
- Implement security best practices (non-root user, minimal base image)
- Add production deployment considerations and troubleshooting guide

Each server can now be run individually or all together using Docker Compose,
making deployment and scaling much easier for production environments.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Adolfo Delorenzo 2025-07-21 00:12:39 -03:00
parent f9d8407c09
commit 2098453ff1
13 changed files with 735 additions and 38 deletions

View File

@ -1,45 +1,18 @@
# Portainer Core MCP Server Configuration # Portainer MCP Environment Configuration
# Copy this file to .env and update with your values
# ============================================================================= # Required - Your Portainer instance URL
# REQUIRED CONFIGURATION
# =============================================================================
# Portainer instance URL (required)
# Examples:
# - https://portainer.example.com
# - https://portainer.company.com:9443
# - http://localhost:9000
PORTAINER_URL=https://your-portainer-instance.com PORTAINER_URL=https://your-portainer-instance.com
# Portainer API key for authentication (required) # Required - Your Portainer API key
# Generate this from Portainer UI: User settings > API tokens # Generate from Portainer UI: My Account > Access Tokens
# Example: ptr_XYZ123abc456def789
PORTAINER_API_KEY=your-api-key-here PORTAINER_API_KEY=your-api-key-here
# ============================================================================= # Optional - Set to true if using self-signed certificates
# OPTIONAL CONFIGURATION PORTAINER_INSECURE=false
# =============================================================================
# HTTP client settings # Optional - HTTP request timeout in seconds
HTTP_TIMEOUT=30 HTTP_TIMEOUT=30
# Optional - Maximum number of retry attempts
MAX_RETRIES=3 MAX_RETRIES=3
RETRY_DELAY=1.0
# Circuit breaker settings
CIRCUIT_BREAKER_FAILURE_THRESHOLD=5
CIRCUIT_BREAKER_RECOVERY_TIMEOUT=60
# Token management settings
TOKEN_CACHE_TTL=3600
TOKEN_REFRESH_THRESHOLD=300
# Logging settings
LOG_LEVEL=INFO
LOG_FORMAT=json
# Development settings
DEBUG=false
# MCP mode - disables stdout logging
MCP_MODE=true

235
DOCKER.md Normal file
View File

@ -0,0 +1,235 @@
# Docker Deployment Guide for Portainer MCP Servers
This guide covers how to build and run the Portainer MCP servers using Docker containers.
## Prerequisites
- Docker Engine 20.10+
- Docker Compose 2.0+
- Portainer Business Edition instance
- Valid Portainer API key
## Quick Start
1. **Clone the repository**
```bash
git clone https://github.com/yourusername/portainer-mcp.git
cd portainer-mcp
```
2. **Create environment file**
```bash
cp .env.example .env
# Edit .env with your Portainer credentials
```
3. **Build all containers**
```bash
./build-docker.sh all
```
4. **Start all services**
```bash
docker-compose up -d
```
## Individual Container Management
### Building Containers
Build all containers:
```bash
./build-docker.sh all
```
Build specific container:
```bash
./build-docker.sh core
./build-docker.sh docker
./build-docker.sh kubernetes
# etc...
```
### Running Containers
Run all services:
```bash
docker-compose up -d
```
Run specific service:
```bash
docker-compose up -d portainer-core
docker-compose up -d portainer-docker
# etc...
```
View logs:
```bash
docker-compose logs -f portainer-core
docker-compose logs -f portainer-docker
# etc...
```
Stop services:
```bash
docker-compose down
```
## Container Details
Each MCP server runs in its own container with the following configuration:
| Service | Container Name | Port | Purpose |
|---------|---------------|------|---------|
| portainer-core | portainer-mcp-core | 3000 | User management, teams, RBAC |
| portainer-environments | portainer-mcp-environments | 3001 | Environment management |
| portainer-docker | portainer-mcp-docker | 3002 | Docker operations |
| portainer-kubernetes | portainer-mcp-kubernetes | 3003 | Kubernetes management |
| portainer-stacks | portainer-mcp-stacks | 3004 | Stack deployment |
| portainer-edge | portainer-mcp-edge | 3005 | Edge computing |
| portainer-gitops | portainer-mcp-gitops | 3006 | GitOps automation |
## Environment Variables
All containers share the same environment variables:
```bash
# Required
PORTAINER_URL=https://your-portainer-instance.com
PORTAINER_API_KEY=your-api-key-here
# Optional
PORTAINER_INSECURE=false # Set to true for self-signed certificates
HTTP_TIMEOUT=30
MAX_RETRIES=3
```
## Docker Compose Configuration
The `docker-compose.yml` file defines all services with:
- Individual port mappings
- Shared environment variables
- Automatic restart policies
- Isolated network for inter-container communication
## Production Deployment
For production environments, consider:
1. **Use Docker Swarm or Kubernetes**
```bash
# Deploy as stack in Swarm
docker stack deploy -c docker-compose.yml portainer-mcp
```
2. **Enable health checks**
Add to each service in docker-compose.yml:
```yaml
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
```
3. **Use secrets for API keys**
```yaml
secrets:
portainer_api_key:
external: true
```
4. **Configure resource limits**
```yaml
deploy:
resources:
limits:
cpus: '0.5'
memory: 256M
```
## Troubleshooting
### Container won't start
- Check logs: `docker-compose logs portainer-<service>`
- Verify environment variables in `.env`
- Ensure Portainer URL is accessible from container
### Connection errors
- For self-signed certificates, set `PORTAINER_INSECURE=true`
- Check network connectivity: `docker exec portainer-mcp-core curl https://your-portainer-url`
### Permission errors
- Verify API key has required permissions
- Check Portainer RBAC settings
## Security Considerations
1. **Never commit `.env` file** - It contains sensitive credentials
2. **Use read-only mounts** where possible
3. **Run containers as non-root user** (already configured)
4. **Enable TLS** for MCP connections in production
5. **Regularly update** base images and dependencies
## Advanced Configuration
### Custom Network
```yaml
networks:
portainer-mcp:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16
```
### Volume Mounts
For persistent logs or configuration:
```yaml
volumes:
- ./logs:/app/logs
- ./config:/app/config:ro
```
### Multi-stage Builds
The Dockerfiles use single-stage builds for simplicity. For smaller images:
```dockerfile
FROM python:3.11-slim AS builder
# Build stage...
FROM python:3.11-slim
# Runtime stage...
```
## Integration with Claude Desktop
When running in Docker, configure Claude Desktop to connect to the containerized servers:
```json
{
"mcpServers": {
"portainer-core": {
"url": "http://localhost:3000"
},
"portainer-docker": {
"url": "http://localhost:3002"
}
// ... other servers
}
}
```
## Monitoring
Monitor container health and resource usage:
```bash
# View resource usage
docker stats
# Check container health
docker ps --format "table {{.Names}}\t{{.Status}}"
# Export metrics
docker-compose ps --format json
```

33
Dockerfile.base Normal file
View File

@ -0,0 +1,33 @@
# Base Dockerfile for Portainer MCP Servers
FROM python:3.11-slim
# Install system dependencies
RUN apt-get update && apt-get install -y \
git \
curl \
&& rm -rf /var/lib/apt/lists/*
# Set working directory
WORKDIR /app
# Copy requirements file
COPY requirements.txt .
# Install Python dependencies
RUN pip install --no-cache-dir -r requirements.txt
# Create non-root user
RUN useradd -m -u 1000 mcp && chown -R mcp:mcp /app
# Switch to non-root user
USER mcp
# Set Python to unbuffered mode
ENV PYTHONUNBUFFERED=1
# Default environment variables
ENV PORTAINER_URL=""
ENV PORTAINER_API_KEY=""
ENV PORTAINER_INSECURE=false
ENV HTTP_TIMEOUT=30
ENV MAX_RETRIES=3

View File

@ -2,6 +2,26 @@
A comprehensive collection of Model Context Protocol (MCP) servers for managing Portainer Business Edition through natural language interfaces. This suite provides modular servers for different aspects of Portainer management, from user authentication to container orchestration. A comprehensive collection of Model Context Protocol (MCP) servers for managing Portainer Business Edition through natural language interfaces. This suite provides modular servers for different aspects of Portainer management, from user authentication to container orchestration.
## Quick Start
```bash
# Clone and setup
git clone https://github.com/yourusername/portainer-mcp.git
cd portainer-mcp
# Docker deployment (recommended)
cp .env.example .env
# Edit .env with your Portainer URL and API key
./build-docker.sh all
docker-compose up -d
# Or Python local installation
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python portainer_core_server.py
```
## Overview ## Overview
The Portainer MCP Server Suite consists of specialized servers that work together to provide complete Portainer management capabilities: The Portainer MCP Server Suite consists of specialized servers that work together to provide complete Portainer management capabilities:
@ -74,6 +94,8 @@ The Portainer MCP Server Suite consists of specialized servers that work togethe
## Installation ## Installation
### Option 1: Local Python Installation
1. Clone the repository: 1. Clone the repository:
```bash ```bash
git clone https://github.com/yourusername/portainer-mcp.git git clone https://github.com/yourusername/portainer-mcp.git
@ -88,9 +110,29 @@ source .venv/bin/activate # On Windows: .venv\Scripts\activate
3. Install dependencies: 3. Install dependencies:
```bash ```bash
pip install mcp httpx aiohttp pip install -r requirements.txt
# or manually: pip install mcp httpx aiohttp
``` ```
### Option 2: Docker Installation (Recommended)
1. Clone the repository:
```bash
git clone https://github.com/yourusername/portainer-mcp.git
cd portainer-mcp
```
2. Build and run with Docker Compose:
```bash
cp .env.example .env
# Edit .env with your Portainer credentials
./build-docker.sh all
docker-compose up -d
```
See the [Docker Deployment Guide](./DOCKER.md) for detailed instructions.
## Configuration ## Configuration
### Environment Variables ### Environment Variables
@ -255,6 +297,7 @@ Each server has its own detailed documentation:
- **[Stacks Server Documentation](./README_STACKS.md)** - Stack deployment and lifecycle management - **[Stacks Server Documentation](./README_STACKS.md)** - Stack deployment and lifecycle management
- **[Edge Server Documentation](./README_EDGE.md)** - Edge device and edge compute management - **[Edge Server Documentation](./README_EDGE.md)** - Edge device and edge compute management
- **[GitOps Server Documentation](./README_GITOPS.md)** - GitOps automation and continuous deployment - **[GitOps Server Documentation](./README_GITOPS.md)** - GitOps automation and continuous deployment
- **[Docker Deployment Guide](./DOCKER.md)** - Running MCP servers in Docker containers
## Contributing ## Contributing

57
build-docker.sh Executable file
View File

@ -0,0 +1,57 @@
#!/bin/bash
# Build script for Portainer MCP Docker containers
set -e
echo "Building Portainer MCP Docker containers..."
# Colors for output
GREEN='\033[0;32m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Check if .env file exists
if [ ! -f .env ]; then
echo "Creating .env file from template..."
cat > .env << EOF
# Required
PORTAINER_URL=https://your-portainer-instance.com
PORTAINER_API_KEY=your-api-key-here
# Optional
PORTAINER_INSECURE=false
HTTP_TIMEOUT=30
MAX_RETRIES=3
EOF
echo -e "${BLUE}Please edit .env file with your Portainer credentials${NC}"
exit 1
fi
# Build individual containers
build_container() {
local name=$1
echo -e "${BLUE}Building portainer-${name}...${NC}"
docker build -f docker/Dockerfile.${name} -t portainer-mcp-${name}:latest .
echo -e "${GREEN}✓ portainer-${name} built successfully${NC}"
}
# Build all containers
if [ "$1" == "all" ] || [ -z "$1" ]; then
for server in core environments docker kubernetes stacks edge gitops; do
build_container $server
done
else
# Build specific container
build_container $1
fi
echo -e "${GREEN}Build complete!${NC}"
echo ""
echo "To run all containers:"
echo " docker-compose up -d"
echo ""
echo "To run a specific container:"
echo " docker-compose up -d portainer-<service>"
echo ""
echo "Available services: core, environments, docker, kubernetes, stacks, edge, gitops"

125
docker-compose.yml Normal file
View File

@ -0,0 +1,125 @@
version: '3.8'
services:
portainer-core:
build:
context: .
dockerfile: docker/Dockerfile.core
container_name: portainer-mcp-core
ports:
- "3000:3000"
environment:
- PORTAINER_URL=${PORTAINER_URL}
- PORTAINER_API_KEY=${PORTAINER_API_KEY}
- PORTAINER_INSECURE=${PORTAINER_INSECURE:-false}
- HTTP_TIMEOUT=${HTTP_TIMEOUT:-30}
- MAX_RETRIES=${MAX_RETRIES:-3}
restart: unless-stopped
networks:
- portainer-mcp
portainer-environments:
build:
context: .
dockerfile: docker/Dockerfile.environments
container_name: portainer-mcp-environments
ports:
- "3001:3001"
environment:
- PORTAINER_URL=${PORTAINER_URL}
- PORTAINER_API_KEY=${PORTAINER_API_KEY}
- PORTAINER_INSECURE=${PORTAINER_INSECURE:-false}
- HTTP_TIMEOUT=${HTTP_TIMEOUT:-30}
- MAX_RETRIES=${MAX_RETRIES:-3}
restart: unless-stopped
networks:
- portainer-mcp
portainer-docker:
build:
context: .
dockerfile: docker/Dockerfile.docker
container_name: portainer-mcp-docker
ports:
- "3002:3002"
environment:
- PORTAINER_URL=${PORTAINER_URL}
- PORTAINER_API_KEY=${PORTAINER_API_KEY}
- PORTAINER_INSECURE=${PORTAINER_INSECURE:-false}
- HTTP_TIMEOUT=${HTTP_TIMEOUT:-30}
- MAX_RETRIES=${MAX_RETRIES:-3}
restart: unless-stopped
networks:
- portainer-mcp
portainer-kubernetes:
build:
context: .
dockerfile: docker/Dockerfile.kubernetes
container_name: portainer-mcp-kubernetes
ports:
- "3003:3003"
environment:
- PORTAINER_URL=${PORTAINER_URL}
- PORTAINER_API_KEY=${PORTAINER_API_KEY}
- PORTAINER_INSECURE=${PORTAINER_INSECURE:-false}
- HTTP_TIMEOUT=${HTTP_TIMEOUT:-30}
- MAX_RETRIES=${MAX_RETRIES:-3}
restart: unless-stopped
networks:
- portainer-mcp
portainer-stacks:
build:
context: .
dockerfile: docker/Dockerfile.stacks
container_name: portainer-mcp-stacks
ports:
- "3004:3004"
environment:
- PORTAINER_URL=${PORTAINER_URL}
- PORTAINER_API_KEY=${PORTAINER_API_KEY}
- PORTAINER_INSECURE=${PORTAINER_INSECURE:-false}
- HTTP_TIMEOUT=${HTTP_TIMEOUT:-30}
- MAX_RETRIES=${MAX_RETRIES:-3}
restart: unless-stopped
networks:
- portainer-mcp
portainer-edge:
build:
context: .
dockerfile: docker/Dockerfile.edge
container_name: portainer-mcp-edge
ports:
- "3005:3005"
environment:
- PORTAINER_URL=${PORTAINER_URL}
- PORTAINER_API_KEY=${PORTAINER_API_KEY}
- PORTAINER_INSECURE=${PORTAINER_INSECURE:-false}
- HTTP_TIMEOUT=${HTTP_TIMEOUT:-30}
- MAX_RETRIES=${MAX_RETRIES:-3}
restart: unless-stopped
networks:
- portainer-mcp
portainer-gitops:
build:
context: .
dockerfile: docker/Dockerfile.gitops
container_name: portainer-mcp-gitops
ports:
- "3006:3006"
environment:
- PORTAINER_URL=${PORTAINER_URL}
- PORTAINER_API_KEY=${PORTAINER_API_KEY}
- PORTAINER_INSECURE=${PORTAINER_INSECURE:-false}
- HTTP_TIMEOUT=${HTTP_TIMEOUT:-30}
- MAX_RETRIES=${MAX_RETRIES:-3}
restart: unless-stopped
networks:
- portainer-mcp
networks:
portainer-mcp:
driver: bridge

33
docker/Dockerfile.core Normal file
View File

@ -0,0 +1,33 @@
# Dockerfile for Portainer Core MCP Server
FROM python:3.11-slim AS base
# Install system dependencies
RUN apt-get update && apt-get install -y \
git \
curl \
&& rm -rf /var/lib/apt/lists/*
# Set working directory
WORKDIR /app
# Copy requirements and server file
COPY requirements.txt .
COPY portainer_core_server.py .
# Install Python dependencies
RUN pip install --no-cache-dir -r requirements.txt
# Create non-root user
RUN useradd -m -u 1000 mcp && chown -R mcp:mcp /app
# Switch to non-root user
USER mcp
# Set Python to unbuffered mode
ENV PYTHONUNBUFFERED=1
# Expose MCP standard port
EXPOSE 3000
# Run the server
CMD ["python", "portainer_core_server.py"]

33
docker/Dockerfile.docker Normal file
View File

@ -0,0 +1,33 @@
# Dockerfile for Portainer Docker MCP Server
FROM python:3.11-slim AS base
# Install system dependencies
RUN apt-get update && apt-get install -y \
git \
curl \
&& rm -rf /var/lib/apt/lists/*
# Set working directory
WORKDIR /app
# Copy requirements and server file
COPY requirements.txt .
COPY portainer_docker_server.py .
# Install Python dependencies
RUN pip install --no-cache-dir -r requirements.txt
# Create non-root user
RUN useradd -m -u 1000 mcp && chown -R mcp:mcp /app
# Switch to non-root user
USER mcp
# Set Python to unbuffered mode
ENV PYTHONUNBUFFERED=1
# Expose MCP standard port
EXPOSE 3002
# Run the server
CMD ["python", "portainer_docker_server.py"]

33
docker/Dockerfile.edge Normal file
View File

@ -0,0 +1,33 @@
# Dockerfile for Portainer Edge MCP Server
FROM python:3.11-slim AS base
# Install system dependencies
RUN apt-get update && apt-get install -y \
git \
curl \
&& rm -rf /var/lib/apt/lists/*
# Set working directory
WORKDIR /app
# Copy requirements and server file
COPY requirements.txt .
COPY portainer_edge_server.py .
# Install Python dependencies
RUN pip install --no-cache-dir -r requirements.txt
# Create non-root user
RUN useradd -m -u 1000 mcp && chown -R mcp:mcp /app
# Switch to non-root user
USER mcp
# Set Python to unbuffered mode
ENV PYTHONUNBUFFERED=1
# Expose MCP standard port
EXPOSE 3005
# Run the server
CMD ["python", "portainer_edge_server.py"]

View File

@ -0,0 +1,33 @@
# Dockerfile for Portainer Environments MCP Server
FROM python:3.11-slim AS base
# Install system dependencies
RUN apt-get update && apt-get install -y \
git \
curl \
&& rm -rf /var/lib/apt/lists/*
# Set working directory
WORKDIR /app
# Copy requirements and server file
COPY requirements.txt .
COPY portainer_environments_server.py .
# Install Python dependencies
RUN pip install --no-cache-dir -r requirements.txt
# Create non-root user
RUN useradd -m -u 1000 mcp && chown -R mcp:mcp /app
# Switch to non-root user
USER mcp
# Set Python to unbuffered mode
ENV PYTHONUNBUFFERED=1
# Expose MCP standard port
EXPOSE 3001
# Run the server
CMD ["python", "portainer_environments_server.py"]

33
docker/Dockerfile.gitops Normal file
View File

@ -0,0 +1,33 @@
# Dockerfile for Portainer GitOps MCP Server
FROM python:3.11-slim AS base
# Install system dependencies
RUN apt-get update && apt-get install -y \
git \
curl \
&& rm -rf /var/lib/apt/lists/*
# Set working directory
WORKDIR /app
# Copy requirements and server file
COPY requirements.txt .
COPY portainer_gitops_server.py .
# Install Python dependencies
RUN pip install --no-cache-dir -r requirements.txt
# Create non-root user
RUN useradd -m -u 1000 mcp && chown -R mcp:mcp /app
# Switch to non-root user
USER mcp
# Set Python to unbuffered mode
ENV PYTHONUNBUFFERED=1
# Expose MCP standard port
EXPOSE 3006
# Run the server
CMD ["python", "portainer_gitops_server.py"]

View File

@ -0,0 +1,33 @@
# Dockerfile for Portainer Kubernetes MCP Server
FROM python:3.11-slim AS base
# Install system dependencies
RUN apt-get update && apt-get install -y \
git \
curl \
&& rm -rf /var/lib/apt/lists/*
# Set working directory
WORKDIR /app
# Copy requirements and server file
COPY requirements.txt .
COPY portainer_kubernetes_server.py .
# Install Python dependencies
RUN pip install --no-cache-dir -r requirements.txt
# Create non-root user
RUN useradd -m -u 1000 mcp && chown -R mcp:mcp /app
# Switch to non-root user
USER mcp
# Set Python to unbuffered mode
ENV PYTHONUNBUFFERED=1
# Expose MCP standard port
EXPOSE 3003
# Run the server
CMD ["python", "portainer_kubernetes_server.py"]

33
docker/Dockerfile.stacks Normal file
View File

@ -0,0 +1,33 @@
# Dockerfile for Portainer Stacks MCP Server
FROM python:3.11-slim AS base
# Install system dependencies
RUN apt-get update && apt-get install -y \
git \
curl \
&& rm -rf /var/lib/apt/lists/*
# Set working directory
WORKDIR /app
# Copy requirements and server file
COPY requirements.txt .
COPY portainer_stacks_server.py .
# Install Python dependencies
RUN pip install --no-cache-dir -r requirements.txt
# Create non-root user
RUN useradd -m -u 1000 mcp && chown -R mcp:mcp /app
# Switch to non-root user
USER mcp
# Set Python to unbuffered mode
ENV PYTHONUNBUFFERED=1
# Expose MCP standard port
EXPOSE 3004
# Run the server
CMD ["python", "portainer_stacks_server.py"]