Portainer Model Context Protocol (MCP) Server

repository·main·Indexed 42 days ago

https://github.com/portainer/portainer-mcp

An official MCP server that exposes the Portainer REST API as tools for AI agents. It enables the management of Docker, Kubernetes, stacks, and Helm resources through MCP-compatible clients. The server dynamically generates tools from the Portainer OpenAPI specification and supports various deployment modes, including local stdio via uvx, containerized deployments with TLS, and integration with identity-aware proxies. Version 2.45.1.

Tokens
14.7K
Snippets
26
Records
65
Agent score
53%

What's inside Portainer MCP

  1. How the ALL option works in Portainer MCP

    main

    Setting PORTAINER_PROFILES=ALL bypasses the tag filter entirely. Every operation in the Portainer API spec is registered as a tool.

    Unlike standard profiles, ALL is not a bundle of tags; it ensures that any new tags added to the upstream Portainer API will appear automatically in the MCP server without requiring a code change. This is useful for an 'audit' persona when combined with PORTAINER_READ_ONLY=1.

    PORTAINER_PROFILES=ALL uv run portainer-mcp
  2. Understand the Portainer MCP architecture and design principles

    main

    Portainer MCP is designed to be an OpenAPI-driven server rather than a hand-coded one. This architecture ensures that tool generation is tied directly to the Portainer specification, allowing for easier updates when the spec changes.

    Key architectural pillars include:

    • OpenAPI-driven tool generation: Uses FastMCP to generate tools directly from the Portainer spec.
    • Spec-layer filtering: Uses 'Profiles' to reduce the visible tool surface area without modifying individual tools.
    • Universal shaping: Applies consistent select, redaction, and capability caps across all tools (both generated and hand-written).
    • Safe-by-default disclosure: Environment values in JSON responses are redacted on the server before reaching the LLM. Exposure of these values is a global, operator-controlled toggle rather than a per-tool configuration.
  3. Understand Portainer MCP versioning and compatibility

    main

    Portainer MCP uses a three-part versioning scheme: <portainer-major>.<portainer-minor>.<mcp-patch>.

    Compatibility is tied to the Portainer API minor version. For example, MCP 2.45.x is compatible with Portainer 2.45.x.

    • Major.Minor: Tracks the target Portainer API version. The minor version only increments when the embedded Portainer specification changes.
    • Patch: Tracks MCP-specific changes such as bug fixes, new profiles, transform changes, dependency updates, or documentation changes. These do not change the Portainer API target.
  4. Understand the Guidance Gate mechanism

    main

    To ensure LLMs use the tools correctly, the server implements a Guidance Gate.

    How it works

    • The server maintains a hygiene guide that is too large for the standard MCP instructions field.
    • The get_guidance tool provides the full guide.
    • GuidanceGateMiddleware acts as a 'toll booth': the first tool call from a user after their idle window (PORTAINER_MCP_GUIDANCE_TTL, default 1800s) has lapsed will be intercepted. The server returns the guide and a retry instruction instead of executing the requested tool.
    • Once the guide is delivered, the user is marked as 'guided' for the remainder of their session.

    Disabling the gate

    Set PORTAINER_MCP_DISABLE_GUIDANCE_GATE=1 to bypass this enforcement.

  5. Explore Portainer MCP architectural components

    main

    The Portainer MCP server is composed of several specialized modules that handle different aspects of the tool lifecycle and security:

    • Spec & tool generation (server.py): Handles the core logic of turning the OpenAPI spec into MCP tools.
    • Tag filter (profiles.py): Manages how tools are grouped and filtered via profiles.
    • Proxy tools (proxy.py): Manages tools that act as proxies.
    • Tool annotations (readOnlyHint): Uses annotations to signal to the model which operations are read-only.
    • HTTP auth (auth.py and passthrough.py): Manages authentication logic.
    • Auth posture (auth_posture.py): Defines the security stance for authentication.
    • HTTP security (http_security.py): Handles security constraints at the HTTP layer.
    • TLS posture (tls.py): Manages TLS/SSL security configurations.
    • Per-request context (request_context.py): Manages state and context for individual requests.
  6. Handle heavy fields in EndpointList and Kubernetes proxy

    main

    Certain fields in Portainer responses are extremely large and should be projected out unless specifically requested.

    EndpointList

    • .Snapshots[0]: This contains the entire environment state (containers, images, networks). For counts or status, project into specific fields instead of fetching the whole snapshot.

    Kubernetes (via kubernetes_proxy)

    • metadata.managedFields: Often accounts for 30-70% of an object's size. Project this out unless investigating reconciliation history.
    • status blocks: Very verbose for Deployments, Pods, and Nodes. Project specific fields like phase or readyReplicas.

    Note on Pod Health: For pods that haven't started, status.containerStatuses[0].restartCount will return null. To robustly check health, always include status.phase and status.reason (e.g., Evicted).

    # Efficiently get container counts per environment
    EndpointList(select="[].{name:Name,running:Snapshots[0].RunningContainerCount,total:Snapshots[0].ContainerCount}")
    
    # Robust Pod health check (handles non-running pods)
    kubernetes_proxy(environment_id=N, path="/api/v1/pods", select="items[].{name:metadata.name,phase:status.phase,reason:status.reason,restarts:status.containerStatuses[0].restartCount}")
  7. Understand environment variable redaction

    main

    By default, environment variables for Stacks, Containers, and Kubernetes resources are redacted and returned as the literal string [REDACTED].

    • Redaction Scope: Covers Env pairs, Docker KEY=VAL strings, and K8s env[].value. K8s valueFrom references (Secrets/ConfigMaps) are not redacted as they are references, not the values themselves.
    • Limitations: You cannot bypass redaction using JMESPath select because projection happens after the server redacts the data.
    • How to view values: If the user needs to see actual values for troubleshooting, the MCP server must be restarted with the environment variable PORTAINER_EXPOSE_ENV_VALUES=1 set. Do not attempt to toggle this via tool calls.
  8. Understand Read-Only semantics in Portainer MCP

    main

    When PORTAINER_READ_ONLY=1 is set, the server restricts available tools based on the HTTP method used by the Portainer API:

    1. Only GET and HEAD operations are registered as tools.
    2. Proxy tools will reject any non-GET requests at call time.

    Note: Some Portainer endpoints use POST for read-shaped operations (e.g., certain snapshot listings). These will also be hidden in read-only mode. This behavior is based on the HTTP method to ensure predictable and stable filtering.

  9. Optimize Portainer MCP tool calls using the `select` parameter

    main

    Portainer MCP tools return large JSON payloads (e.g., full K8s status blocks or complete stack manifests) that can quickly exceed the ~50,000 character response cap and consume significant context tokens (up to 20K tokens for a single call).

    To prevent truncation and save context, use the optional select parameter to apply a JMESPath projection server-side. This allows you to request only the specific fields required for a task.

    Key JMESPath patterns for Portainer:

    • List of objects: Start with [] to map over elements (e.g., [].{id:Id, name:Name}).
    • Wrapped lists (Kubernetes): Start with items[] (e.g., items[].{name:metadata.name}).
    • Single object: Use direct paths without leading brackets (e.g., {field:path.to.value}).
    • Nested paths: Use dots (e.g., Snapshots[0].RunningContainerCount).
    • Keys with dots/hyphens: Use double quotes to avoid syntax collisions (e.g., Labels."com.docker.compose.project"). Note: Do not use backslashes to escape quotes inside the string; use plain double quotes "..." or use a function filter like [?contains(metadata.name, 'value')] to avoid transport issues.
    # Example: Projecting only necessary fields for a list of endpoints
    EndpointList(select="[].{id:Id,name:Name,type:Type,status:Status}")
    
    # Example: Projecting Kubernetes pod details
    kubernetes_proxy(environment_id=N, path="/api/v1/pods", select="items[].{name:metadata.name,ns:metadata.namespace,phase:status.phase}")
  10. How Portainer MCP tool generation and filtering work

    main

    The MCP server generates tools by loading a patched Portainer OpenAPI specification. Because the full spec is too large for LLMs, the server uses Profiles to filter the available tools.

    Tool Generation

    • OpenAPI Tools: Each operation in the spec becomes an MCP tool. Tools automatically include a readOnlyHint annotation: GET/HEAD methods are marked as read-only, while others are write operations.
    • Proxy Tools: Two manual tools, docker_proxy and kubernetes_proxy, are provided to handle arbitrary paths (e.g., /endpoints/{id}/docker/...) that are not explicitly enumerated in the OpenAPI spec.

    Filtering with Profiles

    Profiles are named bundles of tags (e.g., DOCKER, KUBERNETES). You can control the exposed surface using:

    • PORTAINER_PROFILES: A list of profiles to enable. The union of their tags defines the available tools.
    • PORTAINER_READ_ONLY=1: Restricts the surface strictly to GET/HEAD methods.
    # Example configuration concept
    # PORTAINER_PROFILES=DOCKER,KUBERNETES
    # PORTAINER_READ_ONLY=1
  11. Understand the behavior of StackUpdateGit

    main

    The StackUpdateGit tool updates the Git settings (configuration), but it does not trigger a redeployment of the running stack.

    The Trap: The response from StackUpdateGit often looks like a successful deployment because it returns a populated stackResponse. However, while GitConfig.ReferenceName will show your new target, GitConfig.ConfigHash and CurrentDeploymentInfo will still reflect the old commit.

    Correct Workflow:

    1. Call StackUpdateGit to update settings.
    2. Call StackGitRedeploy to apply the changes.
    3. Verify that CurrentDeploymentInfo (and ConfigHash) has advanced to the new reference.
  12. How the Portainer MCP server architecture works

    main

    The Portainer MCP server acts as a bridge between Portainer's REST API and MCP clients. Instead of manually defining every tool, the server is bootstrapped dynamically from the Portainer OpenAPI specification at startup. This approach ensures the tool surface stays synchronized with the Portainer API. The architecture uses a pipeline that processes the OpenAPI spec through several layers: a tag filter, hand-written proxy tools, argument transformations, environment variable redaction, and various middleware layers (GuidanceGate and ResponseCap) before delivering the final tool interface to the MCP client.

    data/portainer-patched.yaml ──► FastMCP.from_openapi ──► tag filter ──┐
                                                                          │
                                hand-written proxy tools ─────────────────┤
                                                                          ▼
                                                      SelectArgTransform (per tool)
                                                                          │
                                                                          ▼
                                                      redact_envs (before projection)
                                                                          │
                                                                          ▼
                                              GuidanceGateMiddleware (toll booth, per call)
                                                                          │
                                                                          ▼
                                                      ResponseCapMiddleware (per call)
                                                                          │
                                                                          ▼
                                                                      MCP client