MCP server for managing Deckhouse Kubernetes Platform (Community Edition).
Deployed as a Pod in d8-system namespace, authenticated via ServiceAccount + RBAC.
Communicates over SSE (HTTP) transport using the Model Context Protocol.
Diagnostics (read-only)
deckhouse_GetClusterStatus— aggregated cluster health: nodes, modules, releases, unhealthy podsdeckhouse_ListNodes— cluster nodes with filtering by group, status, roledeckhouse_ListNodeGroups— NodeGroup resources with status and conditionsdeckhouse_ListStaticInstances— StaticInstance resources with filtering by group and phasedeckhouse_ListUnhealthyPods— pods not in Running/Succeeded state
Modules (read-only)
deckhouse_ListModuleConfigs— ModuleConfig resources with enabled/disabled filter
Releases (read-only)
deckhouse_ListDeckhouseReleases— DeckhouseRelease resources with phase filter
Nodes (write)
deckhouse_CreateSSHCredentials— create SSHCredentials resourcedeckhouse_CreateStaticInstance— create StaticInstance resourcedeckhouse_AddWorkerNode— composite: SSHCredentials → StaticInstance → wait for Running
- Go 1.26 with MCP Go SDK
- Protobuf + protoc-gen-mcp — proto-first tool generation
- EasyP — proto linting, codegen, dependency management
- client-go — typed client for core resources, dynamic client for Deckhouse CRDs
- Distroless — minimal container image
- Go 1.26+
- EasyP (
brew install easyp-tech/tap/easyp) - go-task (
brew install go-task) - Docker
- A Kubernetes cluster with Deckhouse CE installed
# Generate protobuf code
task generate
# Build
task build
# Run tests
task test
# Build Docker image
task docker:buildtask generate # easyp mod download + easyp generate
task lint # easyp lint
task build # go build
task test # go test ./...
task docker:build # docker build
task docker:load # kind load docker-image (for local Kind cluster)
task integration # full integration test cycle
All MCP tools are defined in .proto files under proto/deckhouse/v1/:
| File | Service | Purpose |
|---|---|---|
diagnostics.proto |
DiagnosticsAPI |
Read-only cluster status, nodes, pods |
modules.proto |
ModulesAPI |
ModuleConfig listing |
releases.proto |
ReleasesAPI |
DeckhouseRelease listing |
nodes.proto |
NodesAPI |
Node provisioning (SSHCredentials, StaticInstance) |
After editing .proto files, regenerate:
task generateThis produces *.pb.go (protobuf types) and *.mcp.go (MCP tool handler interfaces + registration).
Each handler implements a generated *ToolHandler interface:
// Generated interface (example)
type DiagnosticsAPIToolHandler interface {
GetClusterStatus(ctx context.Context, req *emptypb.Empty) (*GetClusterStatusResponse, error)
ListNodes(ctx context.Context, req *ListNodesRequest) (*ListNodesResponse, error)
// ...
}Handlers live in internal/handler/ and receive a k8s.Client interface for all Kubernetes operations.
task test # 38 unit tests, ~60s (includes polling tests)Tests use a mock k8s.Client with function fields — no external mock libraries.
The server runs with least-privilege permissions:
- Read:
nodes,pods(core);nodegroups,staticinstances,moduleconfigs,deckhousereleases(deckhouse.io CRDs) - Write:
staticinstances,sshcredentials(create only)
| Variable | Default | Description |
|---|---|---|
LISTEN_ADDR |
:8080 |
HTTP listen address |
The server uses SSE transport. To connect:
- Open SSE stream:
GET /sse— receiveevent: endpoint\ndata: /sse?sessionid=XXX - Initialize:
POST /sse?sessionid=XXXwith JSON-RPCinitializerequest - Confirm:
POSTwithnotifications/initialized - Call tools:
POSTwithtools/call— responses arrive via the SSE stream
Example with curl:
# Open SSE stream in background
curl -sN http://localhost:8080/sse > /tmp/sse.log &
# Get session endpoint (wait ~1s for first event)
ENDPOINT="http://localhost:8080$(grep '^data:' /tmp/sse.log | head -1 | sed 's/^data: *//')"
# Initialize
curl -s -X POST "$ENDPOINT" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":0,"method":"initialize","params":{
"protocolVersion":"2024-11-05",
"capabilities":{},
"clientInfo":{"name":"demo","version":"1.0"}}}'
# Confirm initialized
curl -s -X POST "$ENDPOINT" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"notifications/initialized"}'
# List available tools
curl -s -X POST "$ENDPOINT" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# Call a tool
curl -s -X POST "$ENDPOINT" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{
"name":"deckhouse_GetClusterStatus","arguments":{}}}'Responses appear as data: lines in the SSE stream.
proto/deckhouse/v1/ # .proto files — single source of truth
├── diagnostics.proto # DiagnosticsAPI (5 RPCs)
├── modules.proto # ModulesAPI (1 RPC)
├── releases.proto # ReleasesAPI (1 RPC)
├── nodes.proto # NodesAPI (3 RPCs)
├── config.proto # ConfigAPI (stub, planned)
└── sources.proto # SourcesAPI (stub, planned)
cmd/deckhouse-mcp/main.go # SSE HTTP server entrypoint
internal/handler/ # Tool handler implementations
internal/k8s/client.go # Kubernetes client interface
deploy/ # Plain Kubernetes manifests
Taskfile.yml # Build tasks (go-task)
easyp.yaml # Proto config
Dockerfile # Multi-stage: golang → distroless