Skip to content

Commit 173eb4c

Browse files
committed
feat: add tool mutator to add cluster parameter
Signed-off-by: Calum Murray <[email protected]>
1 parent 5c3b2ba commit 173eb4c

File tree

2 files changed

+72
-1
lines changed

2 files changed

+72
-1
lines changed

pkg/mcp/mcp.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ func NewServer(configuration Configuration) (*Server, error) {
100100
}
101101

102102
func (s *Server) reloadKubernetesClusterProvider() error {
103+
ctx := context.Background()
103104
p, err := internalk8s.NewClusterProvider(s.configuration.StaticConfig)
104105
if err != nil {
105106
return err
@@ -112,14 +113,22 @@ func (s *Server) reloadKubernetesClusterProvider() error {
112113

113114
s.p = p
114115

115-
k, err := s.p.GetClusterManager(context.Background(), s.p.GetDefaultCluster())
116+
k, err := s.p.GetClusterManager(ctx, s.p.GetDefaultCluster())
116117
if err != nil {
117118
return err
118119
}
119120

121+
clusters, err := p.GetClusters(ctx)
122+
if err != nil {
123+
return err
124+
}
125+
126+
mutator := WithClusterParameter(p.GetDefaultCluster(), clusters, []string{}) // TODO: see which tools (if any) do not need the cluster parameter
127+
120128
applicableTools := make([]api.ServerTool, 0)
121129
for _, toolset := range s.configuration.Toolsets() {
122130
for _, tool := range toolset.GetTools(k) {
131+
tool := mutator(tool)
123132
if !s.configuration.isToolApplicable(tool) {
124133
continue
125134
}

pkg/mcp/tool_mutator.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package mcp
2+
3+
import (
4+
"fmt"
5+
"sort"
6+
7+
"github.com/containers/kubernetes-mcp-server/pkg/api"
8+
"github.com/google/jsonschema-go/jsonschema"
9+
)
10+
11+
type ToolMutator func(tool api.ServerTool) api.ServerTool
12+
13+
const maxClustersInEnum = 15 // TODO: test and validate that this is a reasonable cutoff
14+
15+
func WithClusterParameter(defaultCluster string, clusters, skipToolNames []string) ToolMutator {
16+
skipNames := make(map[string]struct{}, len(skipToolNames))
17+
for _, n := range skipToolNames {
18+
skipNames[n] = struct{}{}
19+
}
20+
21+
return func(tool api.ServerTool) api.ServerTool {
22+
if _, ok := skipNames[tool.Tool.Name]; ok {
23+
return tool
24+
}
25+
26+
if tool.Tool.InputSchema == nil {
27+
tool.Tool.InputSchema = &jsonschema.Schema{Type: "object"}
28+
}
29+
30+
if tool.Tool.InputSchema.Properties == nil {
31+
tool.Tool.InputSchema.Properties = make(map[string]*jsonschema.Schema)
32+
}
33+
34+
if len(clusters) > 1 {
35+
tool.Tool.InputSchema.Properties["cluster"] = createClusterProperty(defaultCluster, clusters)
36+
}
37+
38+
return tool
39+
}
40+
}
41+
42+
func createClusterProperty(defaultCluster string, clusters []string) *jsonschema.Schema {
43+
baseSchema := &jsonschema.Schema{
44+
Type: "string",
45+
Description: fmt.Sprintf(
46+
"Optional parameter selecting which cluster to run the tool in. Defaults to %s if not set", defaultCluster,
47+
),
48+
}
49+
50+
if len(clusters) <= maxClustersInEnum {
51+
// Sort clusters to ensure consistent enum ordering
52+
sort.Strings(clusters)
53+
54+
enumValues := make([]any, 0, len(clusters))
55+
for _, c := range clusters {
56+
enumValues = append(enumValues, c)
57+
}
58+
baseSchema.Enum = enumValues
59+
}
60+
61+
return baseSchema
62+
}

0 commit comments

Comments
 (0)