Skip to content
Draft
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
3 changes: 3 additions & 0 deletions pkg/api/toolsets.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ type Toolset interface {
// Will be used to generate documentation and help text.
GetDescription() string
GetTools(o internalk8s.Openshift) []ServerTool
// IsValid returns whether or not the toolset is valid on the current cluster
// Used to identify if toolsets should be disabled despite the config based on what is installed in the cluster
IsValid(k *internalk8s.Kubernetes) bool
}

type ToolCallRequest interface {
Expand Down
31 changes: 25 additions & 6 deletions pkg/mcp/mcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,6 @@ func (s *Server) reloadKubernetesClusterProvider() error {
ShouldIncludeTargetListTool(p.GetTargetParameterName(), targets),
)

mutator := WithTargetParameter(
p.GetDefaultTarget(),
p.GetTargetParameterName(),
targets,
)

// TODO: No option to perform a full replacement of tools.
// s.server.SetTools(m3labsServerTools...)

Expand All @@ -136,6 +130,31 @@ func (s *Server) reloadKubernetesClusterProvider() error {
applicableTools := make([]api.ServerTool, 0)
s.enabledTools = make([]string, 0)
for _, toolset := range s.configuration.Toolsets() {
// make sure that the toolset is valid for at least one target, otherwise don't add tools from it
toolTargets := []string{}
for _, target := range targets {
k, err := p.GetDerivedKubernetes(ctx, target)
if err != nil {
// don't error out - just continue, this is just to determine if a tool is valid or not
continue
}

if toolset.IsValid(k) {
toolTargets = append(toolTargets, target)
}
}

if len(toolTargets) == 0 {
// no targets for this toolset, don't add tools from it
continue
}

mutator := WithTargetParameter(
p.GetDefaultTarget(),
p.GetTargetParameterName(),
targets,
)

for _, tool := range toolset.GetTools(p) {
tool := mutator(tool)
if !filter(tool) {
Expand Down
4 changes: 4 additions & 0 deletions pkg/toolsets/config/toolset.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ func (t *Toolset) GetTools(_ internalk8s.Openshift) []api.ServerTool {
)
}

func (t *Toolset) IsValid(_ *internalk8s.Kubernetes) bool {
return true
}

func init() {
toolsets.Register(&Toolset{})
}
4 changes: 4 additions & 0 deletions pkg/toolsets/core/toolset.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ func (t *Toolset) GetTools(o internalk8s.Openshift) []api.ServerTool {
)
}

func (t *Toolset) IsValid(_ *internalk8s.Kubernetes) bool {
return true // core is always valid
}

func init() {
toolsets.Register(&Toolset{})
}
4 changes: 4 additions & 0 deletions pkg/toolsets/helm/toolset.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ func (t *Toolset) GetTools(_ internalk8s.Openshift) []api.ServerTool {
)
}

func (t *Toolset) IsValid(_ *internalk8s.Kubernetes) bool {
return true
}

func init() {
toolsets.Register(&Toolset{})
}
14 changes: 14 additions & 0 deletions pkg/toolsets/kiali/toolset.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package kiali

import (
"context"
"slices"

"github.com/containers/kubernetes-mcp-server/pkg/api"
Expand Down Expand Up @@ -39,6 +40,19 @@ func (t *Toolset) GetTools(_ internalk8s.Openshift) []api.ServerTool {
)
}

func (t *Toolset) IsValid(k *internalk8s.Kubernetes) bool {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aljesusg when you have time can you take a look at this method and let me know if this makes sense?

It's supposed to validate that there is a working kiali installation in the target cluster

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes… although I would do the following: since the AuthInfoEndpoint endpoint is already created, I would create a method to call it, as it doesn’t require any parameters and is ‘available’ whether the user is authenticated or not.

// Create a Kiali client
kialiClient := k.NewKiali()

// Check if Kiali is actually accessible by making a lightweight API call
// We'll try to get the mesh status as it's a simple endpoint that doesn't require parameters
ctx := context.Background()
_, err := kialiClient.MeshStatus(ctx)

// If we can successfully call Kiali, it's valid
return err == nil
}

func init() {
toolsets.Register(&Toolset{})
}
2 changes: 2 additions & 0 deletions pkg/toolsets/toolsets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ func (t *TestToolset) GetDescription() string { return t.description }

func (t *TestToolset) GetTools(_ kubernetes.Openshift) []api.ServerTool { return nil }

func (t *TestToolset) IsValid(_ *kubernetes.Kubernetes) bool { return true }

var _ api.Toolset = (*TestToolset)(nil)

func (s *ToolsetsSuite) TestToolsetNames() {
Expand Down