Skip to main content
Glama
tool.go11.7 kB
package create import ( _ "embed" "fmt" "strings" "text/template" "github.com/containers/kubernetes-mcp-server/pkg/api" "github.com/containers/kubernetes-mcp-server/pkg/kubernetes" "github.com/containers/kubernetes-mcp-server/pkg/kubevirt" "github.com/containers/kubernetes-mcp-server/pkg/output" "github.com/google/jsonschema-go/jsonschema" "k8s.io/utils/ptr" ) //go:embed vm.yaml.tmpl var vmYamlTemplate string func Tools() []api.ServerTool { return []api.ServerTool{ { Tool: api.Tool{ Name: "vm_create", Description: "Create a VirtualMachine in the cluster with the specified configuration, automatically resolving instance types, preferences, and container disk images. VM will be created in Halted state by default; use autostart parameter to start it immediately.", InputSchema: &jsonschema.Schema{ Type: "object", Properties: map[string]*jsonschema.Schema{ "namespace": { Type: "string", Description: "The namespace for the virtual machine", }, "name": { Type: "string", Description: "The name of the virtual machine", }, "workload": { Type: "string", Description: "The workload for the VM. Accepts OS names (e.g., 'fedora' (default), 'ubuntu', 'centos', 'centos-stream', 'debian', 'rhel', 'opensuse', 'opensuse-tumbleweed', 'opensuse-leap') or full container disk image URLs", Examples: []any{"fedora", "ubuntu", "centos", "debian", "rhel", "quay.io/containerdisks/fedora:latest"}, }, "instancetype": { Type: "string", Description: "Optional instance type name for the VM (e.g., 'u1.small', 'u1.medium', 'u1.large')", }, "preference": { Type: "string", Description: "Optional preference name for the VM", }, "size": { Type: "string", Description: "Optional workload size hint for the VM (e.g., 'small', 'medium', 'large', 'xlarge'). Used to auto-select an appropriate instance type if not explicitly specified.", Examples: []any{"small", "medium", "large"}, }, "performance": { Type: "string", Description: "Optional performance family hint for the VM instance type (e.g., 'u1' for general-purpose, 'o1' for overcommitted, 'c1' for compute-optimized, 'm1' for memory-optimized). Defaults to 'u1' (general-purpose) if not specified.", Examples: []any{"general-purpose", "overcommitted", "compute-optimized", "memory-optimized"}, }, "autostart": { Type: "boolean", Description: "Optional flag to automatically start the VM after creation (sets runStrategy to Always instead of Halted). Defaults to false.", }, "storage": { Type: "string", Description: "Optional storage size for the VM's root disk when using DataSources (e.g., '30Gi', '50Gi', '100Gi'). Defaults to 30Gi. Ignored when using container disks.", Examples: []any{"30Gi", "50Gi", "100Gi"}, }, }, Required: []string{"namespace", "name"}, }, Annotations: api.ToolAnnotations{ Title: "Virtual Machine: Create", ReadOnlyHint: ptr.To(false), DestructiveHint: ptr.To(true), IdempotentHint: ptr.To(true), OpenWorldHint: ptr.To(false), }, }, Handler: create, }, } } type vmParams struct { Namespace string Name string ContainerDisk string Instancetype string InstancetypeKind string Preference string PreferenceKind string UseDataSource bool DataSourceName string DataSourceNamespace string Storage string RunStrategy string } func create(params api.ToolHandlerParams) (*api.ToolCallResult, error) { // Parse and validate input parameters createParams, err := parseCreateParameters(params) if err != nil { return api.NewToolCallResult("", err), nil } // Type assert to concrete type to access AccessControlClientset k8s, ok := params.KubernetesClient.(*kubernetes.Kubernetes) if !ok { return api.NewToolCallResult("", fmt.Errorf("kubernetes client type assertion failed")), nil } dynamicClient := k8s.AccessControlClientset().DynamicClient() // Search for available DataSources dataSources := kubevirt.SearchDataSources(params.Context, dynamicClient) // Match DataSource based on workload input matchedDataSource := kubevirt.MatchDataSource(dataSources, createParams.Workload) // Search for preferences and instancetypes preferences := kubevirt.SearchPreferences(params.Context, dynamicClient, createParams.Namespace) instancetypes := kubevirt.SearchInstancetypes(params.Context, dynamicClient, createParams.Namespace) // Resolve preference from DataSource defaults or cluster resources preferenceInfo := kubevirt.ResolvePreference(preferences, createParams.Preference, createParams.Workload, matchedDataSource) // Resolve instancetype from DataSource defaults or size/performance hints instancetypeInfo := kubevirt.ResolveInstancetype(instancetypes, createParams.Instancetype, createParams.Size, createParams.Performance, matchedDataSource) // Build template parameters from resolved resources templateParams := buildTemplateParams(createParams, matchedDataSource, instancetypeInfo, preferenceInfo) // Render the VM YAML vmYaml, err := renderVMYaml(templateParams) if err != nil { return api.NewToolCallResult("", err), nil } // Create the VM in the cluster resources, err := params.ResourcesCreateOrUpdate(params, vmYaml) if err != nil { return api.NewToolCallResult("", fmt.Errorf("failed to create VirtualMachine: %w", err)), nil } // Format the output marshalledYaml, err := output.MarshalYaml(resources) if err != nil { return api.NewToolCallResult("", fmt.Errorf("failed to marshal created VirtualMachine: %w", err)), nil } return api.NewToolCallResult("# VirtualMachine created successfully\n"+marshalledYaml, nil), nil } // createParameters holds parsed input parameters for VM creation type createParameters struct { Namespace string Name string Workload string Instancetype string Preference string Size string Performance string Storage string Autostart bool } // parseCreateParameters parses and validates input parameters func parseCreateParameters(params api.ToolHandlerParams) (*createParameters, error) { namespace, err := api.RequiredString(params, "namespace") if err != nil { return nil, err } name, err := api.RequiredString(params, "name") if err != nil { return nil, err } return &createParameters{ Namespace: namespace, Name: name, Workload: api.OptionalString(params, "workload", "fedora"), Instancetype: api.OptionalString(params, "instancetype", ""), Preference: api.OptionalString(params, "preference", ""), Size: api.OptionalString(params, "size", ""), Performance: normalizePerformance(api.OptionalString(params, "performance", "")), Storage: api.OptionalString(params, "storage", "30Gi"), Autostart: api.OptionalBool(params, "autostart", false), }, nil } // buildTemplateParams constructs the template parameters for VM creation func buildTemplateParams(createParams *createParameters, matchedDataSource *kubevirt.DataSourceInfo, instancetypeInfo *kubevirt.InstancetypeInfo, preferenceInfo *kubevirt.PreferenceInfo) vmParams { // Determine runStrategy based on autostart parameter runStrategy := "Halted" if createParams.Autostart { runStrategy = "Always" } params := vmParams{ Namespace: createParams.Namespace, Name: createParams.Name, Storage: createParams.Storage, RunStrategy: runStrategy, } // Set instancetype and kind if available if instancetypeInfo != nil { params.Instancetype = instancetypeInfo.Name if instancetypeInfo.Namespace == "" { params.InstancetypeKind = "VirtualMachineClusterInstancetype" } else { params.InstancetypeKind = "VirtualMachineInstancetype" } } // Set preference and kind if available if preferenceInfo != nil { params.Preference = preferenceInfo.Name if preferenceInfo.Namespace == "" { params.PreferenceKind = "VirtualMachineClusterPreference" } else { params.PreferenceKind = "VirtualMachinePreference" } } if matchedDataSource != nil && matchedDataSource.Namespace != "" { // Use the matched DataSource (real cluster DataSource with namespace) params.UseDataSource = true params.DataSourceName = matchedDataSource.Name params.DataSourceNamespace = matchedDataSource.Namespace } else if matchedDataSource != nil { // Matched a built-in containerdisk (no namespace) params.ContainerDisk = matchedDataSource.Source } else { // No match, resolve container disk image from workload name params.ContainerDisk = resolveContainerDisk(createParams.Workload) } return params } // renderVMYaml renders the VM YAML from template func renderVMYaml(templateParams vmParams) (string, error) { tmpl, err := template.New("vm").Parse(vmYamlTemplate) if err != nil { return "", fmt.Errorf("failed to parse template: %w", err) } var result strings.Builder if err := tmpl.Execute(&result, templateParams); err != nil { return "", fmt.Errorf("failed to render template: %w", err) } return result.String(), nil } func normalizePerformance(performance string) string { // Normalize to lowercase and trim spaces normalized := strings.ToLower(strings.TrimSpace(performance)) // Map natural language terms to instance type prefixes performanceMap := map[string]string{ "general-purpose": "u1", "generalpurpose": "u1", "general": "u1", "overcommitted": "o1", "compute": "c1", "compute-optimized": "c1", "computeoptimized": "c1", "memory-optimized": "m1", "memoryoptimized": "m1", "memory": "m1", "u1": "u1", "o1": "o1", "c1": "c1", "m1": "m1", } // Look up the mapping if prefix, exists := performanceMap[normalized]; exists { return prefix } // Default to "u1" (general-purpose) if not recognized or empty return "u1" } // resolveContainerDisk resolves OS names to container disk images from quay.io/containerdisks func resolveContainerDisk(input string) string { // If input already looks like a container image, return as-is if strings.Contains(input, "/") || strings.Contains(input, ":") { return input } // Common OS name mappings to containerdisk images osMap := map[string]string{ "fedora": "quay.io/containerdisks/fedora:latest", "ubuntu": "quay.io/containerdisks/ubuntu:24.04", "centos": "quay.io/containerdisks/centos-stream:9-latest", "centos-stream": "quay.io/containerdisks/centos-stream:9-latest", "debian": "quay.io/containerdisks/debian:latest", "opensuse": "quay.io/containerdisks/opensuse-tumbleweed:1.0.0", "opensuse-tumbleweed": "quay.io/containerdisks/opensuse-tumbleweed:1.0.0", "opensuse-leap": "quay.io/containerdisks/opensuse-leap:15.6", "rhel8": "registry.redhat.io/rhel8/rhel-guest-image:latest", "rhel9": "registry.redhat.io/rhel9/rhel-guest-image:latest", "rhel10": "registry.redhat.io/rhel10/rhel-guest-image:latest", } // Normalize input to lowercase for lookup normalized := strings.ToLower(strings.TrimSpace(input)) // Look up the OS name if containerDisk, exists := osMap[normalized]; exists { return containerDisk } // If no match found, return the input as-is (assume it's a valid container image URL) return input }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/containers/kubernetes-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server