{"slug": "deep-dive-testing-radar-ui-for-kubernetes-using-mcp-a-go-gui-and-an-autonomous", "title": "Deep Dive: Testing Radar UI for Kubernetes using MCP, a Go GUI, and an Autonomous Agent", "summary": "Radar, a Kubernetes dashboard from radarhq.io, integrates a Model Context Protocol (MCP) server to expose cluster state for external clients and AI agents. A developer tested the system on macOS using Minikube with Podman and CRI-O, deploying a Go workload and running a Fyne-based GUI client and an autonomous ReAct agent that performed cluster diagnostics over MCP.", "body_md": "Hand-on test of [radarhq.io](https://radarhq.io/) K8S UI and dashboard on a macOS with Minikube and Podman\n\nKubernetes dashboards are often either overloaded with unnecessary complexity or too minimalist to provide deep operational context during an outage. **Radar** ([radarhq.io](https://radarhq.io/)) takes a refreshingly modern approach. It not only delivers a clean visual cluster dashboard but also natively integrates a **Model Context Protocol (MCP)** server.\n\nBy exposing cluster state — deployments, pods, topology, live operational issues, and logs — via MCP, Radar allows external clients, CLI tools, and AI agents to programmatically query and analyze Kubernetes workloads without direct, high-privilege access to the raw Kubernetes API.\n\nIn this blog post, I explore an end-to-end testing environment for Radar. I tested the overall system architecture, set up a sample Go workload (hello-k8s), inspect a Fyne-based GUI desktop client, and run an autonomous 7-step ReAct agent that performs cluster diagnostics over MCP.\n\nI used IBM Bob SDLC for the implementation.\n\nTo test Radar locally on macOS without Docker Desktop, I leveraged **Minikube** powered by the **Podman** driver and **CRI-O** runtime.\n\nBecause Minikube’s Podman driver places the VM inside an AppleHV VM whose internal IP (`192.168.49.x`\n\n) is not directly routed to the host machine, I implemented background kubectl port-forward tunnels to bridge the local host to the internal cluster services:\n\n`localhost:30928`\n\n→ Radar Pod (`:9280`\n\n)`localhost:30800`\n\n→ `hello-k8s`\n\nPod (`:8080`\n\n)`hello-k8s`\n\n)\nTo give Radar and our MCP clients a realistic workload to inspect, a a lightweight HTTP application written in Go is deploye. The application tracks request counts, renders pod metadata obtained via the Kubernetes Downward API, and exposes health probes at `/healthz`\n\n.\n\n`k8s/hello-k8s.yaml`\n\n)\nA **2-replica deployment** is set-up, so that Radar can construct a multi-pod service topology graph:\n\n```\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: hello-k8s\n  namespace: hello-k8s\n  labels:\n    app: hello-k8s\nspec:\n  replicas: 2\n  selector:\n    matchLabels:\n      app: hello-k8s\n  template:\n    metadata:\n      labels:\n        app: hello-k8s\n    spec:\n      containers:\n        - name: hello-k8s\n          image: hello-k8s:latest\n          imagePullPolicy: Never\n          ports:\n            - name: http\n              containerPort: 8080\n          env:\n            - name: PORT\n              value: \"8080\"\n            - name: POD_NAME\n              valueFrom:\n                fieldRef:\n                  fieldPath: metadata.name\n            - name: POD_NAMESPACE\n              valueFrom:\n                fieldRef:\n                  fieldPath: metadata.namespace\n          readinessProbe:\n            httpGet:\n              path: /healthz\n              port: 8080\n            initialDelaySeconds: 3\n            periodSeconds: 5\n          resources:\n            requests:\n              cpu: 10m\n              memory: 16Mi\n            limits:\n              cpu: 100m\n              memory: 64Mi\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: hello-k8s\n  namespace: hello-k8s\nspec:\n  type: NodePort\n  selector:\n    app: hello-k8s\n  ports:\n    - name: http\n      port: 8080\n      targetPort: 8080\n      nodePort: 30800\n```\n\nRather than accessing raw Kubernetes endpoints directly, the custom GUI client communicates purely over HTTP JSON-RPC using the **Model Context Protocol (MCP)** endpoint provided by Radar at `/mcp`\n\n.\n\nThe client uses the official `@modelcontextprotocol/go-sdk`\n\nto establish stateless HTTP transport sessions and execute tools like get_dashboard and issues.\n\n`gui-client/main.go`\n\n)\n\n```\nfunc (c *MCPClient) callTool(ctx context.Context, toolName string, args map[string]any) ([]byte, error) {\n  client := mcp.NewClient(&mcp.Implementation{\n    Name:    \"radar-mcp-gui\",\n    Version: \"1.0.0\",\n  }, nil)\n​\n  transport := &mcp.StreamableClientTransport{\n    Endpoint: c.endpointURL,\n  }\n​\n  session, err := client.Connect(ctx, transport, nil)\n  if err != nil {\n    return nil, fmt.Errorf(\"connect to %s: %w\", c.endpointURL, err)\n  }\n  defer session.Close()\n​\n  result, err := session.CallTool(ctx, &mcp.CallToolParams{\n    Name:      toolName,\n    Arguments: args,\n  })\n  if err != nil {\n    return nil, fmt.Errorf(\"call %q: %w\", toolName, err)\n  }\n​\n  var combined []byte\n  for _, content := range result.Content {\n    if tc, ok := content.(*mcp.TextContent); ok {\n      combined = append(combined, []byte(tc.Text)...)\n    }\n  }\n  return combined, nil\n}\n```\n\nTo keep the UI responsive, a background goroutine fetches data snapshots on every poll interval (e.g., 10s) and passes updates over a buffered channel to the main thread:\n\n```\n// Background fetch loop\nsnapCh := make(chan ClusterSnapshot, 1)\n​\ngo func() {\n  ctx := context.Background()\n  for {\n    snap := fetchSnapshot(ctx, mcpClient)\n    select {\n    case snapCh <- snap:\n    default: // Drop if UI thread is busy\n    }\n    time.Sleep(cfg.PollInterval)\n  }\n}()\n```\n\nTo automate workload inspection, a Go CLI agent (`radar-agent`\n\n) that acts as an autonomous operator is implemented. It runs a deterministic 7-step ReAct (Reason + Act) loop using Radar's MCP tool set.\n\n`agent/main.go`\n\n)\nHere is an excerpt showing how the agent executes the observation phase and lists resources:\n\n```\nfunc run() error {\n  flag.Parse()\n  client := newMCPClient()\n  ctx    := context.Background()\n​\n  // Step 1: OBSERVE cluster health\n  step(1, \"OBSERVE\", \"cluster overview via get_dashboard\")\n  dash, err := stepObserve(ctx, client, *flagNamespace)\n  if err != nil {\n    return fmt.Errorf(\"observe: %w\", err)\n  }\n​\n  // Step 2: FOCUS on targeted pods\n  step(2, \"FOCUS\", fmt.Sprintf(\"list pods in namespace %q\", *flagNamespace))\n  pods, err := stepListPods(ctx, client, *flagNamespace)\n  if err != nil {\n    fmt.Printf(\"  Warning: %v\\n\", err)\n  }\n​\n  // Step 3: INSPECT Deployment spec/status\n  step(3, \"INSPECT\", fmt.Sprintf(\"get_resource deployment/%s\", *flagWorkload))\n  dep, _ := stepInspectDeployment(ctx, client, *flagNamespace, *flagWorkload)\n​\n  // Step 4: TOPOLOGY graph analysis\n  step(4, \"TOPOLOGY\", fmt.Sprintf(\"get_topology namespace=%q\", *flagNamespace))\n  topo, _ := stepTopology(ctx, client, *flagNamespace)\n​\n  // Step 5: Live operational ISSUES\n  step(5, \"ISSUES\", fmt.Sprintf(\"issues namespace=%q\", *flagNamespace))\n  issues, _ := stepIssues(ctx, client, *flagNamespace)\n​\n  // Step 6: Sample LOGS\n  step(6, \"LOGS\", fmt.Sprintf(\"get_workload_logs deployment/%s\", *flagWorkload))\n  logs, _ := stepLogs(ctx, client, *flagNamespace, *flagWorkload)\n​\n  // Step 7: Print Final Summary\n  step(7, \"REPORT\", \"structured summary\")\n  // ... renders formatted report to stdout ...\n  return nil\n}\n```\n\nTo test the full stack on your local machine, follow these steps:\n\nRun the setup script to initialize Minikube, deploy Radar via Helm, build `hello-k8s`\n\n, apply RBAC patches, and start port-forwarding:\n\n```\nchmod +x scripts/*.sh\n./scripts/deploy.sh\n./scripts/run-agent.sh --namespace hello-k8s --workload hello-k8s\n════════════════════════════════════════════════════════════\n  RADAR AGENT REPORT\n  Goal      : Investigate the hello-k8s workload and summarise its health\n  Namespace : hello-k8s\n  Workload  : hello-k8s\n════════════════════════════════════════════════════════════\n​\n  CLUSTER OVERVIEW\n  Health:                healthy  (cluster: minikube v1.28.0)\n  Pods:                  healthy=4  warning=0  error=0\n  Nodes:                 total=1  ready=1  notReady=0\n  Total problems:        0\n​\n  PODS IN NAMESPACE \"hello-k8s\"\n  hello-k8s-74b884988f-2k9ll                    Running     ready=1/1  node=minikube\n  hello-k8s-74b884988f-b98x7                    Running     ready=1/1  node=minikube\n​\n  DEPLOYMENT: hello-k8s\n  Replicas:              2 desired / 2 ready / 2 available\n  Container:             hello-k8s → hello-k8s:latest\n​\n  TOPOLOGY (namespace \"hello-k8s\")\n  3 nodes, 2 edges\n​\n  LIVE ISSUES\n  No issues found — workload looks healthy.\n════════════════════════════════════════════════════════════\n./scripts/start.sh\n| Technical Decision                   | Rationale                                                    |\n| ------------------------------------ | ------------------------------------------------------------ |\n| **Podman Driver + CRI-O**            | Enables containerized Kubernetes testing on macOS without relying on Docker Desktop.  MD |\n| **`kubectl port-forward` Tunneling** | Solves AppleHV VM network isolation by routing `localhost:30928` directly to Radar's container port.  MD |\n| **Chart Tag Pinning (`1.7.0`)**      | Prevents version mismatch crashes with Helm flags present in newer chart versions.  MD |\n| **RBAC Supplemental Patch**          | Adds missing `rbac.authorization.k8s.io` read permissions required for Radar's permission inspection panels to function properly.  MD |\n| **Model Context Protocol (MCP)**     | Decouples direct Kubernetes API access from monitoring tools, allowing lightweight agents and GUIs to consume structured cluster context safely.  MD+ 1 |\n```\n\nBy exposing cluster telemetry and operational controls through the Model Context Protocol, Radar transforms Kubernetes monitoring from a manual dashboard-checking task into a programmatic foundation for automation. Whether powering custom desktop GUIs like Fyne or enabling autonomous ReAct agents to run multi-step diagnostic workflows, MCP bridges the gap between raw cluster metrics and intelligent operational tools. As container environments continue to grow in complexity, decoupling cluster context from high-privilege API access via MCP offers a cleaner, safer, and far more extensible approach to Kubernetes management.\n\n**Thanks for reading 🍇**", "url": "https://wpnews.pro/news/deep-dive-testing-radar-ui-for-kubernetes-using-mcp-a-go-gui-and-an-autonomous", "canonical_source": "https://dev.to/aairom/deep-dive-testing-radar-ui-for-kubernetes-using-mcp-a-go-gui-and-an-autonomous-agent-4ioi", "published_at": "2026-08-25 07:31:59+00:00", "updated_at": "2026-08-25 07:43:44.615946+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents", "ai-infrastructure"], "entities": ["Radar", "radarhq.io", "Minikube", "Podman", "CRI-O", "Fyne", "MCP", "Go"], "alternates": {"html": "https://wpnews.pro/news/deep-dive-testing-radar-ui-for-kubernetes-using-mcp-a-go-gui-and-an-autonomous", "markdown": "https://wpnews.pro/news/deep-dive-testing-radar-ui-for-kubernetes-using-mcp-a-go-gui-and-an-autonomous.md", "text": "https://wpnews.pro/news/deep-dive-testing-radar-ui-for-kubernetes-using-mcp-a-go-gui-and-an-autonomous.txt", "jsonld": "https://wpnews.pro/news/deep-dive-testing-radar-ui-for-kubernetes-using-mcp-a-go-gui-and-an-autonomous.jsonld"}}