| 
1 | 1 | package compute  | 
2 | 2 | 
 
  | 
3 | 3 | import (  | 
4 |  | -	"github.com/Azure/aks-mcp/internal/utils"  | 
 | 4 | +	"fmt"  | 
 | 5 | +	"slices"  | 
 | 6 | + | 
 | 7 | +	"github.com/Azure/aks-mcp/internal/config"  | 
5 | 8 | 	"github.com/mark3labs/mcp-go/mcp"  | 
6 | 9 | )  | 
7 | 10 | 
 
  | 
8 |  | -// ComputeCommand defines a specific az vmss command to be registered as a tool  | 
9 |  | -type ComputeCommand struct {  | 
10 |  | -	Name        string  | 
11 |  | -	Description string  | 
12 |  | -	ArgsExample string // Example of command arguments  | 
13 |  | -}  | 
 | 11 | +// ComputeOperationType defines the type of compute operation  | 
 | 12 | +type ComputeOperationType string  | 
 | 13 | + | 
 | 14 | +// ResourceType defines the compute resource type  | 
 | 15 | +type ResourceType string  | 
 | 16 | + | 
 | 17 | +const (  | 
 | 18 | +	// VM operations - safe operations only  | 
 | 19 | +	OpVMShow            ComputeOperationType = "show"  | 
 | 20 | +	OpVMList            ComputeOperationType = "list"  | 
 | 21 | +	OpVMStart           ComputeOperationType = "start"  | 
 | 22 | +	OpVMStop            ComputeOperationType = "stop"  | 
 | 23 | +	OpVMRestart         ComputeOperationType = "restart"  | 
 | 24 | +	OpVMGetInstanceView ComputeOperationType = "get-instance-view"  | 
 | 25 | +	OpVMRunCommand      ComputeOperationType = "run-command"  | 
 | 26 | + | 
 | 27 | +	// VMSS operations - only safe operations for AKS-managed VMSS  | 
 | 28 | +	OpVMSSShow            ComputeOperationType = "show"  | 
 | 29 | +	OpVMSSList            ComputeOperationType = "list"  | 
 | 30 | +	OpVMSSRestart         ComputeOperationType = "restart"  | 
 | 31 | +	OpVMSSReimage         ComputeOperationType = "reimage"  | 
 | 32 | +	OpVMSSGetInstanceView ComputeOperationType = "get-instance-view"  | 
 | 33 | +	OpVMSSRunCommand      ComputeOperationType = "run-command"  | 
 | 34 | + | 
 | 35 | +	// Resource types  | 
 | 36 | +	ResourceTypeVM   ResourceType = "vm"  | 
 | 37 | +	ResourceTypeVMSS ResourceType = "vmss"  | 
 | 38 | +)  | 
 | 39 | + | 
 | 40 | +// generateToolDescription creates a tool description based on access level  | 
 | 41 | +func generateToolDescription(accessLevel string) string {  | 
 | 42 | +	baseDesc := `Unified tool for managing Azure Virtual Machines (VMs) and Virtual Machine Scale Sets (VMSS) using Azure CLI.  | 
 | 43 | +
  | 
 | 44 | +IMPORTANT: VM/VMSS resources are managed by AKS. Write operations should be used carefully and only for debugging purposes.  | 
 | 45 | +
  | 
 | 46 | +Use resource_type="vm" for single virtual machines or resource_type="vmss" for virtual machine scale sets.  | 
 | 47 | +
  | 
 | 48 | +Available operation values:`  | 
14 | 49 | 
 
  | 
15 |  | -// RegisterAzComputeCommand registers a specific az vmss command as an MCP tool  | 
16 |  | -func RegisterAzComputeCommand(cmd ComputeCommand) mcp.Tool {  | 
17 |  | -	// Convert spaces to underscores for valid tool name  | 
18 |  | -	commandName := cmd.Name  | 
19 |  | -	validToolName := utils.ReplaceSpacesWithUnderscores(commandName)  | 
 | 50 | +	// Add operations by access level  | 
 | 51 | +	desc := baseDesc + "\n"  | 
20 | 52 | 
 
  | 
21 |  | -	description := "Run " + cmd.Name + " command: " + cmd.Description + "."  | 
 | 53 | +	// Basic operations for all access levels  | 
 | 54 | +	desc += "- show: Get details of a VM/VMSS\n"  | 
 | 55 | +	desc += "- list: List VMs/VMSS in subscription or resource group\n"  | 
 | 56 | +	desc += "- get-instance-view: Get runtime status\n"  | 
22 | 57 | 
 
  | 
23 |  | -	// Add example if available, with proper punctuation  | 
24 |  | -	if cmd.ArgsExample != "" {  | 
25 |  | -		description += "\nExample: `" + cmd.ArgsExample + "`"  | 
 | 58 | +	// Management operations for readwrite/admin  | 
 | 59 | +	if accessLevel == "readwrite" || accessLevel == "admin" {  | 
 | 60 | +		desc += "- start: Start VM\n"  | 
 | 61 | +		desc += "- stop: Stop VM\n"  | 
 | 62 | +		desc += "- restart: Restart VM/VMSS instances\n"  | 
 | 63 | +		desc += "- run-command: Execute commands remotely on VM/VMSS instances\n"  | 
 | 64 | +		desc += "- reimage: Reimage VMSS instances (VM not supported for reimage)\n"  | 
26 | 65 | 	}  | 
27 | 66 | 
 
  | 
28 |  | -	return mcp.NewTool(validToolName,  | 
 | 67 | +	// Note: All destructive operations (create, delete, deallocate, update, resize, scale)  | 
 | 68 | +	// have been removed for AKS environment safety  | 
 | 69 | + | 
 | 70 | +	// Examples  | 
 | 71 | +	desc += "\nEXAMPLES:\n"  | 
 | 72 | +	desc += `List VMSS: operation="list", resource_type="vmss", args="--resource-group myRG"` + "\n"  | 
 | 73 | +	desc += `Show VMSS: operation="show", resource_type="vmss", args="--name myVMSS --resource-group myRG"` + "\n"  | 
 | 74 | +	desc += `List VMs: operation="list", resource_type="vm", args="--resource-group myRG"` + "\n"  | 
 | 75 | + | 
 | 76 | +	if accessLevel == "readwrite" || accessLevel == "admin" {  | 
 | 77 | +		desc += `Restart VMSS: operation="restart", resource_type="vmss", args="--name myVMSS --resource-group myRG"` + "\n"  | 
 | 78 | +		desc += `Reimage VMSS: operation="reimage", resource_type="vmss", args="--name myVMSS --resource-group myRG"` + "\n"  | 
 | 79 | +		desc += `Run command on VM: operation="run-command", resource_type="vm", args="--name myVM --resource-group myRG --command-id RunShellScript --scripts 'echo hello'"` + "\n"  | 
 | 80 | +		desc += `Run command on VMSS: operation="run-command", resource_type="vmss", args="--name myVMSS --resource-group myRG --command-id RunShellScript --scripts 'hostname' --instance-id 0"` + "\n"  | 
 | 81 | +	}  | 
 | 82 | + | 
 | 83 | +	return desc  | 
 | 84 | +}  | 
 | 85 | + | 
 | 86 | +// RegisterAzComputeOperations registers the unified compute operations tool  | 
 | 87 | +func RegisterAzComputeOperations(cfg *config.ConfigData) mcp.Tool {  | 
 | 88 | +	description := generateToolDescription(cfg.AccessLevel)  | 
 | 89 | + | 
 | 90 | +	return mcp.NewTool("az_compute_operations",  | 
29 | 91 | 		mcp.WithDescription(description),  | 
 | 92 | +		mcp.WithString("operation",  | 
 | 93 | +			mcp.Required(),  | 
 | 94 | +			mcp.Description("Operation to perform. Common operations: list, show, start, stop, restart, deallocate, run-command, scale, etc."),  | 
 | 95 | +		),  | 
 | 96 | +		mcp.WithString("resource_type",  | 
 | 97 | +			mcp.Required(),  | 
 | 98 | +			mcp.Description("Resource type: 'vm' (single virtual machine) or 'vmss' (virtual machine scale set)"),  | 
 | 99 | +		),  | 
30 | 100 | 		mcp.WithString("args",  | 
31 | 101 | 			mcp.Required(),  | 
32 |  | -			mcp.Description("Arguments for the `"+cmd.Name+"` command"),  | 
 | 102 | +			mcp.Description("Azure CLI arguments: '--resource-group myRG' (required for most operations), '--name myVM' (for specific resources), '--new-capacity 3' (for scaling)"),  | 
33 | 103 | 		),  | 
34 | 104 | 	)  | 
35 | 105 | }  | 
36 | 106 | 
 
  | 
37 |  | -// GetReadOnlyVmssCommands returns all read-only az vmss commands  | 
38 |  | -func GetReadOnlyVmssCommands() []ComputeCommand {  | 
39 |  | -	return []ComputeCommand{  | 
40 |  | -		// No read-only commands for now  | 
 | 107 | +// GetOperationAccessLevel returns the required access level for an operation  | 
 | 108 | +func GetOperationAccessLevel(operation string) string {  | 
 | 109 | +	readOnlyOps := []string{  | 
 | 110 | +		string(OpVMShow), string(OpVMList), string(OpVMGetInstanceView),  | 
 | 111 | +		string(OpVMSSShow), string(OpVMSSList), string(OpVMSSGetInstanceView),  | 
 | 112 | +	}  | 
 | 113 | + | 
 | 114 | +	readWriteOps := []string{  | 
 | 115 | +		// VM operations - safe operations only  | 
 | 116 | +		string(OpVMStart), string(OpVMStop), string(OpVMRestart), string(OpVMRunCommand),  | 
 | 117 | +		// VMSS operations - only safe operations for AKS-managed VMSS  | 
 | 118 | +		string(OpVMSSRestart), string(OpVMSSReimage), string(OpVMSSRunCommand),  | 
 | 119 | +	}  | 
 | 120 | + | 
 | 121 | +	// No admin operations - all unsafe operations removed  | 
 | 122 | +	adminOps := []string{}  | 
 | 123 | + | 
 | 124 | +	if slices.Contains(readOnlyOps, operation) {  | 
 | 125 | +		return "readonly"  | 
 | 126 | +	}  | 
 | 127 | + | 
 | 128 | +	if slices.Contains(readWriteOps, operation) {  | 
 | 129 | +		return "readwrite"  | 
 | 130 | +	}  | 
 | 131 | + | 
 | 132 | +	if slices.Contains(adminOps, operation) {  | 
 | 133 | +		return "admin"  | 
41 | 134 | 	}  | 
 | 135 | + | 
 | 136 | +	return "unknown"  | 
42 | 137 | }  | 
43 | 138 | 
 
  | 
44 |  | -// GetReadWriteVmssCommands returns all read-write az vmss commands  | 
45 |  | -func GetReadWriteVmssCommands() []ComputeCommand {  | 
46 |  | -	return []ComputeCommand{  | 
47 |  | -		// Run command execution  | 
48 |  | -		{Name: "az vmss run-command invoke", Description: "Execute a command on instances of a Virtual Machine Scale Set", ArgsExample: "--name myVMSS --resource-group myResourceGroup --command-id RunShellScript --scripts 'echo Hello World' --instance-id 0"},  | 
 | 139 | +// ValidateOperationAccess checks if the operation is allowed for the given access level  | 
 | 140 | +func ValidateOperationAccess(operation string, cfg *config.ConfigData) error {  | 
 | 141 | +	requiredLevel := GetOperationAccessLevel(operation)  | 
 | 142 | + | 
 | 143 | +	switch requiredLevel {  | 
 | 144 | +	case "admin":  | 
 | 145 | +		if cfg.AccessLevel != "admin" {  | 
 | 146 | +			return fmt.Errorf("operation '%s' requires admin access level", operation)  | 
 | 147 | +		}  | 
 | 148 | +	case "readwrite":  | 
 | 149 | +		if cfg.AccessLevel != "readwrite" && cfg.AccessLevel != "admin" {  | 
 | 150 | +			return fmt.Errorf("operation '%s' requires readwrite or admin access level", operation)  | 
 | 151 | +		}  | 
 | 152 | +	case "readonly":  | 
 | 153 | +		// All access levels can perform readonly operations  | 
 | 154 | +	case "unknown":  | 
 | 155 | +		return fmt.Errorf("unknown operation: %s", operation)  | 
49 | 156 | 	}  | 
 | 157 | + | 
 | 158 | +	return nil  | 
50 | 159 | }  | 
51 | 160 | 
 
  | 
52 |  | -// GetAdminVmssCommands returns all admin az vmss commands  | 
53 |  | -func GetAdminVmssCommands() []ComputeCommand {  | 
54 |  | -	return []ComputeCommand{  | 
55 |  | -		// No admin commands for now  | 
 | 161 | +// MapOperationToCommand maps an operation and resource type to its corresponding az command  | 
 | 162 | +func MapOperationToCommand(operation string, resourceType string) (string, error) {  | 
 | 163 | +	// Validate resource type  | 
 | 164 | +	if resourceType != string(ResourceTypeVM) && resourceType != string(ResourceTypeVMSS) {  | 
 | 165 | +		return "", fmt.Errorf("invalid resource type: %s (must be 'vm' or 'vmss')", resourceType)  | 
 | 166 | +	}  | 
 | 167 | + | 
 | 168 | +	commandMap := map[string]map[string]string{  | 
 | 169 | +		string(ResourceTypeVM): {  | 
 | 170 | +			// Safe VM operations only  | 
 | 171 | +			string(OpVMShow):            "az vm show",  | 
 | 172 | +			string(OpVMList):            "az vm list",  | 
 | 173 | +			string(OpVMStart):           "az vm start",  | 
 | 174 | +			string(OpVMStop):            "az vm stop",  | 
 | 175 | +			string(OpVMRestart):         "az vm restart",  | 
 | 176 | +			string(OpVMGetInstanceView): "az vm get-instance-view",  | 
 | 177 | +			string(OpVMRunCommand):      "az vm run-command invoke",  | 
 | 178 | +		},  | 
 | 179 | +		string(ResourceTypeVMSS): {  | 
 | 180 | +			// Read-only operations  | 
 | 181 | +			string(OpVMSSShow):            "az vmss show",  | 
 | 182 | +			string(OpVMSSList):            "az vmss list",  | 
 | 183 | +			string(OpVMSSGetInstanceView): "az vmss get-instance-view",  | 
 | 184 | +			// Safe operations for AKS-managed VMSS  | 
 | 185 | +			string(OpVMSSRestart):    "az vmss restart",  | 
 | 186 | +			string(OpVMSSReimage):    "az vmss reimage",  | 
 | 187 | +			string(OpVMSSRunCommand): "az vmss run-command invoke",  | 
 | 188 | +			// Removed unsafe operations: create, delete, start, stop, deallocate, scale, update  | 
 | 189 | +		},  | 
56 | 190 | 	}  | 
 | 191 | + | 
 | 192 | +	resourceCommands, exists := commandMap[resourceType]  | 
 | 193 | +	if !exists {  | 
 | 194 | +		return "", fmt.Errorf("unsupported resource type: %s", resourceType)  | 
 | 195 | +	}  | 
 | 196 | + | 
 | 197 | +	cmd, exists := resourceCommands[operation]  | 
 | 198 | +	if !exists {  | 
 | 199 | +		return "", fmt.Errorf("unsupported operation '%s' for resource type '%s'", operation, resourceType)  | 
 | 200 | +	}  | 
 | 201 | + | 
 | 202 | +	return cmd, nil  | 
57 | 203 | }  | 
0 commit comments