mirror of
https://github.com/openshift/openshift-mcp-server.git
synced 2025-10-17 14:27:48 +03:00
feat: improved prompt efficiency for OpenShift resources
This commit is contained in:
18
pkg/kubernetes/openshift.go
Normal file
18
pkg/kubernetes/openshift.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package kubernetes
|
||||
|
||||
import (
|
||||
"context"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
func (k *Kubernetes) IsOpenShift(ctx context.Context) bool {
|
||||
if _, err := k.dynamicClient.Resource(schema.GroupVersionResource{
|
||||
Group: "project.openshift.io",
|
||||
Version: "v1",
|
||||
Resource: "projects",
|
||||
}).List(ctx, metav1.ListOptions{Limit: 1}); err == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -174,21 +174,29 @@ func (c *mcpContext) withEnvTest() {
|
||||
// inOpenShift sets up the kubernetes environment to seem to be running OpenShift
|
||||
func (c *mcpContext) inOpenShift() func() {
|
||||
c.withKubeConfig(envTestRestConfig)
|
||||
return c.crdApply(`
|
||||
crdTemplate := `
|
||||
{
|
||||
"apiVersion": "apiextensions.k8s.io/v1",
|
||||
"kind": "CustomResourceDefinition",
|
||||
"metadata": {"name": "routes.route.openshift.io"},
|
||||
"metadata": {"name": "%s"},
|
||||
"spec": {
|
||||
"group": "route.openshift.io",
|
||||
"group": "%s",
|
||||
"versions": [{
|
||||
"name": "v1","served": true,"storage": true,
|
||||
"schema": {"openAPIV3Schema": {"type": "object","x-kubernetes-preserve-unknown-fields": true}}
|
||||
}],
|
||||
"scope": "Namespaced",
|
||||
"names": {"plural": "routes","singular": "route","kind": "Route"}
|
||||
"names": {"plural": "%s","singular": "%s","kind": "%s"}
|
||||
}
|
||||
}`)
|
||||
}`
|
||||
removeProjects := c.crdApply(fmt.Sprintf(crdTemplate, "projects.project.openshift.io", "project.openshift.io",
|
||||
"projects", "project", "Project"))
|
||||
removeRoutes := c.crdApply(fmt.Sprintf(crdTemplate, "routes.route.openshift.io", "route.openshift.io",
|
||||
"routes", "route", "Route"))
|
||||
return func() {
|
||||
removeProjects()
|
||||
removeRoutes()
|
||||
}
|
||||
}
|
||||
|
||||
// newKubernetesClient creates a new Kubernetes client with the envTest kubeconfig
|
||||
@@ -224,7 +232,7 @@ func (c *mcpContext) crdApply(resource string) func() {
|
||||
}
|
||||
c.crdWaitUntilReady(crd.Name)
|
||||
return func() {
|
||||
err = apiExtensionsV1Client.CustomResourceDefinitions().Delete(c.ctx, "routes.route.openshift.io", metav1.DeleteOptions{})
|
||||
err = apiExtensionsV1Client.CustomResourceDefinitions().Delete(c.ctx, crd.Name, metav1.DeleteOptions{})
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to delete CRD %v", err))
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package mcp
|
||||
|
||||
import (
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -41,3 +42,28 @@ func TestTools(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestToolsInOpenShift(t *testing.T) {
|
||||
testCase(t, func(c *mcpContext) {
|
||||
defer c.inOpenShift()() // n.b. two sets of parentheses to invoke the first function
|
||||
c.mcpServer.server.AddTools(c.mcpServer.initResources()...)
|
||||
tools, err := c.mcpClient.ListTools(c.ctx, mcp.ListToolsRequest{})
|
||||
t.Run("ListTools returns tools", func(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("call ListTools failed %v", err)
|
||||
return
|
||||
}
|
||||
})
|
||||
t.Run("ListTools has resources_list tool with OpenShift hint", func(t *testing.T) {
|
||||
if tools.Tools[10].Name != "resources_list" {
|
||||
t.Fatalf("tool resources_list not found")
|
||||
return
|
||||
}
|
||||
if !strings.Contains(tools.Tools[10].Description, ", route.openshift.io/v1 Route") {
|
||||
t.Fatalf("tool resources_list does not have OpenShift hint, got %s", tools.Tools[9].Description)
|
||||
return
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -10,10 +10,15 @@ import (
|
||||
)
|
||||
|
||||
func (s *Server) initResources() []server.ServerTool {
|
||||
commonApiVersion := "v1 Pod, v1 Service, apps/v1 Deployment, networking.k8s.io/v1 Ingress"
|
||||
if s.k.IsOpenShift(context.Background()) {
|
||||
commonApiVersion += ", route.openshift.io/v1 Route"
|
||||
}
|
||||
commonApiVersion = fmt.Sprintf("(common apiVersion and kind include: %s)", commonApiVersion)
|
||||
return []server.ServerTool{
|
||||
{mcp.NewTool("resources_list",
|
||||
mcp.WithDescription("List Kubernetes resources and objects in the current cluster by providing their apiVersion and kind and optionally the namespace\n"+
|
||||
"(typical apiVersion and kind include: v1 Pod, v1 Service, apps/v1 Deployment, networking.k8s.io/v1 Ingress)"),
|
||||
commonApiVersion),
|
||||
mcp.WithString("apiVersion",
|
||||
mcp.Description("apiVersion of the resources (examples of valid apiVersion are: v1, apps/v1, networking.k8s.io/v1)"),
|
||||
mcp.Required(),
|
||||
@@ -26,7 +31,7 @@ func (s *Server) initResources() []server.ServerTool {
|
||||
mcp.Description("Optional Namespace to retrieve the namespaced resources from (ignored in case of cluster scoped resources). If not provided, will list resources from all namespaces"))), s.resourcesList},
|
||||
{mcp.NewTool("resources_get",
|
||||
mcp.WithDescription("Get a Kubernetes resource in the current cluster by providing its apiVersion, kind, optionally the namespace, and its name\n"+
|
||||
"(typical apiVersion and kind include: v1 Pod, v1 Service, apps/v1 Deployment, networking.k8s.io/v1 Ingress)"),
|
||||
commonApiVersion),
|
||||
mcp.WithString("apiVersion",
|
||||
mcp.Description("apiVersion of the resource (examples of valid apiVersion are: v1, apps/v1, networking.k8s.io/v1)"),
|
||||
mcp.Required(),
|
||||
@@ -42,7 +47,7 @@ func (s *Server) initResources() []server.ServerTool {
|
||||
), s.resourcesGet},
|
||||
{mcp.NewTool("resources_create_or_update",
|
||||
mcp.WithDescription("Create or update a Kubernetes resource in the current cluster by providing a YAML or JSON representation of the resource\n"+
|
||||
"(typical apiVersion and kind include: v1 Pod, v1 Service, apps/v1 Deployment, networking.k8s.io/v1 Ingress)"),
|
||||
commonApiVersion),
|
||||
mcp.WithString("resource",
|
||||
mcp.Description("A JSON or YAML containing a representation of the Kubernetes resource. Should include top-level fields such as apiVersion,kind,metadata, and spec"),
|
||||
mcp.Required(),
|
||||
@@ -50,7 +55,7 @@ func (s *Server) initResources() []server.ServerTool {
|
||||
), s.resourcesCreateOrUpdate},
|
||||
{mcp.NewTool("resources_delete",
|
||||
mcp.WithDescription("Delete a Kubernetes resource in the current cluster by providing its apiVersion, kind, optionally the namespace, and its name\n"+
|
||||
"(typical apiVersion and kind include: v1 Pod, v1 Service, apps/v1 Deployment, networking.k8s.io/v1 Ingress)"),
|
||||
commonApiVersion),
|
||||
mcp.WithString("apiVersion",
|
||||
mcp.Description("apiVersion of the resource (examples of valid apiVersion are: v1, apps/v1, networking.k8s.io/v1)"),
|
||||
mcp.Required(),
|
||||
|
||||
Reference in New Issue
Block a user