Rename to k8scope, add 14 new tools, dynamic client, graceful shutdown and error handling#1
Conversation
…n, and error handling
Add unit tests and GitHub Actions CI
There was a problem hiding this comment.
Pull request overview
This PR renames the project to k8scope, expands the MCP server’s Kubernetes toolset (including dynamic-client-backed tooling for CRDs/generic resources), and adds unit tests plus a GitHub Actions CI workflow.
Changes:
- Renamed module/binary/docs from
kubelens→k8scope(Go module path, server name, Dockerfile, README). - Added 14 Kubernetes tools (20 total) including CRD listing/instances and generic YAML retrieval via a dynamic Kubernetes client.
- Added tests across
internal/auth,internal/k8s, andinternal/tools, and introduced a CI workflow to build/vet/test.
Reviewed changes
Copilot reviewed 14 out of 16 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| README.md | Updates documentation and examples to use k8scope. |
| cmd/server/main.go | Updates server identity/logging strings to k8scope. |
| Dockerfile | Renames built binary and entrypoint to /k8scope. |
| go.mod | Renames module path and updates YAML dependency version. |
| go.sum | Adds/updates dependency checksums for new/updated deps. |
| internal/k8s/client.go | Adds NewDynamicClientForUser for CRDs/generic resources. |
| internal/k8s/client_test.go | Adds coverage for cluster-details caching behavior. |
| internal/tools/tools.go | Registers additional tools and introduces safeErr for logging + error results. |
| internal/tools/tools_extended.go | Implements the new Kubernetes tools (namespaces, workloads, CRDs, YAML fetch, etc.). |
| internal/tools/tools_test.go | Adds unit tests for argument parsing/validation helpers and error-result helpers. |
| internal/auth/session_test.go | Adds tests for session/pending/auth-code/client store behaviors. |
| internal/auth/ratelimit_test.go | Adds tests for per-IP rate limiting middleware. |
| internal/auth/oauth_test.go | Adds tests for metadata/register helpers and redirect URI logic. |
| internal/auth/middleware_test.go | Adds tests for auth middleware behavior and context session extraction. |
| .gitignore | Adds .mcp.json to ignored files. |
| .github/workflows/ci.yml | Adds CI job to build, vet, and run tests with race detector. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // safeErr logs the error server-side and returns it to the client. | ||
| func safeErr(clientMsg string, err error) (*mcp.CallToolResult, error) { | ||
| slog.Error(clientMsg, "error", err) | ||
| return mcp.NewToolResultError(fmt.Sprintf("%s: %v", clientMsg, err)), nil |
There was a problem hiding this comment.
safeErr logs the server-side error but also returns the full underlying error text to the client. This can leak sensitive operational details (cluster endpoints, internal URLs, etc.) in error strings. Consider returning only clientMsg (or a sanitized/typed subset) and keeping full details in logs, optionally behind a debug flag.
| // safeErr logs the error server-side and returns it to the client. | |
| func safeErr(clientMsg string, err error) (*mcp.CallToolResult, error) { | |
| slog.Error(clientMsg, "error", err) | |
| return mcp.NewToolResultError(fmt.Sprintf("%s: %v", clientMsg, err)), nil | |
| // safeErr logs the error server-side and returns a sanitized message to the client. | |
| func safeErr(clientMsg string, err error) (*mcp.CallToolResult, error) { | |
| slog.Error(clientMsg, "error", err) | |
| return mcp.NewToolResultError(clientMsg), nil |
|
|
||
| // Convert kind to lowercase plural (simple heuristic). | ||
| resource := strings.ToLower(kind) + "s" | ||
|
|
||
| gvr := schema.GroupVersionResource{Group: group, Version: version, Resource: resource} |
There was a problem hiding this comment.
get_resource_yaml converts Kind -> resource via strings.ToLower(kind)+"s", which will fail for many built-in kinds (e.g., Ingress -> ingresses) and for CRDs where pluralization is arbitrary. To reliably support "any Kubernetes resource", use discovery + a RESTMapper to resolve (apiVersion, kind) to the correct GVR, or change the API to accept the resource name explicitly.
| yamlBytes, err := sigsyaml.Marshal(obj.Object) | ||
| if err != nil { | ||
| return safeErr("failed to serialize resource", err) | ||
| } | ||
|
|
||
| return mcp.NewToolResultText(string(yamlBytes)), nil |
There was a problem hiding this comment.
get_resource_yaml returns the full YAML for any resource, which will include Secret data (and potentially other sensitive fields) if a user requests them. Consider explicitly blocking Secret/ServiceAccount/TokenRequest kinds or redacting sensitive fields (e.g., data, stringData) before returning YAML.
| sb.WriteString(fmt.Sprintf("%-45s %d/%-12d %-10d %-8d %s\n", | ||
| j.Namespace+"/"+j.Name, j.Status.Succeeded, desired, j.Status.Succeeded, j.Status.Failed, age)) |
There was a problem hiding this comment.
The jobs output prints COMPLETIONS as succeeded/desired and then prints SUCCEEDED as succeeded again, which makes the SUCCEEDED column redundant and may be misleading. Consider using active/succeeded/failed or making COMPLETIONS show desired completions only.
| sb.WriteString(fmt.Sprintf("%-45s %d/%-12d %-10d %-8d %s\n", | |
| j.Namespace+"/"+j.Name, j.Status.Succeeded, desired, j.Status.Succeeded, j.Status.Failed, age)) | |
| sb.WriteString(fmt.Sprintf("%-45s %-14d %-10d %-8d %s\n", | |
| j.Namespace+"/"+j.Name, desired, j.Status.Succeeded, j.Status.Failed, age)) |
| // contains is a small helper to check substring presence. | ||
| func contains(s, substr string) bool { | ||
| return len(s) >= len(substr) && searchSubstring(s, substr) | ||
| } | ||
|
|
||
| func searchSubstring(s, substr string) bool { | ||
| for i := 0; i <= len(s)-len(substr); i++ { | ||
| if s[i:i+len(substr)] == substr { | ||
| return true | ||
| } | ||
| } | ||
| return false | ||
| } |
There was a problem hiding this comment.
The test helpers contains/searchSubstring reimplement strings.Contains, which adds code surface area without improving test clarity. Consider using strings.Contains directly to keep tests simpler and more idiomatic.
Summary
Rearchitects the MCP server with OAuth 2.0 proxy, session management, 14 new K8s tools (20 total), security hardening, and renames to
k8scope.
POST /register,client_idvalidation at/authorizeand/tokenTRUST_PROXYflag for X-Forwarded-Proto, panic recovery middleware, rand.Read errorcheck, CORS
handlers, pagination limits on list operations
instances, get_resource_yaml
Test plan
go build ./...andgo vet ./...pass