mirror of
https://github.com/containers/kubernetes-mcp-server.git
synced 2025-10-23 01:22:57 +03:00
feat: added tool annotations
This commit is contained in:
@@ -15,6 +15,10 @@ func (s *Server) initConfiguration() []server.ServerTool {
|
||||
"If set to true, keeps only the current-context and the relevant pieces of the configuration for that context. "+
|
||||
"If set to false, all contexts, clusters, auth-infos, and users are returned in the configuration. "+
|
||||
"(Optional, default true)")),
|
||||
// Tool annotations
|
||||
mcp.WithTitleAnnotation("Configuration: View"),
|
||||
mcp.WithReadOnlyHintAnnotation(true),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
), s.configurationView},
|
||||
}
|
||||
return tools
|
||||
|
||||
@@ -13,6 +13,10 @@ func (s *Server) initEvents() []server.ServerTool {
|
||||
mcp.WithDescription("List all the Kubernetes events in the current cluster from all namespaces"),
|
||||
mcp.WithString("namespace",
|
||||
mcp.Description("Optional Namespace to retrieve the events from. If not provided, will list events from all namespaces")),
|
||||
// Tool annotations
|
||||
mcp.WithTitleAnnotation("Events: List"),
|
||||
mcp.WithReadOnlyHintAnnotation(true),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
), s.eventsList},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,16 +15,32 @@ func (s *Server) initHelm() []server.ServerTool {
|
||||
mcp.WithObject("values", mcp.Description("Values to pass to the Helm chart (Optional)")),
|
||||
mcp.WithString("name", mcp.Description("Name of the Helm release (Optional, random name if not provided)")),
|
||||
mcp.WithString("namespace", mcp.Description("Namespace to install the Helm chart in (Optional, current namespace if not provided)")),
|
||||
// Tool annotations
|
||||
mcp.WithTitleAnnotation("Helm: Install"),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithIdempotentHintAnnotation(false), // TODO: consider replacing implementation with equivalent to: helm upgrade --install
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
), s.helmInstall},
|
||||
{mcp.NewTool("helm_list",
|
||||
mcp.WithDescription("List all the Helm releases in the current or provided namespace (or in all namespaces if specified)"),
|
||||
mcp.WithString("namespace", mcp.Description("Namespace to list Helm releases from (Optional, all namespaces if not provided)")),
|
||||
mcp.WithBoolean("all_namespaces", mcp.Description("If true, lists all Helm releases in all namespaces ignoring the namespace argument (Optional)")),
|
||||
// Tool annotations
|
||||
mcp.WithTitleAnnotation("Helm: List"),
|
||||
mcp.WithReadOnlyHintAnnotation(true),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
), s.helmList},
|
||||
{mcp.NewTool("helm_uninstall",
|
||||
mcp.WithDescription("Uninstall a Helm release in the current or provided namespace"),
|
||||
mcp.WithString("name", mcp.Description("Name of the Helm release to uninstall"), mcp.Required()),
|
||||
mcp.WithString("namespace", mcp.Description("Namespace to uninstall the Helm release from (Optional, current namespace if not provided)")),
|
||||
// Tool annotations
|
||||
mcp.WithTitleAnnotation("Helm: Uninstall"),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithDestructiveHintAnnotation(true),
|
||||
mcp.WithIdempotentHintAnnotation(true),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
), s.helmUninstall},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,12 +12,20 @@ func (s *Server) initNamespaces() []server.ServerTool {
|
||||
ret = append(ret, server.ServerTool{
|
||||
Tool: mcp.NewTool("namespaces_list",
|
||||
mcp.WithDescription("List all the Kubernetes namespaces in the current cluster"),
|
||||
// Tool annotations
|
||||
mcp.WithTitleAnnotation("Namespaces: List"),
|
||||
mcp.WithReadOnlyHintAnnotation(true),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
), Handler: s.namespacesList,
|
||||
})
|
||||
if s.k.IsOpenShift(context.Background()) {
|
||||
ret = append(ret, server.ServerTool{
|
||||
Tool: mcp.NewTool("projects_list",
|
||||
mcp.WithDescription("List all the OpenShift projects in the current cluster"),
|
||||
// Tool annotations
|
||||
mcp.WithTitleAnnotation("Projects: List"),
|
||||
mcp.WithReadOnlyHintAnnotation(true),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
), Handler: s.projectsList,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,21 +14,39 @@ func (s *Server) initPods() []server.ServerTool {
|
||||
{Tool: mcp.NewTool("pods_list",
|
||||
mcp.WithDescription("List all the Kubernetes pods in the current cluster from all namespaces"),
|
||||
mcp.WithString("labelSelector", mcp.Description("Optional Kubernetes label selector (e.g. 'app=myapp,env=prod' or 'app in (myapp,yourapp)'), use this option when you want to filter the pods by label"), mcp.Pattern("([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]")),
|
||||
// Tool annotations
|
||||
mcp.WithTitleAnnotation("Pods: List"),
|
||||
mcp.WithReadOnlyHintAnnotation(true),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
), Handler: s.podsListInAllNamespaces},
|
||||
{Tool: mcp.NewTool("pods_list_in_namespace",
|
||||
mcp.WithDescription("List all the Kubernetes pods in the specified namespace in the current cluster"),
|
||||
mcp.WithString("namespace", mcp.Description("Namespace to list pods from"), mcp.Required()),
|
||||
mcp.WithString("labelSelector", mcp.Description("Optional Kubernetes label selector (e.g. 'app=myapp,env=prod' or 'app in (myapp,yourapp)'), use this option when you want to filter the pods by label"), mcp.Pattern("([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]")),
|
||||
// Tool annotations
|
||||
mcp.WithTitleAnnotation("Pods: List in Namespace"),
|
||||
mcp.WithReadOnlyHintAnnotation(true),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
), Handler: s.podsListInNamespace},
|
||||
{Tool: mcp.NewTool("pods_get",
|
||||
mcp.WithDescription("Get a Kubernetes Pod in the current or provided namespace with the provided name"),
|
||||
mcp.WithString("namespace", mcp.Description("Namespace to get the Pod from")),
|
||||
mcp.WithString("name", mcp.Description("Name of the Pod"), mcp.Required()),
|
||||
// Tool annotations
|
||||
mcp.WithTitleAnnotation("Pods: Get"),
|
||||
mcp.WithReadOnlyHintAnnotation(true),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
), Handler: s.podsGet},
|
||||
{Tool: mcp.NewTool("pods_delete",
|
||||
mcp.WithDescription("Delete a Kubernetes Pod in the current or provided namespace with the provided name"),
|
||||
mcp.WithString("namespace", mcp.Description("Namespace to delete the Pod from")),
|
||||
mcp.WithString("name", mcp.Description("Name of the Pod to delete"), mcp.Required()),
|
||||
// Tool annotations
|
||||
mcp.WithTitleAnnotation("Pods: Delete"),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithDestructiveHintAnnotation(true),
|
||||
mcp.WithIdempotentHintAnnotation(true),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
), Handler: s.podsDelete},
|
||||
{Tool: mcp.NewTool("pods_exec",
|
||||
mcp.WithDescription("Execute a command in a Kubernetes Pod in the current or provided namespace with the provided name and command"),
|
||||
@@ -48,12 +66,22 @@ func (s *Server) initPods() []server.ServerTool {
|
||||
mcp.Required(),
|
||||
),
|
||||
mcp.WithString("container", mcp.Description("Name of the Pod container where the command will be executed (Optional)")),
|
||||
// Tool annotations
|
||||
mcp.WithTitleAnnotation("Pods: Exec"),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithDestructiveHintAnnotation(true), // Depending on the Pod's entrypoint, executing certain commands may kill the Pod
|
||||
mcp.WithIdempotentHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
), Handler: s.podsExec},
|
||||
{Tool: mcp.NewTool("pods_log",
|
||||
mcp.WithDescription("Get the logs of a Kubernetes Pod in the current or provided namespace with the provided name"),
|
||||
mcp.WithString("namespace", mcp.Description("Namespace to get the Pod logs from")),
|
||||
mcp.WithString("name", mcp.Description("Name of the Pod to get the logs from"), mcp.Required()),
|
||||
mcp.WithString("container", mcp.Description("Name of the Pod container to get the logs from (Optional)")),
|
||||
// Tool annotations
|
||||
mcp.WithTitleAnnotation("Pods: Log"),
|
||||
mcp.WithReadOnlyHintAnnotation(true),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
), Handler: s.podsLog},
|
||||
{Tool: mcp.NewTool("pods_run",
|
||||
mcp.WithDescription("Run a Kubernetes Pod in the current or provided namespace with the provided container image and optional name"),
|
||||
@@ -61,6 +89,12 @@ func (s *Server) initPods() []server.ServerTool {
|
||||
mcp.WithString("name", mcp.Description("Name of the Pod (Optional, random name if not provided)")),
|
||||
mcp.WithString("image", mcp.Description("Container Image to run in the Pod"), mcp.Required()),
|
||||
mcp.WithNumber("port", mcp.Description("TCP/IP port to expose from the Pod container (Optional, no port exposed if not provided)")),
|
||||
// Tool annotations
|
||||
mcp.WithTitleAnnotation("Pods: Run"),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithDestructiveHintAnnotation(false),
|
||||
mcp.WithIdempotentHintAnnotation(false),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
), Handler: s.podsRun},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +31,12 @@ func (s *Server) initResources() []server.ServerTool {
|
||||
mcp.WithString("namespace",
|
||||
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")),
|
||||
mcp.WithString("labelSelector",
|
||||
mcp.Description("Optional Kubernetes label selector (e.g. 'app=myapp,env=prod' or 'app in (myapp,yourapp)'), use this option when you want to filter the pods by label"), mcp.Pattern("([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]"))),
|
||||
Handler: s.resourcesList},
|
||||
mcp.Description("Optional Kubernetes label selector (e.g. 'app=myapp,env=prod' or 'app in (myapp,yourapp)'), use this option when you want to filter the pods by label"), mcp.Pattern("([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]")),
|
||||
// Tool annotations
|
||||
mcp.WithTitleAnnotation("Resources: List"),
|
||||
mcp.WithReadOnlyHintAnnotation(true),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
), Handler: s.resourcesList},
|
||||
{Tool: 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"+
|
||||
commonApiVersion),
|
||||
@@ -48,6 +52,10 @@ func (s *Server) initResources() []server.ServerTool {
|
||||
mcp.Description("Optional Namespace to retrieve the namespaced resource from (ignored in case of cluster scoped resources). If not provided, will get resource from configured namespace"),
|
||||
),
|
||||
mcp.WithString("name", mcp.Description("Name of the resource"), mcp.Required()),
|
||||
// Tool annotations
|
||||
mcp.WithTitleAnnotation("Resources: Get"),
|
||||
mcp.WithReadOnlyHintAnnotation(true),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
), Handler: s.resourcesGet},
|
||||
{Tool: 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"+
|
||||
@@ -56,6 +64,12 @@ func (s *Server) initResources() []server.ServerTool {
|
||||
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(),
|
||||
),
|
||||
// Tool annotations
|
||||
mcp.WithTitleAnnotation("Resources: Create or Update"),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithDestructiveHintAnnotation(true),
|
||||
mcp.WithIdempotentHintAnnotation(true),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
), Handler: s.resourcesCreateOrUpdate},
|
||||
{Tool: 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"+
|
||||
@@ -72,6 +86,12 @@ func (s *Server) initResources() []server.ServerTool {
|
||||
mcp.Description("Optional Namespace to delete the namespaced resource from (ignored in case of cluster scoped resources). If not provided, will delete resource from configured namespace"),
|
||||
),
|
||||
mcp.WithString("name", mcp.Description("Name of the resource"), mcp.Required()),
|
||||
// Tool annotations
|
||||
mcp.WithTitleAnnotation("Resources: Delete"),
|
||||
mcp.WithReadOnlyHintAnnotation(false),
|
||||
mcp.WithDestructiveHintAnnotation(true),
|
||||
mcp.WithIdempotentHintAnnotation(true),
|
||||
mcp.WithOpenWorldHintAnnotation(true),
|
||||
), Handler: s.resourcesDelete},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -476,6 +476,17 @@ func TestResourcesDelete(t *testing.T) {
|
||||
return
|
||||
}
|
||||
})
|
||||
t.Run("resources_delete with nonexistent resource returns error", func(t *testing.T) {
|
||||
toolResult, _ := c.callTool("resources_delete", map[string]interface{}{"apiVersion": "v1", "kind": "ConfigMap", "name": "nonexistent-configmap"})
|
||||
if !toolResult.IsError {
|
||||
t.Fatalf("call tool should fail")
|
||||
return
|
||||
}
|
||||
if toolResult.Content[0].(mcp.TextContent).Text != `failed to delete resource: configmaps "nonexistent-configmap" not found` {
|
||||
t.Fatalf("invalid error message, got %v", toolResult.Content[0].(mcp.TextContent).Text)
|
||||
return
|
||||
}
|
||||
})
|
||||
resourcesDeleteCm, err := c.callTool("resources_delete", map[string]interface{}{"apiVersion": "v1", "kind": "ConfigMap", "name": "a-configmap-to-delete"})
|
||||
t.Run("resources_delete with valid namespaced resource returns success", func(t *testing.T) {
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user