Skip to content

Commit 8b8c9e9

Browse files
committed
check cluster health
Signed-off-by: Rohit Patil <[email protected]>
1 parent 255eb9b commit 8b8c9e9

File tree

12 files changed

+1185
-1
lines changed

12 files changed

+1185
-1
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,11 @@ In case multi-cluster support is enabled (default) and you have access to multip
243243
- **events_list** - List all the Kubernetes events in the current cluster from all namespaces
244244
- `namespace` (`string`) - Optional Namespace to retrieve the events from. If not provided, will list events from all namespaces
245245

246+
- **cluster_health_check** - Perform comprehensive health check on Kubernetes/OpenShift cluster and report issues. Examines cluster operators (OpenShift), nodes, deployments, pods, persistent volumes, and events to identify problems affecting cluster stability or workload availability.
247+
- `check_events` (`boolean`) - Include recent warning events in the health check (may increase execution time)
248+
- `output_format` (`string`) - Output format for results: 'text' (human-readable) or 'json' (machine-readable)
249+
- `verbose` (`boolean`) - Enable detailed output with additional context and resource-level details
250+
246251
- **namespaces_list** - List all the Kubernetes namespaces in the current cluster
247252

248253
- **projects_list** - List all the OpenShift projects in the current cluster

pkg/api/prompts.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package api
2+
3+
import (
4+
internalk8s "github.com/containers/kubernetes-mcp-server/pkg/kubernetes"
5+
)
6+
7+
// ServerPrompt represents a prompt that can be provided to the MCP server
8+
type ServerPrompt struct {
9+
Name string
10+
Description string
11+
Arguments []PromptArgument
12+
GetMessages func(arguments map[string]string) []PromptMessage
13+
}
14+
15+
// PromptArgument defines an argument that can be passed to a prompt
16+
type PromptArgument struct {
17+
Name string
18+
Description string
19+
Required bool
20+
}
21+
22+
// PromptMessage represents a message in a prompt
23+
type PromptMessage struct {
24+
Role string // "user" or "assistant"
25+
Content string
26+
}
27+
28+
// PromptSet groups related prompts together
29+
type PromptSet interface {
30+
// GetName returns the name of the prompt set
31+
GetName() string
32+
// GetDescription returns a description of what this prompt set provides
33+
GetDescription() string
34+
// GetPrompts returns all prompts in this set
35+
GetPrompts(o internalk8s.Openshift) []ServerPrompt
36+
}

pkg/config/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ type StaticConfig struct {
3131
// When true, disable tools annotated with destructiveHint=true
3232
DisableDestructive bool `toml:"disable_destructive,omitempty"`
3333
Toolsets []string `toml:"toolsets,omitempty"`
34+
Promptsets []string `toml:"promptsets,omitempty"`
3435
EnabledTools []string `toml:"enabled_tools,omitempty"`
3536
DisabledTools []string `toml:"disabled_tools,omitempty"`
3637

pkg/mcp/mcp.go

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ import (
99

1010
"github.com/modelcontextprotocol/go-sdk/mcp"
1111
authenticationapiv1 "k8s.io/api/authentication/v1"
12+
"k8s.io/klog/v2"
1213
"k8s.io/utils/ptr"
1314

1415
"github.com/containers/kubernetes-mcp-server/pkg/api"
1516
"github.com/containers/kubernetes-mcp-server/pkg/config"
1617
internalk8s "github.com/containers/kubernetes-mcp-server/pkg/kubernetes"
1718
"github.com/containers/kubernetes-mcp-server/pkg/output"
19+
"github.com/containers/kubernetes-mcp-server/pkg/promptsets"
1820
"github.com/containers/kubernetes-mcp-server/pkg/toolsets"
1921
"github.com/containers/kubernetes-mcp-server/pkg/version"
2022
)
@@ -27,6 +29,7 @@ type Configuration struct {
2729
*config.StaticConfig
2830
listOutput output.Output
2931
toolsets []api.Toolset
32+
promptsets []api.PromptSet
3033
}
3134

3235
func (c *Configuration) Toolsets() []api.Toolset {
@@ -38,6 +41,23 @@ func (c *Configuration) Toolsets() []api.Toolset {
3841
return c.toolsets
3942
}
4043

44+
func (c *Configuration) Promptsets() []api.PromptSet {
45+
if c.promptsets == nil {
46+
// Default to core if no promptsets configured
47+
promptsetNames := c.StaticConfig.Promptsets
48+
if len(promptsetNames) == 0 {
49+
promptsetNames = []string{"core"}
50+
}
51+
for _, promptset := range promptsetNames {
52+
ps := promptsets.PromptSetFromString(promptset)
53+
if ps != nil {
54+
c.promptsets = append(c.promptsets, ps)
55+
}
56+
}
57+
}
58+
return c.promptsets
59+
}
60+
4161
func (c *Configuration) ListOutput() output.Output {
4262
if c.listOutput == nil {
4363
c.listOutput = output.FromString(c.StaticConfig.ListOutput)
@@ -77,7 +97,7 @@ func NewServer(configuration Configuration) (*Server, error) {
7797
},
7898
&mcp.ServerOptions{
7999
HasResources: false,
80-
HasPrompts: false,
100+
HasPrompts: true,
81101
HasTools: true,
82102
}),
83103
}
@@ -165,11 +185,42 @@ func (s *Server) reloadKubernetesClusterProvider() error {
165185
s.server.AddTool(goSdkTool, goSdkToolHandler)
166186
}
167187

188+
// Register prompts
189+
if err := s.registerPrompts(p); err != nil {
190+
klog.Warningf("Failed to register prompts: %v", err)
191+
// Don't fail the whole reload if prompts fail
192+
}
193+
168194
// start new watch
169195
s.p.WatchTargets(s.reloadKubernetesClusterProvider)
170196
return nil
171197
}
172198

199+
// registerPrompts loads and registers all prompts with the MCP server
200+
func (s *Server) registerPrompts(p internalk8s.Provider) error {
201+
allPrompts := make([]api.ServerPrompt, 0)
202+
for _, ps := range s.configuration.Promptsets() {
203+
prompts := ps.GetPrompts(p)
204+
allPrompts = append(allPrompts, prompts...)
205+
klog.V(5).Infof("Loaded %d prompts from promptset '%s'", len(prompts), ps.GetName())
206+
}
207+
208+
goSdkPrompts, goSdkHandlers, err := ServerPromptToGoSdkPrompt(s, allPrompts)
209+
if err != nil {
210+
return fmt.Errorf("failed to convert prompts: %v", err)
211+
}
212+
213+
// Register each prompt with its handler
214+
for name, prompt := range goSdkPrompts {
215+
handler := goSdkHandlers[name]
216+
s.server.AddPrompt(prompt, handler)
217+
}
218+
219+
klog.V(3).Infof("Registered %d prompts", len(goSdkPrompts))
220+
221+
return nil
222+
}
223+
173224
func (s *Server) ServeStdio(ctx context.Context) error {
174225
return s.server.Run(ctx, &mcp.LoggingTransport{Transport: &mcp.StdioTransport{}, Writer: os.Stderr})
175226
}

pkg/mcp/modules.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package mcp
22

3+
import _ "github.com/containers/kubernetes-mcp-server/pkg/promptsets/core"
34
import _ "github.com/containers/kubernetes-mcp-server/pkg/toolsets/config"
45
import _ "github.com/containers/kubernetes-mcp-server/pkg/toolsets/core"
56
import _ "github.com/containers/kubernetes-mcp-server/pkg/toolsets/helm"

pkg/mcp/prompts.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package mcp
2+
3+
import (
4+
"context"
5+
6+
"github.com/modelcontextprotocol/go-sdk/mcp"
7+
8+
"github.com/containers/kubernetes-mcp-server/pkg/api"
9+
)
10+
11+
// ServerPromptToGoSdkPrompt converts our internal ServerPrompt to go-sdk Prompt format
12+
func ServerPromptToGoSdkPrompt(s *Server, prompts []api.ServerPrompt) (map[string]*mcp.Prompt, map[string]mcp.PromptHandler, error) {
13+
goSdkPrompts := make(map[string]*mcp.Prompt)
14+
goSdkHandlers := make(map[string]mcp.PromptHandler)
15+
16+
for _, prompt := range prompts {
17+
// Convert arguments to PromptArgument pointers
18+
var arguments []*mcp.PromptArgument
19+
for _, arg := range prompt.Arguments {
20+
arguments = append(arguments, &mcp.PromptArgument{
21+
Name: arg.Name,
22+
Description: arg.Description,
23+
Required: arg.Required,
24+
})
25+
}
26+
27+
goSdkPrompt := &mcp.Prompt{
28+
Name: prompt.Name,
29+
Description: prompt.Description,
30+
Arguments: arguments,
31+
}
32+
33+
// Create the prompt handler
34+
handler := createPromptHandler(s, prompt)
35+
36+
goSdkPrompts[prompt.Name] = goSdkPrompt
37+
goSdkHandlers[prompt.Name] = handler
38+
}
39+
40+
return goSdkPrompts, goSdkHandlers, nil
41+
}
42+
43+
// createPromptHandler creates a handler function for a prompt
44+
func createPromptHandler(s *Server, prompt api.ServerPrompt) mcp.PromptHandler {
45+
return func(ctx context.Context, request *mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
46+
// Get arguments from the request
47+
params := request.GetParams()
48+
arguments := make(map[string]string)
49+
if params != nil {
50+
// Cast to concrete type to access Arguments field
51+
if getPromptParams, ok := params.(*mcp.GetPromptParams); ok && getPromptParams.Arguments != nil {
52+
arguments = getPromptParams.Arguments
53+
}
54+
}
55+
56+
// Get messages from the prompt
57+
promptMessages := prompt.GetMessages(arguments)
58+
59+
// Convert to mcp-go format - need to use pointers
60+
messages := make([]*mcp.PromptMessage, 0, len(promptMessages))
61+
for _, msg := range promptMessages {
62+
messages = append(messages, &mcp.PromptMessage{
63+
Role: mcp.Role(msg.Role),
64+
Content: &mcp.TextContent{
65+
Text: msg.Content,
66+
},
67+
})
68+
}
69+
70+
return &mcp.GetPromptResult{
71+
Description: prompt.Description,
72+
Messages: messages,
73+
}, nil
74+
}
75+
}

0 commit comments

Comments
 (0)