microk8s-mcp View on GitHub

Troubleshooting

The errors people actually hit, what each one really means, and the fix. Start with make doctor — it checks most of the preconditions in one go and installs nothing.

Registration

Server shows disconnected in claude mcp list

Claude Code launches subprocesses with a different environment than your shell. The command must be an absolute path to the venv binary. make register always uses one; if you registered by hand, check it. Confirm the binary works on its own:

make check

The server does not appear at all

Two causes, both of which make a healthy registration look like a missing one.

Scope. Without SCOPE=user the entry is local — stored per-project, visible only when your working directory is inside that project. Nothing is wrong; you are just looking from the wrong place:

claude mcp get microk8s

Restart. Claude Code loads MCP servers at session start. A server registered during a running session will not appear in /mcp until you restart, whatever its scope.

Every tool appears twice

You have both a microk8s and a microk8s-rw entry registered. That is a supported setup, but if you would rather have one, re-register with RW_SERVER_NAME=microk8s to replace the read-only entry instead of adding to it.

Connecting to the cluster

connection refused on :16443

MicroK8s only binds the API server to addresses in its certificate SANs. If you are connecting by hostname or a non-primary IP, add it on the node and refresh the certificates:

# on the node
sudo nano /var/snap/microk8s/current/certs/csr.conf.template
microk8s refresh-certs --cert server.crt

x509: certificate is valid for ..., not <host>

The address in the kubeconfig is reachable, but it is not one the certificate covers. See which names are present:

ssh you@your-node 'openssl x509 -in \
  /var/snap/microk8s/current/certs/server.crt -noout -text' | grep -A3 "Alternative Name"

Then either re-mint against an address that is listed, or add yours to csr.conf.template and refresh as above.

'<host>' does not resolve on this machine

From make node-check. The API server address is not usable from here — almost always an ~/.ssh/config alias that ssh -G could not expand, or a name only the node knows. Pass the real one:

make node-setup SSH_HOST=<alias> APISERVER=https://<ip-or-dns>:16443

Nothing was minted; this check fires before any cluster contact.

cannot reach <host> over ssh

Check ssh you@your-node true works on its own first. If you need a key or a jump host:

make node-check SSH_HOST=you@your-node SSH_KEY=~/.ssh/id_ed25519
make node-check SSH_HOST=you@your-node SSH_OPTS='-J bastion -o ConnectTimeout=10'

The node-* targets do not force BatchMode, so password and passphrase prompts still work. The running server does force it — it never prompts — so key-based auth is required at runtime.

Secret mcp-system/claude-mcp-token not found

The RBAC step did not run, or ran against a different cluster:

make node-rbac       SSH_HOST=you@your-node
make node-kubeconfig SSH_HOST=you@your-node

Remote script failed; ... left untouched

Nothing was damaged — node-kubeconfig validates the minted kubeconfig before it replaces anything. Run make node-check SSH_HOST=... to find the missing precondition.

The node backend

microk8s: command not found over SSH

SSH command execution does not source your login profile, so /snap/bin is often missing from PATH. There are two separate settings, for two separate stages:

# at runtime, for the server
--env MICROK8S_MCP_MICROK8S=/snap/bin/microk8s

# during setup, for the make targets
make node-setup SSH_HOST=you@your-node KUBECTL='/snap/bin/microk8s kubectl'

No node backend available

A tool that needs the node backend — microk8s_status or manage_addon — was called with no way to reach it. Either run the server on the MicroK8s host, or set MICROK8S_MCP_SSH_HOST=user@host with key-based auth.

metrics-server errors from top

Enable the addon on the node, then give it a minute to scrape:

microk8s enable metrics-server

Install and upgrade

No module named microk8s_mcp

The venv is stale or the install did not complete:

make distclean install

bad interpreter after moving or renaming the repo

Virtualenvs are not relocatable. Scripts in <venv>/bin hardcode the absolute path of the interpreter that created them, and an editable install records the absolute path of the checkout. Renaming breaks both, and make install cannot repair it because pip itself is one of those scripts:

make distclean install

An out-of-repo copy install is immune to repo renames — that is the point of it. Moving the venv itself has the same problem, so reinstall rather than mv a venv.

Code changes have no effect

The classic copy-install trap: with EDITABLE=0 the venv holds its own copy, so a git pull changes nothing until you reinstall. Everything reports success and the old code keeps running. Ask the venv where it imports from — from outside the checkout, since python -c prepends the working directory to sys.path and would otherwise answer with the local source:

cd /tmp && ~/.local/share/microk8s-mcp/venv/bin/python -c \
  "import microk8s_mcp, inspect; print(inspect.getfile(microk8s_mcp))"

A path under site-packages is a copy install; a path inside the repo means it is editable after all.

Permission denied writing the kubeconfig

KUBECONFIG_OUT points somewhere you cannot write:

make kubeconfig APISERVER=... KUBECONFIG_OUT=/tmp/claude-mcp.kubeconfig

Unexpected behaviour

It refuses to change anything

Read-only is the default, and the refusal message says so. Register a read-write entry:

make register-rw SSH_HOST=you@your-node NAMESPACES=apps,staging

Then restart Claude Code. If it still refuses, work through these in order: the namespace is outside MICROK8S_MCP_NAMESPACES; or it is a protected namespace and MICROK8S_MCP_ALLOW_PROTECTED_WRITES is false; or the tool needs its own switch (ALLOW_ADDON_CHANGES, ALLOW_NODE_OPS); or the RBAC binding does not grant it — check with auth can-i.

It can do more than you expected

Almost always the kubectl fallback: with no local kubectl, commands run under the node's admin credential and your scoped kubeconfig stops applying. Check the logs for falling back to `microk8s kubectl`, and set MICROK8S_MCP_KUBECTL to an absolute path.

Then confirm what the credential can really do:

kubectl --kubeconfig ~/.kube/claude-mcp.kubeconfig auth can-i --list

Output is truncated

Anything over MICROK8S_MCP_MAX_OUTPUT characters (24000 by default) is clipped in the middle, with a note saying how much was dropped. Narrow the query — a label selector, a single namespace, fewer tail_lines — rather than raising the limit, since the output has to fit in a context window either way.

Commands time out

MICROK8S_MCP_TIMEOUT is 60 seconds per command. Addon operations and node drains raise it internally, but a slow cluster or a slow SSH hop can still hit it. Raise it if the timeout is legitimate rather than a symptom.

Still stuck? Open an issue on GitHub with the startup log line — it records the mode, kubeconfig, backend and namespace allowlist the server is actually running with.