202602-1: Local Stack Support via Raw HTTP Client
Date: 24/02/2026
Context
The official Portainer MCP server (portainer/portainer-mcp) only supports Edge Stacks — stacks distributed via Edge Groups to edge environments. However, the most common stack deployment model in Portainer is regular/standalone Docker Compose stacks deployed directly on environments.
The official SDK (portainer/client-api-go) does not expose any API methods for regular stacks — it only contains edge_stack.go. As a result, the existing listStacks, getStackFile, createStack, and updateStack tools all operate on Edge Stacks and return errors (503) when Edge features are disabled on the Portainer instance.
Users who deploy standard Docker Compose stacks (the majority of non-Edge setups) have no way to manage them through the MCP tools.
Decision
Add 7 new MCP tools for managing local (standalone) Docker Compose stacks using direct HTTP requests to the Portainer REST API, bypassing the SDK entirely.
New tools:
listLocalStacks— List all local stacks across environmentsgetLocalStackFile— Retrieve the docker-compose.yml content for a stackcreateLocalStack— Create a new standalone Docker Compose stackupdateLocalStack— Update compose file and environment variablesstartLocalStack— Start a stopped stackstopLocalStack— Stop a running stackdeleteLocalStack— Permanently remove a stack
Rationale
-
SDK limitation: The
client-api-goSDK has no regular stack methods. Waiting for upstream SDK changes would block functionality indefinitely. -
Raw HTTP approach: A dedicated
rawHTTPClientstruct with anapiRequest()helper method allows direct HTTP communication with the Portainer REST API (/api/stacks/*). This struct is composed intoPortainerClient, keeping the SDK wrapper clean while maintaining the same client abstraction. -
Coexistence with Edge Stacks: The new local stack tools use distinct tool names (prefixed with “Local”) and separate handler functions, avoiding any conflict with existing Edge Stack tools.
-
URL scheme normalization: The
serverURLconfiguration may not include a scheme (e.g.,192.168.0.40:31015). The raw HTTP client normalizes this by defaulting tohttps://, matching the SDK’s internal behavior. -
Read-only mode support: Local stack write tools (create, update, start, stop, delete) respect the existing
readOnlyflag and are only registered when the flag is not set.
Implementation Details
Architecture layers:
MCP Tool Handler (internal/mcp/local_stack.go)
↓ calls
PortainerClient interface (internal/mcp/server.go)
↓ implemented by
Raw HTTP methods (pkg/portainer/client/local_stack.go)
↓ uses apiRequest() helper
Portainer REST API (/api/stacks/*)
Files added:
pkg/portainer/client/local_stack.go—apiRequest()helper + 7 client methodsinternal/mcp/local_stack.go— 7 MCP handler functions +parseEnvVars()helper +AddLocalStackFeatures()registration
Files modified:
pkg/portainer/models/stack.go—LocalStack,RawLocalStack,LocalStackEnvVartypes, enums, conversion functionpkg/portainer/client/client.go— AddedserverURL,token,httpClifields; URL scheme normalizationinternal/mcp/schema.go— 7 new tool name constantsinternal/mcp/server.go— 7 new method signatures inPortainerClientinterfacecmd/portainer-mcp/mcp.go—AddLocalStackFeatures()call in server initializationinternal/tooldef/tools.yaml— 7 new tool definitions
Test files added:
pkg/portainer/client/local_stack_test.go— HTTP client tests usinghttptest.NewServerinternal/mcp/local_stack_test.go— MCP handler tests usingMockPortainerClientpkg/portainer/models/stack_test.go— Model conversion and enum tests (appended to existing file)internal/mcp/mocks_test.go— 7 new mock methods (appended to existing file)
Trade-offs
Benefits:
- Enables management of the most common Portainer stack type (standalone Docker Compose)
- Does not modify the SDK, avoiding upstream dependency issues
- Follows existing patterns (tool registration, read-only gating, error handling)
- Comprehensive test coverage at all three layers
Challenges:
- Raw HTTP client is a separate code path from the SDK-based client, requiring its own testing approach (
httptest.NewServervs SDK mocks) - If the Portainer REST API changes the
/api/stacks/endpoints, the raw HTTP methods must be updated manually (no SDK versioning protection)