mirror of
https://github.com/openshift/openshift-mcp-server.git
synced 2025-10-17 14:27:48 +03:00
* feat: add cluster provider for kubeconfig Signed-off-by: Calum Murray <cmurray@redhat.com> * feat: move server to use ClusterProvider interface Signed-off-by: Calum Murray <cmurray@redhat.com> * feat: authentication middleware works with cluster provider Signed-off-by: Calum Murray <cmurray@redhat.com> * fix: unit tests work after cluster provider changes Signed-off-by: Calum Murray <cmurray@redhat.com> * feat: add tool mutator to add cluster parameter Signed-off-by: Calum Murray <cmurray@redhat.com> * test: handle cluster parameter Signed-off-by: Calum Murray <cmurray@redhat.com> * fix: handle lazy init correctly Signed-off-by: Calum Murray <cmurray@redhat.com> * refactor: move to using multi-strategy ManagerProvider Signed-off-by: Calum Murray <cmurray@redhat.com> * feat: add contexts_list tool Signed-off-by: Calum Murray <cmurray@redhat.com> * refactor: make tool mutator generic between cluster/context naming Signed-off-by: Calum Murray <cmurray@redhat.com> * feat: introduce tool filter Signed-off-by: Calum Murray <cmurray@redhat.com> * refactor: use new ManagerProvider/mutator/filter within mcp server Signed-off-by: Calum Murray <cmurray@redhat.com> * fix(test): tests expect context parameter in tool defs Signed-off-by: Calum Murray <cmurray@redhat.com> * feat: auth handles multi-cluster case correctly Signed-off-by: Calum Murray <cmurray@redhat.com> * fix: small changes from local testing Signed-off-by: Calum Murray <cmurray@redhat.com> * chore: fix enum test Signed-off-by: Calum Murray <cmurray@redhat.com> * review: Multi Cluster support (#1) * nit: rename contexts_list to configuration_contexts_list Besides the conventional naming, it helps LLMs understand the context of the tool by providing a certain level of hierarchy. Signed-off-by: Marc Nuri <marc@marcnuri.com> * fix(mcp): ToolMutator doesn't rely on magic strings Signed-off-by: Marc Nuri <marc@marcnuri.com> * refactor(api): don't expose ManagerProvider to toolsets Signed-off-by: Marc Nuri <marc@marcnuri.com> * test(mcp): configuration_contexts_list basic tests Signed-off-by: Marc Nuri <marc@marcnuri.com> * test(toolsets): revert edge-case test This test should not be touched. Signed-off-by: Marc Nuri <marc@marcnuri.com> * test(toolsets): add specific metadata tests for multi-cluster Signed-off-by: Marc Nuri <marc@marcnuri.com> * fix(mcp): ToolFilter doesn't rely on magic strings (partially) Signed-off-by: Marc Nuri <marc@marcnuri.com> * test(api): IsClusterAware and IsTargetListProvider default values Signed-off-by: Marc Nuri <marc@marcnuri.com> * test(mcp): revert unneeded changes in mcp_tools_test.go Signed-off-by: Marc Nuri <marc@marcnuri.com> --------- Signed-off-by: Marc Nuri <marc@marcnuri.com> * fix: always include configuration_contexts_list if contexts > 1 Signed-off-by: Calum Murray <cmurray@redhat.com> * feat: include server urls in configuration_contexts_list Signed-off-by: Calum Murray <cmurray@redhat.com> --------- Signed-off-by: Calum Murray <cmurray@redhat.com> Signed-off-by: Marc Nuri <marc@marcnuri.com> Co-authored-by: Marc Nuri <marc@marcnuri.com>
116 lines
3.7 KiB
Go
116 lines
3.7 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/google/jsonschema-go/jsonschema"
|
|
"k8s.io/utils/ptr"
|
|
|
|
"github.com/containers/kubernetes-mcp-server/pkg/api"
|
|
"github.com/containers/kubernetes-mcp-server/pkg/output"
|
|
)
|
|
|
|
func initConfiguration() []api.ServerTool {
|
|
tools := []api.ServerTool{
|
|
{
|
|
Tool: api.Tool{
|
|
Name: "configuration_contexts_list",
|
|
Description: "List all available context names and associated server urls from the kubeconfig file",
|
|
InputSchema: &jsonschema.Schema{
|
|
Type: "object",
|
|
},
|
|
Annotations: api.ToolAnnotations{
|
|
Title: "Configuration: Contexts List",
|
|
ReadOnlyHint: ptr.To(true),
|
|
DestructiveHint: ptr.To(false),
|
|
IdempotentHint: ptr.To(true),
|
|
OpenWorldHint: ptr.To(false),
|
|
},
|
|
},
|
|
ClusterAware: ptr.To(false),
|
|
TargetListProvider: ptr.To(true),
|
|
Handler: contextsList,
|
|
},
|
|
{
|
|
Tool: api.Tool{
|
|
Name: "configuration_view",
|
|
Description: "Get the current Kubernetes configuration content as a kubeconfig YAML",
|
|
InputSchema: &jsonschema.Schema{
|
|
Type: "object",
|
|
Properties: map[string]*jsonschema.Schema{
|
|
"minified": {
|
|
Type: "boolean",
|
|
Description: "Return a minified version of the configuration. " +
|
|
"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)",
|
|
},
|
|
},
|
|
},
|
|
Annotations: api.ToolAnnotations{
|
|
Title: "Configuration: View",
|
|
ReadOnlyHint: ptr.To(true),
|
|
DestructiveHint: ptr.To(false),
|
|
IdempotentHint: ptr.To(false),
|
|
OpenWorldHint: ptr.To(true),
|
|
},
|
|
},
|
|
ClusterAware: ptr.To(false),
|
|
Handler: configurationView,
|
|
},
|
|
}
|
|
return tools
|
|
}
|
|
|
|
func contextsList(params api.ToolHandlerParams) (*api.ToolCallResult, error) {
|
|
contexts, err := params.ConfigurationContextsList()
|
|
if err != nil {
|
|
return api.NewToolCallResult("", fmt.Errorf("failed to list contexts: %v", err)), nil
|
|
}
|
|
|
|
if len(contexts) == 0 {
|
|
return api.NewToolCallResult("No contexts found in kubeconfig", nil), nil
|
|
}
|
|
|
|
defaultContext, err := params.ConfigurationContextsDefault()
|
|
if err != nil {
|
|
return api.NewToolCallResult("", fmt.Errorf("failed to get default context: %v", err)), nil
|
|
}
|
|
|
|
result := fmt.Sprintf("Available Kubernetes contexts (%d total, default: %s):\n\n", len(contexts), defaultContext)
|
|
result += "Format: [*] CONTEXT_NAME -> SERVER_URL\n"
|
|
result += " (* indicates the default context used in tools if context is not set)\n\n"
|
|
result += "Contexts:\n---------\n"
|
|
for context, server := range contexts {
|
|
marker := " "
|
|
if context == defaultContext {
|
|
marker = "*"
|
|
}
|
|
|
|
result += fmt.Sprintf("%s%s -> %s\n", marker, context, server)
|
|
}
|
|
result += "---------\n\n"
|
|
|
|
result += "To use a specific context with any tool, set the 'context' parameter in the tool call arguments"
|
|
|
|
// TODO: Review output format, current is not parseable and might not be ideal for LLM consumption
|
|
return api.NewToolCallResult(result, nil), nil
|
|
}
|
|
|
|
func configurationView(params api.ToolHandlerParams) (*api.ToolCallResult, error) {
|
|
minify := true
|
|
minified := params.GetArguments()["minified"]
|
|
if _, ok := minified.(bool); ok {
|
|
minify = minified.(bool)
|
|
}
|
|
ret, err := params.ConfigurationView(minify)
|
|
if err != nil {
|
|
return api.NewToolCallResult("", fmt.Errorf("failed to get configuration: %v", err)), nil
|
|
}
|
|
configurationYaml, err := output.MarshalYaml(ret)
|
|
if err != nil {
|
|
err = fmt.Errorf("failed to get configuration: %v", err)
|
|
}
|
|
return api.NewToolCallResult(configurationYaml, err), nil
|
|
}
|