A Go-native take on Cloudflare's Code Mode, specialized for Google Cloud. An MCP server (and CLI) that ships a Go program into a sandboxed GKE pod, compiles and runs it against the real cloud.google.com/go/... SDKs with forwarded credentials, and returns a discriminated structured result.
The wedge: in Go, the LLM's "tool surface" already exists as importable packages — cloud.google.com/go/storage, cloud.google.com/go/compute/apiv1, BigQuery, GKE, Secret Manager — and the model has seen plenty of real code that uses them. So instead of generating typed stubs from MCP tool schemas (Cloudflare's TS approach), we let the model write a normal Go program and execute it. One agent step → many GCP API calls → one structured result back.
Pre-alpha. Shipped through Slice 4 of docs/plan.md:
- CLI (
kode-gopher {exec,serve,auth status}).exec <file.go>andserveaccept a snippet declaringfunc run(ctx context.Context) (any, error)(wrapped mode) or a fullpackage mainprogram (verbatim). Wrapper captures error / panic / json-marshal failure into a discriminatedresult.json.auth statusreports the ambient identity kode-gopher will forward.--contextflag on bothexecandservepins a kubeconfig context instead of inheriting ambientkubectl config current-context. - MCP server (
kode-gopher serve) over stdio usinggithub.com/modelcontextprotocol/go-sdk. Three tools:execute_go_code(code | files, extra_imports?)— build+run Go in the sandbox.codeis single-file;filesismap[path -> source]for multi-file snippets with helper subpackages.gcp_auth_status()— report the sandbox's credential identity (mode, credential type, email, project).lookup_package_docs(package, symbol?)—go docfor curated packages against the prewarmed cache, subsecond. One long-livedsandbox.Sessionper server process; mutex-serialized tool calls;/appreset between calls (caches survive); retry-once onErrSessionDeadso a pod eviction mid-call self-heals.
- Sandbox backend: own thin wrapper at
internal/sandbox/oversigs.k8s.io/agent-sandbox/clients/go/sandbox. Prewarmed image (ghcr.io/gke-demos/kode-gopher-sandbox:latest) bakes GCP SDK +k8s.io/client-gointo$GOCACHE/$GOMODCACHE; the tidied prewarmgo.modis preserved at/opt/kode-gopher-base/and the executor bootstraps each snippet's/app/go.modfrom it so version selection is reproducible and builds cache-hit.PerAttemptTimeoutis 3 minutes (was implicit 60 s from the upstream default). - Credentials unified as
creds.Source— Materialize forwards ADC + env into the sandbox; Identity parses ADC and calls the OAuth2 userinfo endpoint forauthorized_usercreds (cached).Workloadimplementation is stubbed until we run in-cluster. - Substrates: verified end-to-end on local
kindand on a real GKE Autopilot cluster with the agent-sandbox addon + gVisor isolation. Both the direct-CLI and the MCP paths diff cleanly againstgcloud storage buckets list; the MCP smoketest also exercises the k8s and multi-file paths. - Generated LLM prompt:
make promptsregeneratesinternal/prompts/{system.md,description.go}frominternal/curated.Packages, keeping the LLM system prompt and theexecute_go_codetool description in lockstep with the curated set.
Planned slices in docs/plan.md:
- Slice 3 — full GKE deployment story (formalize Artifact Registry push, Workload Identity binding docs; largely done opportunistically).
- Slice 5 — HTTP/SSE transport. Scope gated on five explicit design questions (session topology, auth, per-end-user creds, streaming, deployment topology).
- Slice 6 — alternative Yaegi (interpreter) runtime as opt-in second backend. PoC in
experiments/yaegi-poc/shows ~700ms end-to-end for a real GCS list vs ~5-30s through the compiled path; full slice gated on testingcloud.google.com/go/compute/apiv1(true gRPC) under Yaegi.
Prereqs: kind, kubectl, docker, gcloud, go, python3, plus a GCP project the executing user can list buckets in.
gcloud auth application-default login
export GOOGLE_CLOUD_PROJECT=your-project
# Bootstrap local kind cluster + agent-sandbox + sandbox-router +
# kode-gopher-sandbox image + SandboxTemplate, then exercise both
# verbatim and wrapped test snippets via the direct CLI, diffing each
# against `gcloud storage buckets list`.
./scripts/smoketest-kind.sh --compare
# Same exercise via the MCP layer: spawn `kode-gopher serve` and
# speak MCP over its stdio (no LLM required — the smoketest binary is
# itself an MCP client).
./scripts/smoketest-mcp.sh --target=kind --compareFor GKE (assumes a cluster with the agent-sandbox addon enabled + an ap-gke-sandbox context):
./scripts/smoketest-gke.sh --compare
./scripts/smoketest-mcp.sh --target=gke --compareOnce kode-gopher serve works locally, point any MCP client (Claude Desktop, Gemini CLI, custom) at it. Sample config snippet for Claude Desktop:
{
"mcpServers": {
"kode-gopher": {
"command": "/path/to/bin/kode-gopher",
"args": ["serve", "--namespace=codemode"]
}
}
}All smoketests are idempotent and reuse infra across runs.
| path | what |
|---|---|
docs/design.md |
architecture, transport + runtime modes, auth, result protocol, sandbox boundary |
docs/plan.md |
slice-by-slice build sequence (0-6; 0-4 shipped, 5-6 planned) |
docs/decisions.md |
append-only log of judgment calls per slice |
cmd/kode-gopher |
the CLI binary — subcommands exec and serve |
cmd/mcp-smoketest |
programmatic MCP client; spawns kode-gopher serve and exercises execute_go_code end-to-end |
internal/mcp |
MCP server + tool handlers (execute_go_code, gcp_auth_status, lookup_package_docs) |
internal/executor |
Build/Run/Fetch phases over a sandbox.Session; bootstraps /app/go.mod from the prewarm lockfile |
internal/sandbox |
our thin client over sigs.k8s.io/agent-sandbox; KubeContext + PerAttemptTimeout options; typed ErrSessionDead for retry-once at MCP layer |
internal/creds |
unified Source interface: Materialize (files+env for the executor) + Identity (mode/type/email/project for gcp_auth_status), Forwarded + Workload impls |
internal/normalize |
multi-file input; root vs subdirectory partitioning; same-package rewrite; optional extra_imports companion file |
internal/wrapper |
the generated func main() shipped alongside snippets |
internal/curated |
canonical list of GCP + k8s.io/client-go packages prewarmed in the sandbox image; go:generate drives prompts regen |
internal/prewarm |
standalone Go module imported at image build to populate $GOCACHE; committed go.mod+go.sum pin versions |
internal/prompts |
generated system.md (LLM system prompt) + description.go (execute_go_code tool description); regenerate via make prompts |
sandbox/Dockerfile |
extends ghcr.io/gke-demos/go-runtime-sandbox:latest with the prewarmed cache |
manifests/base |
SandboxTemplate kustomize base (kind-compatible) |
manifests/overlays/gke |
GKE Autopilot overlay: gVisor + securityContext + Workload Identity + SandboxWarmPool + per-namespace sandbox-router |
scripts/smoketest-kind.sh |
local-kind direct-CLI verification |
scripts/smoketest-gke.sh |
GKE Autopilot direct-CLI verification |
scripts/smoketest-mcp.sh |
MCP-layer verification against either substrate |
experiments/yaegi-poc |
Slice 6 proof of concept — Yaegi interpreter as an alternative runtime |
- kubernetes-sigs/agent-sandbox — SandboxClaim / SandboxTemplate / SandboxWarmPool CRDs, in-pod runtime, controller, and Go client (
sigs.k8s.io/agent-sandbox/clients/go/sandbox). - gke-demos/go-runtime-sandbox — the published sandbox image (
ghcr.io/gke-demos/go-runtime-sandbox:latest) we extend as our base insandbox/Dockerfile. - modelcontextprotocol/go-sdk — MCP server + client SDK.
- traefik/yaegi — only inside
experiments/yaegi-poc/(standalone module, not pulled into the main build) as the interpreter behind the Slice 6 PoC.