Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions pkg/mcp/resources_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,113 @@ func (s *ResourcesSuite) TestResourcesScaleDenied() {
})
}

func (s *ResourcesSuite) TestResourcesListGoTemplate() {
s.InitMcpClient()
s.Run("resources_list with gotemplate extracts names", func() {
result, err := s.CallTool("resources_list", map[string]interface{}{
"apiVersion": "v1",
"kind": "Pod",
"namespace": "default",
"gotemplate": "{{range .items}}{{.metadata.name}}\n{{end}}",
})
s.Nilf(err, "call tool failed %v", err)
s.Falsef(result.IsError, "call tool failed: %v", result.Content)
content := result.Content[0].(*mcp.TextContent).Text
s.Contains(content, "a-pod-in-default")
})
s.Run("resources_list with gotemplate extracts namespace and name", func() {
result, err := s.CallTool("resources_list", map[string]interface{}{
"apiVersion": "v1",
"kind": "Pod",
"namespace": "default",
"gotemplate": "{{range .items}}{{.metadata.namespace}}/{{.metadata.name}}\n{{end}}",
})
s.Nilf(err, "call tool failed %v", err)
s.Falsef(result.IsError, "call tool failed: %v", result.Content)
content := result.Content[0].(*mcp.TextContent).Text
s.Contains(content, "default/a-pod-in-default")
})
s.Run("resources_list with invalid gotemplate returns error", func() {
result, _ := s.CallTool("resources_list", map[string]interface{}{
"apiVersion": "v1",
"kind": "Pod",
"namespace": "default",
"gotemplate": "{{invalid",
})
s.Truef(result.IsError, "call tool should fail for invalid template")
s.Contains(result.Content[0].(*mcp.TextContent).Text, "invalid go template expression")
})
s.Run("resources_list with gotemplate on empty list returns no output", func() {
result, err := s.CallTool("resources_list", map[string]interface{}{
"apiVersion": "v1",
"kind": "Pod",
"namespace": "default",
"labelSelector": "nonexistent-label=nonexistent-value",
"gotemplate": "{{range .items}}{{.metadata.name}}\n{{end}}",
})
s.Nilf(err, "call tool failed %v", err)
s.Falsef(result.IsError, "call tool should not fail: %v", result.Content)
content := result.Content[0].(*mcp.TextContent).Text
s.Equal("(no output from go template)", content)
})
}

func (s *ResourcesSuite) TestResourcesListGoTemplateInTableMode() {
s.Cfg.ListOutput = "table"
s.InitMcpClient()
s.Run("resources_list with gotemplate works even when list output is table", func() {
result, err := s.CallTool("resources_list", map[string]interface{}{
"apiVersion": "v1",
"kind": "Pod",
"namespace": "default",
"gotemplate": "{{range .items}}{{.metadata.name}}\n{{end}}",
})
s.Nilf(err, "call tool failed %v", err)
s.Falsef(result.IsError, "call tool failed: %v", result.Content)
content := result.Content[0].(*mcp.TextContent).Text
s.Contains(content, "a-pod-in-default")
})
}

func (s *ResourcesSuite) TestResourcesGetGoTemplate() {
s.InitMcpClient()
s.Run("resources_get with gotemplate extracts specific field", func() {
result, err := s.CallTool("resources_get", map[string]interface{}{
"apiVersion": "v1",
"kind": "Namespace",
"name": "default",
"gotemplate": "{{.metadata.name}}",
})
s.Nilf(err, "call tool failed %v", err)
s.Falsef(result.IsError, "call tool failed: %v", result.Content)
content := result.Content[0].(*mcp.TextContent).Text
s.Equal("default", content)
})
s.Run("resources_get with gotemplate extracts nested fields", func() {
result, err := s.CallTool("resources_get", map[string]interface{}{
"apiVersion": "v1",
"kind": "Pod",
"namespace": "default",
"name": "a-pod-in-default",
"gotemplate": "{{.kind}}/{{.metadata.name}}",
})
s.Nilf(err, "call tool failed %v", err)
s.Falsef(result.IsError, "call tool failed: %v", result.Content)
content := result.Content[0].(*mcp.TextContent).Text
s.Equal("Pod/a-pod-in-default", content)
})
s.Run("resources_get with invalid gotemplate returns error", func() {
result, _ := s.CallTool("resources_get", map[string]interface{}{
"apiVersion": "v1",
"kind": "Namespace",
"name": "default",
"gotemplate": "{{invalid",
})
s.Truef(result.IsError, "call tool should fail for invalid template")
s.Contains(result.Content[0].(*mcp.TextContent).Text, "invalid go template expression")
})
}

func TestResources(t *testing.T) {
suite.Run(t, new(ResourcesSuite))
}
12 changes: 10 additions & 2 deletions pkg/mcp/testdata/toolsets-core-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -449,13 +449,17 @@
"readOnlyHint": true,
"title": "Resources: Get"
},
"description": "Get a Kubernetes resource in the current cluster by providing its apiVersion, kind, optionally the namespace, and its name\n(common apiVersion and kind include: v1 Pod, v1 Service, v1 Node, apps/v1 Deployment, networking.k8s.io/v1 Ingress)",
"description": "Get a Kubernetes resource in the current cluster by providing its apiVersion, kind, optionally the namespace, and its name. Use the optional gotemplate parameter to extract specific fields instead of returning full YAML (same syntax as oc get -o go-template).\n(common apiVersion and kind include: v1 Pod, v1 Service, v1 Node, apps/v1 Deployment, networking.k8s.io/v1 Ingress)",
"inputSchema": {
"properties": {
"apiVersion": {
"description": "apiVersion of the resource (examples of valid apiVersion are: v1, apps/v1, networking.k8s.io/v1)",
"type": "string"
},
"gotemplate": {
"description": "Optional Go template to extract specific fields instead of returning full YAML (e.g. {{.spec.source.repoURL}}, {{.status.phase}})",
"type": "string"
},
"kind": {
"description": "kind of the resource (examples of valid kind are: Pod, Service, Deployment, Ingress)",
"type": "string"
Expand Down Expand Up @@ -486,7 +490,7 @@
"readOnlyHint": true,
"title": "Resources: List"
},
"description": "List Kubernetes resources and objects in the current cluster by providing their apiVersion and kind and optionally the namespace and label selector\n(common apiVersion and kind include: v1 Pod, v1 Service, v1 Node, apps/v1 Deployment, networking.k8s.io/v1 Ingress)",
"description": "List Kubernetes resources and objects in the current cluster by providing their apiVersion and kind and optionally the namespace and label selector. Use the optional gotemplate parameter to extract specific fields instead of returning full YAML (same syntax as oc get -o go-template).\n(common apiVersion and kind include: v1 Pod, v1 Service, v1 Node, apps/v1 Deployment, networking.k8s.io/v1 Ingress)",
"inputSchema": {
"properties": {
"apiVersion": {
Expand All @@ -498,6 +502,10 @@
"pattern": "^[.\\-A-Za-z0-9]+([=!,]{1,2}[.\\-A-Za-z0-9]+)+$",
"type": "string"
},
"gotemplate": {
"description": "Optional Go template to extract specific fields instead of returning full YAML (e.g. {{range .items}}{{.metadata.name}}\\n{{end}}, {{.spec.source.repoURL}})",
"type": "string"
},
"kind": {
"description": "kind of the resources (examples of valid kind are: Pod, Service, Deployment, Ingress)",
"type": "string"
Expand Down
12 changes: 10 additions & 2 deletions pkg/mcp/testdata/toolsets-full-tools-multicluster-enum.json
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,7 @@
"readOnlyHint": true,
"title": "Resources: Get"
},
"description": "Get a Kubernetes resource in the current cluster by providing its apiVersion, kind, optionally the namespace, and its name\n(common apiVersion and kind include: v1 Pod, v1 Service, v1 Node, apps/v1 Deployment, networking.k8s.io/v1 Ingress)",
"description": "Get a Kubernetes resource in the current cluster by providing its apiVersion, kind, optionally the namespace, and its name. Use the optional gotemplate parameter to extract specific fields instead of returning full YAML (same syntax as oc get -o go-template).\n(common apiVersion and kind include: v1 Pod, v1 Service, v1 Node, apps/v1 Deployment, networking.k8s.io/v1 Ingress)",
"inputSchema": {
"properties": {
"apiVersion": {
Expand All @@ -621,6 +621,10 @@
],
"type": "string"
},
"gotemplate": {
"description": "Optional Go template to extract specific fields instead of returning full YAML (e.g. {{.spec.source.repoURL}}, {{.status.phase}})",
"type": "string"
},
"kind": {
"description": "kind of the resource (examples of valid kind are: Pod, Service, Deployment, Ingress)",
"type": "string"
Expand Down Expand Up @@ -651,7 +655,7 @@
"readOnlyHint": true,
"title": "Resources: List"
},
"description": "List Kubernetes resources and objects in the current cluster by providing their apiVersion and kind and optionally the namespace and label selector\n(common apiVersion and kind include: v1 Pod, v1 Service, v1 Node, apps/v1 Deployment, networking.k8s.io/v1 Ingress)",
"description": "List Kubernetes resources and objects in the current cluster by providing their apiVersion and kind and optionally the namespace and label selector. Use the optional gotemplate parameter to extract specific fields instead of returning full YAML (same syntax as oc get -o go-template).\n(common apiVersion and kind include: v1 Pod, v1 Service, v1 Node, apps/v1 Deployment, networking.k8s.io/v1 Ingress)",
"inputSchema": {
"properties": {
"apiVersion": {
Expand All @@ -671,6 +675,10 @@
"pattern": "^[.\\-A-Za-z0-9]+([=!,]{1,2}[.\\-A-Za-z0-9]+)+$",
"type": "string"
},
"gotemplate": {
"description": "Optional Go template to extract specific fields instead of returning full YAML (e.g. {{range .items}}{{.metadata.name}}\\n{{end}}, {{.spec.source.repoURL}})",
"type": "string"
},
"kind": {
"description": "kind of the resources (examples of valid kind are: Pod, Service, Deployment, Ingress)",
"type": "string"
Expand Down
12 changes: 10 additions & 2 deletions pkg/mcp/testdata/toolsets-full-tools-multicluster.json
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@
"readOnlyHint": true,
"title": "Resources: Get"
},
"description": "Get a Kubernetes resource in the current cluster by providing its apiVersion, kind, optionally the namespace, and its name\n(common apiVersion and kind include: v1 Pod, v1 Service, v1 Node, apps/v1 Deployment, networking.k8s.io/v1 Ingress)",
"description": "Get a Kubernetes resource in the current cluster by providing its apiVersion, kind, optionally the namespace, and its name. Use the optional gotemplate parameter to extract specific fields instead of returning full YAML (same syntax as oc get -o go-template).\n(common apiVersion and kind include: v1 Pod, v1 Service, v1 Node, apps/v1 Deployment, networking.k8s.io/v1 Ingress)",
"inputSchema": {
"properties": {
"apiVersion": {
Expand All @@ -557,6 +557,10 @@
"description": "Optional parameter selecting which context to run the tool in. Defaults to fake-context if not set",
"type": "string"
},
"gotemplate": {
"description": "Optional Go template to extract specific fields instead of returning full YAML (e.g. {{.spec.source.repoURL}}, {{.status.phase}})",
"type": "string"
},
"kind": {
"description": "kind of the resource (examples of valid kind are: Pod, Service, Deployment, Ingress)",
"type": "string"
Expand Down Expand Up @@ -587,7 +591,7 @@
"readOnlyHint": true,
"title": "Resources: List"
},
"description": "List Kubernetes resources and objects in the current cluster by providing their apiVersion and kind and optionally the namespace and label selector\n(common apiVersion and kind include: v1 Pod, v1 Service, v1 Node, apps/v1 Deployment, networking.k8s.io/v1 Ingress)",
"description": "List Kubernetes resources and objects in the current cluster by providing their apiVersion and kind and optionally the namespace and label selector. Use the optional gotemplate parameter to extract specific fields instead of returning full YAML (same syntax as oc get -o go-template).\n(common apiVersion and kind include: v1 Pod, v1 Service, v1 Node, apps/v1 Deployment, networking.k8s.io/v1 Ingress)",
"inputSchema": {
"properties": {
"apiVersion": {
Expand All @@ -603,6 +607,10 @@
"pattern": "^[.\\-A-Za-z0-9]+([=!,]{1,2}[.\\-A-Za-z0-9]+)+$",
"type": "string"
},
"gotemplate": {
"description": "Optional Go template to extract specific fields instead of returning full YAML (e.g. {{range .items}}{{.metadata.name}}\\n{{end}}, {{.spec.source.repoURL}})",
"type": "string"
},
"kind": {
"description": "kind of the resources (examples of valid kind are: Pod, Service, Deployment, Ingress)",
"type": "string"
Expand Down
12 changes: 10 additions & 2 deletions pkg/mcp/testdata/toolsets-full-tools-openshift.json
Original file line number Diff line number Diff line change
Expand Up @@ -484,13 +484,17 @@
"readOnlyHint": true,
"title": "Resources: Get"
},
"description": "Get a Kubernetes resource in the current cluster by providing its apiVersion, kind, optionally the namespace, and its name\n(common apiVersion and kind include: v1 Pod, v1 Service, v1 Node, apps/v1 Deployment, networking.k8s.io/v1 Ingress, route.openshift.io/v1 Route)",
"description": "Get a Kubernetes resource in the current cluster by providing its apiVersion, kind, optionally the namespace, and its name. Use the optional gotemplate parameter to extract specific fields instead of returning full YAML (same syntax as oc get -o go-template).\n(common apiVersion and kind include: v1 Pod, v1 Service, v1 Node, apps/v1 Deployment, networking.k8s.io/v1 Ingress, route.openshift.io/v1 Route)",
"inputSchema": {
"properties": {
"apiVersion": {
"description": "apiVersion of the resource (examples of valid apiVersion are: v1, apps/v1, networking.k8s.io/v1)",
"type": "string"
},
"gotemplate": {
"description": "Optional Go template to extract specific fields instead of returning full YAML (e.g. {{.spec.source.repoURL}}, {{.status.phase}})",
"type": "string"
},
"kind": {
"description": "kind of the resource (examples of valid kind are: Pod, Service, Deployment, Ingress)",
"type": "string"
Expand Down Expand Up @@ -521,7 +525,7 @@
"readOnlyHint": true,
"title": "Resources: List"
},
"description": "List Kubernetes resources and objects in the current cluster by providing their apiVersion and kind and optionally the namespace and label selector\n(common apiVersion and kind include: v1 Pod, v1 Service, v1 Node, apps/v1 Deployment, networking.k8s.io/v1 Ingress, route.openshift.io/v1 Route)",
"description": "List Kubernetes resources and objects in the current cluster by providing their apiVersion and kind and optionally the namespace and label selector. Use the optional gotemplate parameter to extract specific fields instead of returning full YAML (same syntax as oc get -o go-template).\n(common apiVersion and kind include: v1 Pod, v1 Service, v1 Node, apps/v1 Deployment, networking.k8s.io/v1 Ingress, route.openshift.io/v1 Route)",
"inputSchema": {
"properties": {
"apiVersion": {
Expand All @@ -533,6 +537,10 @@
"pattern": "^[.\\-A-Za-z0-9]+([=!,]{1,2}[.\\-A-Za-z0-9]+)+$",
"type": "string"
},
"gotemplate": {
"description": "Optional Go template to extract specific fields instead of returning full YAML (e.g. {{range .items}}{{.metadata.name}}\\n{{end}}, {{.spec.source.repoURL}})",
"type": "string"
},
"kind": {
"description": "kind of the resources (examples of valid kind are: Pod, Service, Deployment, Ingress)",
"type": "string"
Expand Down
12 changes: 10 additions & 2 deletions pkg/mcp/testdata/toolsets-full-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -469,13 +469,17 @@
"readOnlyHint": true,
"title": "Resources: Get"
},
"description": "Get a Kubernetes resource in the current cluster by providing its apiVersion, kind, optionally the namespace, and its name\n(common apiVersion and kind include: v1 Pod, v1 Service, v1 Node, apps/v1 Deployment, networking.k8s.io/v1 Ingress)",
"description": "Get a Kubernetes resource in the current cluster by providing its apiVersion, kind, optionally the namespace, and its name. Use the optional gotemplate parameter to extract specific fields instead of returning full YAML (same syntax as oc get -o go-template).\n(common apiVersion and kind include: v1 Pod, v1 Service, v1 Node, apps/v1 Deployment, networking.k8s.io/v1 Ingress)",
"inputSchema": {
"properties": {
"apiVersion": {
"description": "apiVersion of the resource (examples of valid apiVersion are: v1, apps/v1, networking.k8s.io/v1)",
"type": "string"
},
"gotemplate": {
"description": "Optional Go template to extract specific fields instead of returning full YAML (e.g. {{.spec.source.repoURL}}, {{.status.phase}})",
"type": "string"
},
"kind": {
"description": "kind of the resource (examples of valid kind are: Pod, Service, Deployment, Ingress)",
"type": "string"
Expand Down Expand Up @@ -506,7 +510,7 @@
"readOnlyHint": true,
"title": "Resources: List"
},
"description": "List Kubernetes resources and objects in the current cluster by providing their apiVersion and kind and optionally the namespace and label selector\n(common apiVersion and kind include: v1 Pod, v1 Service, v1 Node, apps/v1 Deployment, networking.k8s.io/v1 Ingress)",
"description": "List Kubernetes resources and objects in the current cluster by providing their apiVersion and kind and optionally the namespace and label selector. Use the optional gotemplate parameter to extract specific fields instead of returning full YAML (same syntax as oc get -o go-template).\n(common apiVersion and kind include: v1 Pod, v1 Service, v1 Node, apps/v1 Deployment, networking.k8s.io/v1 Ingress)",
"inputSchema": {
"properties": {
"apiVersion": {
Expand All @@ -518,6 +522,10 @@
"pattern": "^[.\\-A-Za-z0-9]+([=!,]{1,2}[.\\-A-Za-z0-9]+)+$",
"type": "string"
},
"gotemplate": {
"description": "Optional Go template to extract specific fields instead of returning full YAML (e.g. {{range .items}}{{.metadata.name}}\\n{{end}}, {{.spec.source.repoURL}})",
"type": "string"
},
"kind": {
"description": "kind of the resources (examples of valid kind are: Pod, Service, Deployment, Ingress)",
"type": "string"
Expand Down
Loading