Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
module github.com/Azure/aks-mcp

go 1.23.0
go 1.24

toolchain go1.24.2
toolchain go1.24.5

require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1
Expand All @@ -17,6 +17,7 @@ require (

require (
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
github.com/Azure/mcp-kubernetes v0.0.3 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
github.com/google/uuid v1.6.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.2
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.2.1/go.mod h1:Bzf34hhAE9NSxailk8xVeLEZbUjOXcC+GnU1mMKdhLw=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 h1:ECsQtyERDVz3NP3kvDOTLvbQhqWp/x9EsGKtb4ogUr8=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0/go.mod h1:s1tW/At+xHqjNFvWU4G0c0Qv33KOhvbGNj0RCTQDV8s=
github.com/Azure/mcp-kubernetes v0.0.3 h1:5YaaDCL+r5CUg8X72jNaYD3hLK1+8Zu86Ka++KS5MO8=
github.com/Azure/mcp-kubernetes v0.0.3/go.mod h1:nXHG50UKzFqwz8pPidHRXWM9+aSQRJy8m9dvm/FNnJ8=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
Expand Down
36 changes: 30 additions & 6 deletions internal/config/config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package config

import (
"strings"
"time"

"github.com/Azure/aks-mcp/internal/security"
Expand All @@ -21,17 +22,25 @@ type ConfigData struct {
Host string
Port int
AccessLevel string

// Kubernetes-specific options
// Map of additional tools enabled (helm, cilium)
AdditionalTools map[string]bool
// Comma-separated list of allowed Kubernetes namespaces
AllowNamespaces string
}

// NewConfig creates and returns a new configuration instance
func NewConfig() *ConfigData {
return &ConfigData{
Timeout: 60,
CacheTimeout: 1 * time.Minute,
SecurityConfig: security.NewSecurityConfig(),
Transport: "stdio",
Port: 8000,
AccessLevel: "readonly",
Timeout: 60,
CacheTimeout: 1 * time.Minute,
SecurityConfig: security.NewSecurityConfig(),
Transport: "stdio",
Port: 8000,
AccessLevel: "readonly",
AdditionalTools: make(map[string]bool),
AllowNamespaces: "",
}
}

Expand All @@ -45,8 +54,23 @@ func (cfg *ConfigData) ParseFlags() {
// Security settings
flag.StringVar(&cfg.AccessLevel, "access-level", "readonly", "Access level (readonly, readwrite, admin)")

// Kubernetes-specific settings
additionalTools := flag.String("additional-tools", "",
"Comma-separated list of additional Kubernetes tools to support (kubectl is always enabled). Available: helm,cilium")
flag.StringVar(&cfg.AllowNamespaces, "allow-namespaces", "",
"Comma-separated list of allowed Kubernetes namespaces (empty means all namespaces)")

flag.Parse()

// Update security config
cfg.SecurityConfig.AccessLevel = cfg.AccessLevel
cfg.SecurityConfig.AllowedNamespaces = cfg.AllowNamespaces

// Parse additional tools
if *additionalTools != "" {
tools := strings.Split(*additionalTools, ",")
for _, tool := range tools {
cfg.AdditionalTools[strings.TrimSpace(tool)] = true
}
}
}
65 changes: 65 additions & 0 deletions internal/k8s/adapter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package k8s

import (
"github.com/Azure/aks-mcp/internal/config"
"github.com/Azure/aks-mcp/internal/tools"
k8sconfig "github.com/Azure/mcp-kubernetes/pkg/config"
k8ssecurity "github.com/Azure/mcp-kubernetes/pkg/security"
k8stools "github.com/Azure/mcp-kubernetes/pkg/tools"
)

// ConfigAdapter converts aks-mcp config to mcp-kubernetes config
func ConvertConfig(cfg *config.ConfigData) *k8sconfig.ConfigData {
// Create K8s security config
k8sSecurityConfig := k8ssecurity.NewSecurityConfig()

// Map access levels
switch cfg.AccessLevel {
case "readonly":
k8sSecurityConfig.ReadOnly = true
case "readwrite":
k8sSecurityConfig.ReadOnly = false
case "admin":
k8sSecurityConfig.ReadOnly = false
}

// Map allowed namespaces
k8sSecurityConfig.SetAllowedNamespaces(cfg.AllowNamespaces)

// Create K8s config
k8sCfg := &k8sconfig.ConfigData{
AdditionalTools: cfg.AdditionalTools,
Timeout: cfg.Timeout,
SecurityConfig: k8sSecurityConfig,
Transport: cfg.Transport,
Host: cfg.Host,
Port: cfg.Port,
ReadOnly: k8sSecurityConfig.ReadOnly,
AllowNamespaces: cfg.AllowNamespaces,
}

return k8sCfg
}

// WrapK8sExecutor wraps a mcp-kubernetes executor to work with aks-mcp config
func WrapK8sExecutor(k8sExecutor k8stools.CommandExecutor) tools.CommandExecutor {
return &executorAdapter{k8sExecutor: k8sExecutor}
}

// WrapK8sExecutorFunc wraps a mcp-kubernetes executor function to work with aks-mcp config
func WrapK8sExecutorFunc(k8sFunc k8stools.CommandExecutorFunc) tools.CommandExecutor {
return &executorAdapter{k8sExecutor: k8sFunc}
}

// executorAdapter adapts between aks-mcp and mcp-kubernetes configs
type executorAdapter struct {
k8sExecutor k8stools.CommandExecutor
}

func (a *executorAdapter) Execute(params map[string]interface{}, cfg *config.ConfigData) (string, error) {
// Convert aks-mcp config to k8s config
k8sCfg := ConvertConfig(cfg)

// Execute using the k8s executor
return a.k8sExecutor.Execute(params, k8sCfg)
}
25 changes: 24 additions & 1 deletion internal/security/security.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,37 @@
package security

import "strings"

// SecurityConfig holds security-related configuration
type SecurityConfig struct {
// AccessLevel controls the level of operations allowed (readonly, readwrite, admin)
AccessLevel string
// AllowedNamespaces is a comma-separated list of allowed Kubernetes namespaces
AllowedNamespaces string
}

// NewSecurityConfig creates a new SecurityConfig instance
func NewSecurityConfig() *SecurityConfig {
return &SecurityConfig{
AccessLevel: "readwrite",
AccessLevel: "readwrite",
AllowedNamespaces: "",
}
}

// IsNamespaceAllowed checks if a namespace is allowed to be accessed
func (s *SecurityConfig) IsNamespaceAllowed(namespace string) bool {
// If no restrictions are defined, allow all namespaces
if s.AllowedNamespaces == "" {
return true
}

// Check if the namespace is in the allowed list
namespaces := strings.Split(s.AllowedNamespaces, ",")
for _, ns := range namespaces {
if strings.TrimSpace(ns) == namespace {
return true
}
}

return false
}
65 changes: 65 additions & 0 deletions internal/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@ import (
"github.com/Azure/aks-mcp/internal/components/monitor/diagnostics"
"github.com/Azure/aks-mcp/internal/components/network"
"github.com/Azure/aks-mcp/internal/config"
"github.com/Azure/aks-mcp/internal/k8s"
"github.com/Azure/aks-mcp/internal/tools"
"github.com/Azure/aks-mcp/internal/version"
"github.com/Azure/mcp-kubernetes/pkg/cilium"
"github.com/Azure/mcp-kubernetes/pkg/helm"
"github.com/Azure/mcp-kubernetes/pkg/kubectl"
"github.com/mark3labs/mcp-go/server"
)

Expand Down Expand Up @@ -62,6 +66,9 @@ func (s *Service) Initialize() error {
// Register AKS Control Plane tools
s.registerControlPlaneTools()

// Register Kubernetes tools
s.registerKubernetesTools()

return nil
}

Expand Down Expand Up @@ -315,3 +322,61 @@ func (s *Service) registerAdvisorTools() {
advisorTool := advisor.RegisterAdvisorRecommendationTool()
s.mcpServer.AddTool(advisorTool, tools.CreateResourceHandler(advisor.GetAdvisorRecommendationHandler(s.cfg), s.cfg))
}

// registerKubernetesTools registers Kubernetes-related tools (kubectl, helm, cilium)
func (s *Service) registerKubernetesTools() {
log.Println("Registering Kubernetes tools...")

// Register kubectl commands based on access level
s.registerKubectlCommands()

// Register helm if enabled
if s.cfg.AdditionalTools["helm"] {
log.Println("Registering Kubernetes tool: helm")
helmTool := helm.RegisterHelm()
helmExecutor := k8s.WrapK8sExecutor(helm.NewExecutor())
s.mcpServer.AddTool(helmTool, tools.CreateToolHandler(helmExecutor, s.cfg))
}

// Register cilium if enabled
if s.cfg.AdditionalTools["cilium"] {
log.Println("Registering Kubernetes tool: cilium")
ciliumTool := cilium.RegisterCilium()
ciliumExecutor := k8s.WrapK8sExecutor(cilium.NewExecutor())
s.mcpServer.AddTool(ciliumTool, tools.CreateToolHandler(ciliumExecutor, s.cfg))
}
}

// registerKubectlCommands registers kubectl commands based on access level
func (s *Service) registerKubectlCommands() {
// Register read-only kubectl commands (available at all access levels)
for _, cmd := range kubectl.GetReadOnlyKubectlCommands() {
log.Printf("Registering kubectl command: %s", cmd.Name)
kubectlTool := kubectl.RegisterKubectlCommand(cmd)
k8sExecutor := kubectl.CreateCommandExecutorFunc(cmd.Name)
wrappedExecutor := k8s.WrapK8sExecutorFunc(k8sExecutor)
s.mcpServer.AddTool(kubectlTool, tools.CreateToolHandler(wrappedExecutor, s.cfg))
}

// Register read-write commands if access level is readwrite or admin
if s.cfg.AccessLevel == "readwrite" || s.cfg.AccessLevel == "admin" {
for _, cmd := range kubectl.GetReadWriteKubectlCommands() {
log.Printf("Registering kubectl command: %s", cmd.Name)
kubectlTool := kubectl.RegisterKubectlCommand(cmd)
k8sExecutor := kubectl.CreateCommandExecutorFunc(cmd.Name)
wrappedExecutor := k8s.WrapK8sExecutorFunc(k8sExecutor)
s.mcpServer.AddTool(kubectlTool, tools.CreateToolHandler(wrappedExecutor, s.cfg))
}
}

// Register admin commands only if access level is admin
if s.cfg.AccessLevel == "admin" {
for _, cmd := range kubectl.GetAdminKubectlCommands() {
log.Printf("Registering kubectl command: %s", cmd.Name)
kubectlTool := kubectl.RegisterKubectlCommand(cmd)
k8sExecutor := kubectl.CreateCommandExecutorFunc(cmd.Name)
wrappedExecutor := k8s.WrapK8sExecutorFunc(k8sExecutor)
s.mcpServer.AddTool(kubectlTool, tools.CreateToolHandler(wrappedExecutor, s.cfg))
}
}
}
Loading