Repository: containers/kubernetes-mcp-server

Issue:
Derived Kubernetes test for  staticConfig
LGTM, thx!

> Since we forgot to wire staticConfig to Derived object, allow/deny resource list is not actually working.

A test for this would be interesting to prevent future issues.

_Originally posted by @manusa in https://github.com/manusa/kubernetes-mcp-server/pull/159#pullrequestreview-2978950116_
            

Hints from issue comments:
@ardaguclu: I think, this is completed?. @manusa: > I think, this is completed?.

Yes, thx. Fixed by #167.


Solution patch:
diff --git a/pkg/http/http.go b/pkg/http/http.go
index b8a07e2..c1fcc34 100644
--- a/pkg/http/http.go
+++ b/pkg/http/http.go
@@ -1,61 +1,71 @@
 package http
 
 import (
-	"bufio"
-	"net"
+	"context"
+	"errors"
 	"net/http"
+	"os"
+	"os/signal"
+	"syscall"
 	"time"
 
 	"k8s.io/klog/v2"
-)
 
-func RequestMiddleware(next http.Handler) http.Handler {
-	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		start := time.Now()
+	"github.com/manusa/kubernetes-mcp-server/pkg/mcp"
+)
 
-		lrw := &loggingResponseWriter{
-			ResponseWriter: w,
-			statusCode:     http.StatusOK,
-		}
+func Serve(ctx context.Context, mcpServer *mcp.Server, port, sseBaseUrl string) error {
+	mux := http.NewServeMux()
+	wrappedMux := RequestMiddleware(mux)
 
-		next.ServeHTTP(lrw, r)
+	httpServer := &http.Server{
+		Addr:    ":" + port,
+		Handler: wrappedMux,
+	}
 
-		duration := time.Since(start)
-		klog.V(5).Infof("%s %s %d %v", r.Method, r.URL.Path, lrw.statusCode, duration)
+	sseServer := mcpServer.ServeSse(sseBaseUrl, httpServer)
+	streamableHttpServer := mcpServer.ServeHTTP(httpServer)
+	mux.Handle("/sse", sseServer)
+	mux.Handle("/message", sseServer)
+	mux.Handle("/mcp", streamableHttpServer)
+	mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
+		w.WriteHeader(http.StatusOK)
 	})
-}
 
-type loggingResponseWriter struct {
-	http.ResponseWriter
-	statusCode    int
-	headerWritten bool
-}
+	ctx, cancel := context.WithCancel(ctx)
+	defer cancel()
 
-func (lrw *loggingResponseWriter) WriteHeader(code int) {
-	if !lrw.headerWritten {
-		lrw.statusCode = code
-		lrw.headerWritten = true
-		lrw.ResponseWriter.WriteHeader(code)
-	}
-}
+	sigChan := make(chan os.Signal, 1)
+	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
 
-func (lrw *loggingResponseWriter) Write(b []byte) (int, error) {
-	if !lrw.headerWritten {
-		lrw.statusCode = http.StatusOK
-		lrw.headerWritten = true
-	}
-	return lrw.ResponseWriter.Write(b)
-}
+	serverErr := make(chan error, 1)
+	go func() {
+		klog.V(0).Infof("Streaming and SSE HTTP servers starting on port %s and paths /mcp, /sse, /message", port)
+		if err := httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
+			serverErr <- err
+		}
+	}()
 
-func (lrw *loggingResponseWriter) Flush() {
-	if flusher, ok := lrw.ResponseWriter.(http.Flusher); ok {
-		flusher.Flush()
+	select {
+	case sig := <-sigChan:
+		klog.V(0).Infof("Received signal %v, initiating graceful shutdown", sig)
+		cancel()
+	case <-ctx.Done():
+		klog.V(0).Infof("Context cancelled, initiating graceful shutdown")
+	case err := <-serverErr:
+		klog.Errorf("HTTP server error: %v", err)
+		return err
 	}
-}
 
-func (lrw *loggingResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
-	if hijacker, ok := lrw.ResponseWriter.(http.Hijacker); ok {
-		return hijacker.Hijack()
+	shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 10*time.Second)
+	defer shutdownCancel()
+
+	klog.V(0).Infof("Shutting down HTTP server gracefully...")
+	if err := httpServer.Shutdown(shutdownCtx); err != nil {
+		klog.Errorf("HTTP server shutdown error: %v", err)
+		return err
 	}
-	return nil, nil, http.ErrNotSupported
+
+	klog.V(0).Infof("HTTP server shutdown complete")
+	return nil
 }
diff --git a/pkg/http/middleware.go b/pkg/http/middleware.go
new file mode 100644
index 0000000..b8a07e2
--- /dev/null
+++ b/pkg/http/middleware.go
@@ -0,0 +1,61 @@
+package http
+
+import (
+	"bufio"
+	"net"
+	"net/http"
+	"time"
+
+	"k8s.io/klog/v2"
+)
+
+func RequestMiddleware(next http.Handler) http.Handler {
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		start := time.Now()
+
+		lrw := &loggingResponseWriter{
+			ResponseWriter: w,
+			statusCode:     http.StatusOK,
+		}
+
+		next.ServeHTTP(lrw, r)
+
+		duration := time.Since(start)
+		klog.V(5).Infof("%s %s %d %v", r.Method, r.URL.Path, lrw.statusCode, duration)
+	})
+}
+
+type loggingResponseWriter struct {
+	http.ResponseWriter
+	statusCode    int
+	headerWritten bool
+}
+
+func (lrw *loggingResponseWriter) WriteHeader(code int) {
+	if !lrw.headerWritten {
+		lrw.statusCode = code
+		lrw.headerWritten = true
+		lrw.ResponseWriter.WriteHeader(code)
+	}
+}
+
+func (lrw *loggingResponseWriter) Write(b []byte) (int, error) {
+	if !lrw.headerWritten {
+		lrw.statusCode = http.StatusOK
+		lrw.headerWritten = true
+	}
+	return lrw.ResponseWriter.Write(b)
+}
+
+func (lrw *loggingResponseWriter) Flush() {
+	if flusher, ok := lrw.ResponseWriter.(http.Flusher); ok {
+		flusher.Flush()
+	}
+}
+
+func (lrw *loggingResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
+	if hijacker, ok := lrw.ResponseWriter.(http.Hijacker); ok {
+		return hijacker.Hijack()
+	}
+	return nil, nil, http.ErrNotSupported
+}
diff --git a/pkg/kubernetes-mcp-server/cmd/root.go b/pkg/kubernetes-mcp-server/cmd/root.go
index 4608f23..32046e3 100644
--- a/pkg/kubernetes-mcp-server/cmd/root.go
+++ b/pkg/kubernetes-mcp-server/cmd/root.go
@@ -5,7 +5,6 @@ import (
 	"errors"
 	"flag"
 	"fmt"
-	"net/http"
 	"strconv"
 	"strings"
 
@@ -206,28 +205,8 @@ func (m *MCPServerOptions) Run() error {
 	defer mcpServer.Close()
 
 	if m.StaticConfig.Port != "" {
-		mux := http.NewServeMux()
-		wrappedMux := internalhttp.RequestMiddleware(mux)
-
-		httpServer := &http.Server{
-			Addr:    ":" + m.StaticConfig.Port,
-			Handler: wrappedMux,
-		}
-
-		sseServer := mcpServer.ServeSse(m.SSEBaseUrl, httpServer)
-		streamableHttpServer := mcpServer.ServeHTTP(httpServer)
-		mux.Handle("/sse", sseServer)
-		mux.Handle("/message", sseServer)
-		mux.Handle("/mcp", streamableHttpServer)
-		mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
-			w.WriteHeader(http.StatusOK)
-		})
-
-		klog.V(0).Infof("Streaming and SSE HTTP servers starting on port %s and paths /mcp, /sse, /message", m.StaticConfig.Port)
-		if err := httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
-			return err
-		}
-		return nil
+		ctx := context.Background()
+		return internalhttp.Serve(ctx, mcpServer, m.StaticConfig.Port, m.SSEBaseUrl)
 	}
 
 	if err := mcpServer.ServeStdio(); err != nil && !errors.Is(err, context.Canceled) {
diff --git a/pkg/kubernetes/configuration.go b/pkg/kubernetes/configuration.go
index 90a3049..eafff04 100644
--- a/pkg/kubernetes/configuration.go
+++ b/pkg/kubernetes/configuration.go
@@ -24,8 +24,8 @@ var InClusterConfig = func() (*rest.Config, error) {
 func resolveKubernetesConfigurations(kubernetes *Manager) error {
 	// Always set clientCmdConfig
 	pathOptions := clientcmd.NewDefaultPathOptions()
-	if kubernetes.Kubeconfig != "" {
-		pathOptions.LoadingRules.ExplicitPath = kubernetes.Kubeconfig
+	if kubernetes.staticConfig.KubeConfig != "" {
+		pathOptions.LoadingRules.ExplicitPath = kubernetes.staticConfig.KubeConfig
 	}
 	kubernetes.clientCmdConfig = clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
 		pathOptions.LoadingRules,
@@ -46,7 +46,7 @@ func resolveKubernetesConfigurations(kubernetes *Manager) error {
 }
 
 func (m *Manager) IsInCluster() bool {
-	if m.Kubeconfig != "" {
+	if m.staticConfig.KubeConfig != "" {
 		return false
 	}
 	cfg, err := InClusterConfig()
diff --git a/pkg/kubernetes/kubernetes.go b/pkg/kubernetes/kubernetes.go
index d1b7020..1583567 100644
--- a/pkg/kubernetes/kubernetes.go
+++ b/pkg/kubernetes/kubernetes.go
@@ -39,8 +39,6 @@ type Kubernetes struct {
 }
 
 type Manager struct {
-	// Kubeconfig path override
-	Kubeconfig              string
 	cfg                     *rest.Config
 	clientCmdConfig         clientcmd.ClientConfig
 	discoveryClient         discovery.CachedDiscoveryInterface
@@ -57,9 +55,8 @@ var ParameterCodec = runtime.NewParameterCodec(Scheme)
 
 var _ helm.Kubernetes = &Manager{}
 
-func NewManager(kubeconfig string, config *config.StaticConfig) (*Manager, error) {
+func NewManager(config *config.StaticConfig) (*Manager, error) {
 	k8s := &Manager{
-		Kubeconfig:   kubeconfig,
 		staticConfig: config,
 	}
 	if err := resolveKubernetesConfigurations(k8s); err != nil {
@@ -166,7 +163,6 @@ func (m *Manager) Derived(ctx context.Context) *Kubernetes {
 	}
 	clientCmdApiConfig.AuthInfos = make(map[string]*clientcmdapi.AuthInfo)
 	derived := &Kubernetes{manager: &Manager{
-		Kubeconfig:      m.Kubeconfig,
 		clientCmdConfig: clientcmd.NewDefaultClientConfig(clientCmdApiConfig, nil),
 		cfg:             derivedCfg,
 		staticConfig:    m.staticConfig,
diff --git a/pkg/mcp/mcp.go b/pkg/mcp/mcp.go
index d9438d6..1916374 100644
--- a/pkg/mcp/mcp.go
+++ b/pkg/mcp/mcp.go
@@ -65,7 +65,7 @@ func NewServer(configuration Configuration) (*Server, error) {
 }
 
 func (s *Server) reloadKubernetesClient() error {
-	k, err := internalk8s.NewManager(s.configuration.StaticConfig.KubeConfig, s.configuration.StaticConfig)
+	k, err := internalk8s.NewManager(s.configuration.StaticConfig)
 	if err != nil {
 		return err
 	}



Test patch:
diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go
index cbae910..1f52361 100644
--- a/pkg/config/config_test.go
+++ b/pkg/config/config_test.go
@@ -52,10 +52,11 @@ func TestReadConfigValid(t *testing.T) {
 	validConfigPath := writeConfig(t, `
 log_level = 1
 port = "9999"
-kubeconfig = "test"
+sse_base_url = "https://example.com"
+kubeconfig = "./path/to/config"
 list_output = "yaml"
 read_only = true
-disable_destructive = false
+disable_destructive = true
 
 denied_resources = [
     {group = "apps", version = "v1", kind = "Deployment"},
@@ -84,33 +85,62 @@ disabled_tools = ["pods_delete", "pods_top", "pods_log", "pods_run", "pods_exec"
 			config.DeniedResources[0].Kind != "Deployment" {
 			t.Errorf("Unexpected denied resources: %v", config.DeniedResources[0])
 		}
+	})
+	t.Run("log_level parsed correctly", func(t *testing.T) {
 		if config.LogLevel != 1 {
 			t.Fatalf("Unexpected log level: %v", config.LogLevel)
 		}
+	})
+	t.Run("port parsed correctly", func(t *testing.T) {
 		if config.Port != "9999" {
 			t.Fatalf("Unexpected port value: %v", config.Port)
 		}
-		if config.SSEBaseURL != "" {
+	})
+	t.Run("sse_base_url parsed correctly", func(t *testing.T) {
+		if config.SSEBaseURL != "https://example.com" {
 			t.Fatalf("Unexpected sse_base_url value: %v", config.SSEBaseURL)
 		}
-		if config.KubeConfig != "test" {
+	})
+	t.Run("kubeconfig parsed correctly", func(t *testing.T) {
+		if config.KubeConfig != "./path/to/config" {
 			t.Fatalf("Unexpected kubeconfig value: %v", config.KubeConfig)
 		}
+	})
+	t.Run("list_output parsed correctly", func(t *testing.T) {
 		if config.ListOutput != "yaml" {
 			t.Fatalf("Unexpected list_output value: %v", config.ListOutput)
 		}
+	})
+	t.Run("read_only parsed correctly", func(t *testing.T) {
 		if !config.ReadOnly {
 			t.Fatalf("Unexpected read-only mode: %v", config.ReadOnly)
 		}
-		if config.DisableDestructive {
+	})
+	t.Run("disable_destructive parsed correctly", func(t *testing.T) {
+		if !config.DisableDestructive {
 			t.Fatalf("Unexpected disable destructive: %v", config.DisableDestructive)
 		}
+	})
+	t.Run("enabled_tools parsed correctly", func(t *testing.T) {
 		if len(config.EnabledTools) != 8 {
 			t.Fatalf("Unexpected enabled tools: %v", config.EnabledTools)
+
 		}
+		for i, tool := range []string{"configuration_view", "events_list", "namespaces_list", "pods_list", "resources_list", "resources_get", "resources_create_or_update", "resources_delete"} {
+			if config.EnabledTools[i] != tool {
+				t.Errorf("Expected enabled tool %d to be %s, got %s", i, tool, config.EnabledTools[i])
+			}
+		}
+	})
+	t.Run("disabled_tools parsed correctly", func(t *testing.T) {
 		if len(config.DisabledTools) != 5 {
 			t.Fatalf("Unexpected disabled tools: %v", config.DisabledTools)
 		}
+		for i, tool := range []string{"pods_delete", "pods_top", "pods_log", "pods_run", "pods_exec"} {
+			if config.DisabledTools[i] != tool {
+				t.Errorf("Expected disabled tool %d to be %s, got %s", i, tool, config.DisabledTools[i])
+			}
+		}
 	})
 }
 
diff --git a/pkg/kubernetes-mcp-server/cmd/root_test.go b/pkg/kubernetes-mcp-server/cmd/root_test.go
index 13bd5c7..bdac143 100644
--- a/pkg/kubernetes-mcp-server/cmd/root_test.go
+++ b/pkg/kubernetes-mcp-server/cmd/root_test.go
@@ -103,7 +103,7 @@ func TestConfig(t *testing.T) {
 			t.Fatalf("Expected config to be %s, got %s %v", expectedDisableDestruction, out.String(), err)
 		}
 	})
-	t.Run("set with valid --config, flags override", func(t *testing.T) {
+	t.Run("set with valid --config, flags take precedence", func(t *testing.T) {
 		ioStreams, out := testStream()
 		rootCmd := NewMCPServer(ioStreams)
 		_, file, _, _ := runtime.Caller(0)
diff --git a/pkg/kubernetes/configuration_test.go b/pkg/kubernetes/configuration_test.go
index ef11ed7..856623f 100644
--- a/pkg/kubernetes/configuration_test.go
+++ b/pkg/kubernetes/configuration_test.go
@@ -2,18 +2,23 @@ package kubernetes
 
 import (
 	"errors"
-	"k8s.io/client-go/rest"
 	"os"
 	"path"
 	"runtime"
 	"strings"
 	"testing"
+
+	"k8s.io/client-go/rest"
+
+	"github.com/manusa/kubernetes-mcp-server/pkg/config"
 )
 
 func TestKubernetes_IsInCluster(t *testing.T) {
 	t.Run("with explicit kubeconfig", func(t *testing.T) {
 		m := Manager{
-			Kubeconfig: "kubeconfig",
+			staticConfig: &config.StaticConfig{
+				KubeConfig: "kubeconfig",
+			},
 		}
 		if m.IsInCluster() {
 			t.Errorf("expected not in cluster, got in cluster")
@@ -28,7 +33,9 @@ func TestKubernetes_IsInCluster(t *testing.T) {
 			InClusterConfig = originalFunction
 		}()
 		m := Manager{
-			Kubeconfig: "",
+			staticConfig: &config.StaticConfig{
+				KubeConfig: "",
+			},
 		}
 		if !m.IsInCluster() {
 			t.Errorf("expected in cluster, got not in cluster")
@@ -43,7 +50,9 @@ func TestKubernetes_IsInCluster(t *testing.T) {
 			InClusterConfig = originalFunction
 		}()
 		m := Manager{
-			Kubeconfig: "",
+			staticConfig: &config.StaticConfig{
+				KubeConfig: "",
+			},
 		}
 		if m.IsInCluster() {
 			t.Errorf("expected not in cluster, got in cluster")
@@ -58,7 +67,9 @@ func TestKubernetes_IsInCluster(t *testing.T) {
 			InClusterConfig = originalFunction
 		}()
 		m := Manager{
-			Kubeconfig: "",
+			staticConfig: &config.StaticConfig{
+				KubeConfig: "",
+			},
 		}
 		if m.IsInCluster() {
 			t.Errorf("expected not in cluster, got in cluster")
@@ -72,7 +83,9 @@ func TestKubernetes_ResolveKubernetesConfigurations_Explicit(t *testing.T) {
 			t.Skip("Skipping test on non-linux platforms")
 		}
 		tempDir := t.TempDir()
-		m := Manager{Kubeconfig: path.Join(tempDir, "config")}
+		m := Manager{staticConfig: &config.StaticConfig{
+			KubeConfig: path.Join(tempDir, "config"),
+		}}
 		err := resolveKubernetesConfigurations(&m)
 		if err == nil {
 			t.Errorf("expected error, got nil")
@@ -90,7 +103,9 @@ func TestKubernetes_ResolveKubernetesConfigurations_Explicit(t *testing.T) {
 		if err := os.WriteFile(kubeconfigPath, []byte(""), 0644); err != nil {
 			t.Fatalf("failed to create kubeconfig file: %v", err)
 		}
-		m := Manager{Kubeconfig: kubeconfigPath}
+		m := Manager{staticConfig: &config.StaticConfig{
+			KubeConfig: kubeconfigPath,
+		}}
 		err := resolveKubernetesConfigurations(&m)
 		if err == nil {
 			t.Errorf("expected error, got nil")
@@ -123,7 +138,9 @@ users:
 		if err := os.WriteFile(kubeconfigPath, []byte(kubeconfigContent), 0644); err != nil {
 			t.Fatalf("failed to create kubeconfig file: %v", err)
 		}
-		m := Manager{Kubeconfig: kubeconfigPath}
+		m := Manager{staticConfig: &config.StaticConfig{
+			KubeConfig: kubeconfigPath,
+		}}
 		err := resolveKubernetesConfigurations(&m)
 		if err != nil {
 			t.Fatalf("expected no error, got %v", err)
diff --git a/pkg/kubernetes/kubernetes_test.go b/pkg/kubernetes/kubernetes_test.go
new file mode 100644
index 0000000..fcea7f2
--- /dev/null
+++ b/pkg/kubernetes/kubernetes_test.go
@@ -0,0 +1,211 @@
+package kubernetes
+
+import (
+	"context"
+	"os"
+	"path"
+	"testing"
+
+	"github.com/manusa/kubernetes-mcp-server/pkg/config"
+)
+
+func TestManager_Derived(t *testing.T) {
+	// Create a temporary kubeconfig file for testing
+	tempDir := t.TempDir()
+	kubeconfigPath := path.Join(tempDir, "config")
+	kubeconfigContent := `
+apiVersion: v1
+kind: Config
+clusters:
+- cluster:
+    server: https://test-cluster.example.com
+  name: test-cluster
+contexts:
+- context:
+    cluster: test-cluster
+    user: test-user
+  name: test-context
+current-context: test-context
+users:
+- name: test-user
+  user:
+    username: test-username
+    password: test-password
+`
+	if err := os.WriteFile(kubeconfigPath, []byte(kubeconfigContent), 0644); err != nil {
+		t.Fatalf("failed to create kubeconfig file: %v", err)
+	}
+
+	t.Run("without authorization header returns original manager", func(t *testing.T) {
+		testStaticConfig := &config.StaticConfig{
+			KubeConfig:    kubeconfigPath,
+			DisabledTools: []string{"configuration_view"},
+			DeniedResources: []config.GroupVersionKind{
+				{Group: "apps", Version: "v1", Kind: "Deployment"},
+			},
+		}
+
+		testManager, err := NewManager(testStaticConfig)
+		if err != nil {
+			t.Fatalf("failed to create manager: %v", err)
+		}
+		defer testManager.Close()
+		ctx := context.Background()
+		derived := testManager.Derived(ctx)
+
+		if derived.manager != testManager {
+			t.Errorf("expected original manager, got different manager")
+		}
+	})
+
+	t.Run("with invalid authorization header returns original manager", func(t *testing.T) {
+		testStaticConfig := &config.StaticConfig{
+			KubeConfig:    kubeconfigPath,
+			DisabledTools: []string{"configuration_view"},
+			DeniedResources: []config.GroupVersionKind{
+				{Group: "apps", Version: "v1", Kind: "Deployment"},
+			},
+		}
+
+		testManager, err := NewManager(testStaticConfig)
+		if err != nil {
+			t.Fatalf("failed to create manager: %v", err)
+		}
+		defer testManager.Close()
+		ctx := context.WithValue(context.Background(), OAuthAuthorizationHeader, "invalid-token")
+		derived := testManager.Derived(ctx)
+
+		if derived.manager != testManager {
+			t.Errorf("expected original manager, got different manager")
+		}
+	})
+
+	t.Run("with valid bearer token creates derived manager with correct configuration", func(t *testing.T) {
+		testStaticConfig := &config.StaticConfig{
+			KubeConfig:    kubeconfigPath,
+			DisabledTools: []string{"configuration_view"},
+			DeniedResources: []config.GroupVersionKind{
+				{Group: "apps", Version: "v1", Kind: "Deployment"},
+			},
+		}
+
+		testManager, err := NewManager(testStaticConfig)
+		if err != nil {
+			t.Fatalf("failed to create manager: %v", err)
+		}
+		defer testManager.Close()
+		testBearerToken := "test-bearer-token-123"
+		ctx := context.WithValue(context.Background(), OAuthAuthorizationHeader, "Bearer "+testBearerToken)
+		derived := testManager.Derived(ctx)
+
+		if derived.manager == testManager {
+			t.Errorf("expected new derived manager, got original manager")
+		}
+
+		if derived.manager.staticConfig != testStaticConfig {
+			t.Errorf("staticConfig not properly wired to derived manager")
+		}
+
+		derivedCfg := derived.manager.cfg
+		if derivedCfg == nil {
+			t.Fatalf("derived config is nil")
+		}
+
+		originalCfg := testManager.cfg
+		if derivedCfg.Host != originalCfg.Host {
+			t.Errorf("expected Host %s, got %s", originalCfg.Host, derivedCfg.Host)
+		}
+		if derivedCfg.APIPath != originalCfg.APIPath {
+			t.Errorf("expected APIPath %s, got %s", originalCfg.APIPath, derivedCfg.APIPath)
+		}
+		if derivedCfg.QPS != originalCfg.QPS {
+			t.Errorf("expected QPS %f, got %f", originalCfg.QPS, derivedCfg.QPS)
+		}
+		if derivedCfg.Burst != originalCfg.Burst {
+			t.Errorf("expected Burst %d, got %d", originalCfg.Burst, derivedCfg.Burst)
+		}
+		if derivedCfg.Timeout != originalCfg.Timeout {
+			t.Errorf("expected Timeout %v, got %v", originalCfg.Timeout, derivedCfg.Timeout)
+		}
+
+		if derivedCfg.TLSClientConfig.Insecure != originalCfg.TLSClientConfig.Insecure {
+			t.Errorf("expected TLS Insecure %v, got %v", originalCfg.TLSClientConfig.Insecure, derivedCfg.TLSClientConfig.Insecure)
+		}
+		if derivedCfg.TLSClientConfig.ServerName != originalCfg.TLSClientConfig.ServerName {
+			t.Errorf("expected TLS ServerName %s, got %s", originalCfg.TLSClientConfig.ServerName, derivedCfg.TLSClientConfig.ServerName)
+		}
+		if derivedCfg.TLSClientConfig.CAFile != originalCfg.TLSClientConfig.CAFile {
+			t.Errorf("expected TLS CAFile %s, got %s", originalCfg.TLSClientConfig.CAFile, derivedCfg.TLSClientConfig.CAFile)
+		}
+		if string(derivedCfg.TLSClientConfig.CAData) != string(originalCfg.TLSClientConfig.CAData) {
+			t.Errorf("expected TLS CAData %s, got %s", string(originalCfg.TLSClientConfig.CAData), string(derivedCfg.TLSClientConfig.CAData))
+		}
+
+		if derivedCfg.BearerToken != testBearerToken {
+			t.Errorf("expected BearerToken %s, got %s", testBearerToken, derivedCfg.BearerToken)
+		}
+		if derivedCfg.UserAgent != CustomUserAgent {
+			t.Errorf("expected UserAgent %s, got %s", CustomUserAgent, derivedCfg.UserAgent)
+		}
+
+		// Verify that sensitive fields are NOT copied to prevent credential leakage
+		// The derived config should only use the bearer token from the Authorization header
+		// and not inherit any authentication credentials from the original kubeconfig
+		if derivedCfg.TLSClientConfig.CertFile != "" {
+			t.Errorf("expected TLS CertFile to be empty, got %s", derivedCfg.TLSClientConfig.CertFile)
+		}
+		if derivedCfg.TLSClientConfig.KeyFile != "" {
+			t.Errorf("expected TLS KeyFile to be empty, got %s", derivedCfg.TLSClientConfig.KeyFile)
+		}
+		if len(derivedCfg.TLSClientConfig.CertData) != 0 {
+			t.Errorf("expected TLS CertData to be empty, got %v", derivedCfg.TLSClientConfig.CertData)
+		}
+		if len(derivedCfg.TLSClientConfig.KeyData) != 0 {
+			t.Errorf("expected TLS KeyData to be empty, got %v", derivedCfg.TLSClientConfig.KeyData)
+		}
+
+		if derivedCfg.Username != "" {
+			t.Errorf("expected Username to be empty, got %s", derivedCfg.Username)
+		}
+		if derivedCfg.Password != "" {
+			t.Errorf("expected Password to be empty, got %s", derivedCfg.Password)
+		}
+		if derivedCfg.AuthProvider != nil {
+			t.Errorf("expected AuthProvider to be nil, got %v", derivedCfg.AuthProvider)
+		}
+		if derivedCfg.ExecProvider != nil {
+			t.Errorf("expected ExecProvider to be nil, got %v", derivedCfg.ExecProvider)
+		}
+		if derivedCfg.BearerTokenFile != "" {
+			t.Errorf("expected BearerTokenFile to be empty, got %s", derivedCfg.BearerTokenFile)
+		}
+		if derivedCfg.Impersonate.UserName != "" {
+			t.Errorf("expected Impersonate.UserName to be empty, got %s", derivedCfg.Impersonate.UserName)
+		}
+
+		// Verify that the original manager still has the sensitive data
+		if originalCfg.Username == "" && originalCfg.Password == "" {
+			t.Logf("original kubeconfig shouldn't be modified")
+		}
+
+		// Verify that the derived manager has proper clients initialized
+		if derived.manager.accessControlClientSet == nil {
+			t.Error("expected accessControlClientSet to be initialized")
+		}
+		if derived.manager.accessControlClientSet.staticConfig != testStaticConfig {
+			t.Errorf("staticConfig not properly wired to derived manager")
+		}
+		if derived.manager.discoveryClient == nil {
+			t.Error("expected discoveryClient to be initialized")
+		}
+		if derived.manager.accessControlRESTMapper == nil {
+			t.Error("expected accessControlRESTMapper to be initialized")
+		}
+		if derived.manager.accessControlRESTMapper.staticConfig != testStaticConfig {
+			t.Errorf("staticConfig not properly wired to derived manager")
+		}
+		if derived.manager.dynamicClient == nil {
+			t.Error("expected dynamicClient to be initialized")
+		}
+	})
+}