mirror of
https://github.com/containers/kubernetes-mcp-server.git
synced 2025-10-23 01:22:57 +03:00
feat: configuration minification is optional
This commit is contained in:
@@ -5,7 +5,7 @@ import (
|
||||
"k8s.io/client-go/tools/clientcmd/api/latest"
|
||||
)
|
||||
|
||||
func ConfigurationView() (string, error) {
|
||||
func ConfigurationView(minify bool) (string, error) {
|
||||
var cfg clientcmdapi.Config
|
||||
var err error
|
||||
inClusterConfig, err := InClusterConfig()
|
||||
@@ -26,8 +26,10 @@ func ConfigurationView() (string, error) {
|
||||
} else if cfg, err = resolveConfig().RawConfig(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err = clientcmdapi.MinifyConfig(&cfg); err != nil {
|
||||
return "", err
|
||||
if minify {
|
||||
if err = clientcmdapi.MinifyConfig(&cfg); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
if err = clientcmdapi.FlattenConfig(&cfg); err != nil {
|
||||
return "", err
|
||||
|
||||
@@ -144,19 +144,24 @@ func testCase(t *testing.T, test func(c *mcpContext)) {
|
||||
// withKubeConfig sets up a fake kubeconfig in the temp directory based on the provided rest.Config
|
||||
func (c *mcpContext) withKubeConfig(rc *rest.Config) *api.Config {
|
||||
fakeConfig := api.NewConfig()
|
||||
fakeConfig.CurrentContext = "fake-context"
|
||||
fakeConfig.Contexts["fake-context"] = api.NewContext()
|
||||
fakeConfig.Contexts["fake-context"].Cluster = "fake"
|
||||
fakeConfig.Contexts["fake-context"].AuthInfo = "fake"
|
||||
fakeConfig.Clusters["fake"] = api.NewCluster()
|
||||
fakeConfig.Clusters["fake"].Server = "https://example.com"
|
||||
fakeConfig.Clusters["additional-cluster"] = api.NewCluster()
|
||||
fakeConfig.AuthInfos["fake"] = api.NewAuthInfo()
|
||||
fakeConfig.AuthInfos["additional-auth"] = api.NewAuthInfo()
|
||||
if rc != nil {
|
||||
fakeConfig.Clusters["fake"].Server = rc.Host
|
||||
fakeConfig.Clusters["fake"].CertificateAuthorityData = rc.TLSClientConfig.CAData
|
||||
fakeConfig.AuthInfos["fake"].ClientKeyData = rc.TLSClientConfig.KeyData
|
||||
fakeConfig.AuthInfos["fake"].ClientCertificateData = rc.TLSClientConfig.CertData
|
||||
}
|
||||
fakeConfig.Contexts["fake-context"] = api.NewContext()
|
||||
fakeConfig.Contexts["fake-context"].Cluster = "fake"
|
||||
fakeConfig.Contexts["fake-context"].AuthInfo = "fake"
|
||||
fakeConfig.Contexts["additional-context"] = api.NewContext()
|
||||
fakeConfig.Contexts["additional-context"].Cluster = "additional-cluster"
|
||||
fakeConfig.Contexts["additional-context"].AuthInfo = "additional-auth"
|
||||
fakeConfig.CurrentContext = "fake-context"
|
||||
kubeConfig := filepath.Join(c.tempDir, "config")
|
||||
_ = clientcmd.WriteToFile(*fakeConfig, kubeConfig)
|
||||
_ = os.Setenv("KUBECONFIG", kubeConfig)
|
||||
|
||||
@@ -12,12 +12,21 @@ func (s *Server) initConfiguration() []server.ServerTool {
|
||||
return []server.ServerTool{
|
||||
{mcp.NewTool("configuration_view",
|
||||
mcp.WithDescription("Get the current Kubernetes configuration content as a kubeconfig YAML"),
|
||||
mcp.WithBoolean("minified", mcp.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)")),
|
||||
), configurationView},
|
||||
}
|
||||
}
|
||||
|
||||
func configurationView(_ context.Context, _ mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
ret, err := kubernetes.ConfigurationView()
|
||||
func configurationView(_ context.Context, ctr mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
minify := true
|
||||
minified := ctr.Params.Arguments["minified"]
|
||||
if _, ok := minified.(bool); ok {
|
||||
minify = minified.(bool)
|
||||
}
|
||||
ret, err := kubernetes.ConfigurationView(minify)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to get configuration: %v", err)
|
||||
}
|
||||
|
||||
@@ -26,40 +26,87 @@ func TestConfigurationView(t *testing.T) {
|
||||
})
|
||||
t.Run("configuration_view returns current-context", func(t *testing.T) {
|
||||
if decoded.CurrentContext != "fake-context" {
|
||||
t.Fatalf("fake-context not found: %v", decoded.CurrentContext)
|
||||
t.Errorf("fake-context not found: %v", decoded.CurrentContext)
|
||||
}
|
||||
})
|
||||
t.Run("configuration_view returns context info", func(t *testing.T) {
|
||||
if len(decoded.Contexts) != 1 {
|
||||
t.Fatalf("invalid context count, expected 1, got %v", len(decoded.Contexts))
|
||||
t.Errorf("invalid context count, expected 1, got %v", len(decoded.Contexts))
|
||||
}
|
||||
if decoded.Contexts[0].Name != "fake-context" {
|
||||
t.Fatalf("fake-context not found: %v", decoded.Contexts)
|
||||
t.Errorf("fake-context not found: %v", decoded.Contexts)
|
||||
}
|
||||
if decoded.Contexts[0].Context.Cluster != "fake" {
|
||||
t.Fatalf("fake-cluster not found: %v", decoded.Contexts)
|
||||
t.Errorf("fake-cluster not found: %v", decoded.Contexts)
|
||||
}
|
||||
if decoded.Contexts[0].Context.AuthInfo != "fake" {
|
||||
t.Fatalf("fake-auth not found: %v", decoded.Contexts)
|
||||
t.Errorf("fake-auth not found: %v", decoded.Contexts)
|
||||
}
|
||||
})
|
||||
t.Run("configuration_view returns cluster info", func(t *testing.T) {
|
||||
if len(decoded.Clusters) != 1 {
|
||||
t.Fatalf("invalid cluster count, expected 1, got %v", len(decoded.Clusters))
|
||||
t.Errorf("invalid cluster count, expected 1, got %v", len(decoded.Clusters))
|
||||
}
|
||||
if decoded.Clusters[0].Name != "fake" {
|
||||
t.Fatalf("fake-cluster not found: %v", decoded.Clusters)
|
||||
t.Errorf("fake-cluster not found: %v", decoded.Clusters)
|
||||
}
|
||||
if decoded.Clusters[0].Cluster.Server != "https://example.com" {
|
||||
t.Fatalf("fake-server not found: %v", decoded.Clusters)
|
||||
t.Errorf("fake-server not found: %v", decoded.Clusters)
|
||||
}
|
||||
})
|
||||
t.Run("configuration_view returns auth info", func(t *testing.T) {
|
||||
if len(decoded.AuthInfos) != 1 {
|
||||
t.Fatalf("invalid auth info count, expected 1, got %v", len(decoded.AuthInfos))
|
||||
t.Errorf("invalid auth info count, expected 1, got %v", len(decoded.AuthInfos))
|
||||
}
|
||||
if decoded.AuthInfos[0].Name != "fake" {
|
||||
t.Fatalf("fake-auth not found: %v", decoded.AuthInfos)
|
||||
t.Errorf("fake-auth not found: %v", decoded.AuthInfos)
|
||||
}
|
||||
})
|
||||
toolResult, err = c.callTool("configuration_view", map[string]interface{}{
|
||||
"minified": false,
|
||||
})
|
||||
t.Run("configuration_view with minified=false returns configuration", func(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("call tool failed %v", err)
|
||||
}
|
||||
})
|
||||
err = yaml.Unmarshal([]byte(toolResult.Content[0].(mcp.TextContent).Text), &decoded)
|
||||
t.Run("configuration_view with minified=false has yaml content", func(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("invalid tool result content %v", err)
|
||||
}
|
||||
})
|
||||
t.Run("configuration_view with minified=false returns additional context info", func(t *testing.T) {
|
||||
if len(decoded.Contexts) != 2 {
|
||||
t.Errorf("invalid context count, expected2, got %v", len(decoded.Contexts))
|
||||
}
|
||||
if decoded.Contexts[0].Name != "additional-context" {
|
||||
t.Errorf("additional-context not found: %v", decoded.Contexts)
|
||||
}
|
||||
if decoded.Contexts[0].Context.Cluster != "additional-cluster" {
|
||||
t.Errorf("additional-cluster not found: %v", decoded.Contexts)
|
||||
}
|
||||
if decoded.Contexts[0].Context.AuthInfo != "additional-auth" {
|
||||
t.Errorf("additional-auth not found: %v", decoded.Contexts)
|
||||
}
|
||||
if decoded.Contexts[1].Name != "fake-context" {
|
||||
t.Errorf("fake-context not found: %v", decoded.Contexts)
|
||||
}
|
||||
})
|
||||
t.Run("configuration_view with minified=false returns cluster info", func(t *testing.T) {
|
||||
if len(decoded.Clusters) != 2 {
|
||||
t.Errorf("invalid cluster count, expected 2, got %v", len(decoded.Clusters))
|
||||
}
|
||||
if decoded.Clusters[0].Name != "additional-cluster" {
|
||||
t.Errorf("additional-cluster not found: %v", decoded.Clusters)
|
||||
}
|
||||
})
|
||||
t.Run("configuration_view with minified=false returns auth info", func(t *testing.T) {
|
||||
if len(decoded.AuthInfos) != 2 {
|
||||
t.Errorf("invalid auth info count, expected 2, got %v", len(decoded.AuthInfos))
|
||||
}
|
||||
if decoded.AuthInfos[0].Name != "additional-auth" {
|
||||
t.Errorf("additional-auth not found: %v", decoded.AuthInfos)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user