Skip to main content
Glama

mcp-k8s-go

apply_k8s_resource_tool.go4.44 kB
package tools import ( "bytes" "context" "fmt" "io" "strings" "github.com/strowk/foxy-contexts/pkg/fxctx" "github.com/strowk/foxy-contexts/pkg/mcp" "github.com/strowk/foxy-contexts/pkg/toolinput" "github.com/strowk/mcp-k8s-go/internal/k8s" "github.com/strowk/mcp-k8s-go/internal/utils" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/yaml" "k8s.io/client-go/discovery" "k8s.io/client-go/dynamic" ) func NewApplyK8sResourceTool(clientPool k8s.ClientPool) fxctx.Tool { contextProperty := "context" manifestProperty := "manifest" inputSchema := toolinput.NewToolInputSchema( toolinput.WithString(contextProperty, "Name of the Kubernetes context to use, defaults to current context"), toolinput.WithRequiredString(manifestProperty, "YAML manifest of the resource to apply"), ) return fxctx.NewTool( &mcp.Tool{ Name: "apply-k8s-resource", Description: utils.Ptr("Create or modify a Kubernetes resource from a YAML manifest"), InputSchema: inputSchema.GetMcpToolInputSchema(), }, func(ctx context.Context, args map[string]any) *mcp.CallToolResult { input, err := inputSchema.Validate(args) if err != nil { return utils.ErrResponse(err) } k8sCtx := input.StringOr(contextProperty, "") clientset, err := clientPool.GetClientset(k8sCtx) if err != nil { return utils.ErrResponse(err) } manifest, err := input.String(manifestProperty) if err != nil { return utils.ErrResponse(err) } dynamicClient, err := clientPool.GetDynamicClient(k8sCtx) if err != nil { return utils.ErrResponse(fmt.Errorf("failed to retrieve dynamic client from the client pool: %w", err)) } decoder := yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(manifest)), 4096) var results []string for { obj := &unstructured.Unstructured{} if err := decoder.Decode(obj); err != nil { if err == io.EOF { break } return utils.ErrResponse(fmt.Errorf("failed to unmarshal manifest: %w", err)) } if obj.Object == nil { continue } namespace := obj.GetNamespace() if namespace == "" { namespace = "default" } gvk := obj.GroupVersionKind() apiResource, err := findAPIResource(clientset.Discovery(), gvk) if err != nil { return utils.ErrResponse(fmt.Errorf("failed to find API resource: %w", err)) } var dr dynamic.ResourceInterface if apiResource.Namespaced { dr = dynamicClient.Resource(gvk.GroupVersion().WithResource(apiResource.Name)).Namespace(namespace) } else { dr = dynamicClient.Resource(gvk.GroupVersion().WithResource(apiResource.Name)) } action := "configured" if _, err := dr.Get(ctx, obj.GetName(), metav1.GetOptions{}); errors.IsNotFound(err) { action = "created" } rawObj, err := obj.MarshalJSON() if err != nil { return utils.ErrResponse(fmt.Errorf("failed to marshal resource: %w", err)) } _, err = dr.Patch(ctx, obj.GetName(), types.ApplyPatchType, rawObj, metav1.PatchOptions{ FieldManager: "mcp-k8s-go", }) if err != nil { return utils.ErrResponse(fmt.Errorf("failed to patch resource: %w", err)) } resourceText := fmt.Sprintf("%s.%s/%s", strings.ToLower(apiResource.Name), gvk.Group, obj.GetName()) if gvk.Group == "" { resourceText = fmt.Sprintf("%s/%s", strings.ToLower(apiResource.Name), obj.GetName()) } results = append(results, fmt.Sprintf("%s %s", resourceText, action)) } return &mcp.CallToolResult{ Content: []any{ mcp.TextContent{ Type: "text", Text: strings.Join(results, "\n"), }, }, } }, ) } func findAPIResource(discoveryClient discovery.DiscoveryInterface, gvk schema.GroupVersionKind) (*metav1.APIResource, error) { apiResourceList, err := discoveryClient.ServerResourcesForGroupVersion(gvk.GroupVersion().String()) if err != nil { return nil, fmt.Errorf("failed to get server resources for group version %s: %w", gvk.GroupVersion().String(), err) } for _, apiResource := range apiResourceList.APIResources { if apiResource.Kind == gvk.Kind { return &apiResource, nil } } return nil, fmt.Errorf("resource kind %s not found in group version %s", gvk.Kind, gvk.GroupVersion().String()) }

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/strowk/mcp-k8s-go'

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