0e2fc97aa8
The previous provisioner wrote ~/.claude/settings.json with an
mcpServers entry — but claude-code doesn't read its mcpServers from
that path. Inside a bottle, /mcp showed "No MCP servers configured"
even though the sidecar was running.
Switch to the official `claude mcp add` command run via docker exec:
docker exec -u node <agent> \
claude mcp add --scope user --transport http supervise <url>
claude-code owns its config file format (~/.claude.json shape, key
names, scope semantics) and has changed it between versions. The
official command writes to the right place in the right shape for
whatever version is installed.
Failure is logged but not fatal — the bottle still works; you just
have to register the server manually with the command surfaced in
the warning. Worst case is a bad agent claude-code version, not a
bad bottle.
To fix an already-running bottle without restarting, the user can
run the same `docker exec` command directly.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
66 lines
2.2 KiB
Python
66 lines
2.2 KiB
Python
"""Supervise sidecar provisioning inside a running Docker bottle
|
|
(PRD 0013).
|
|
|
|
Registers the per-bottle supervise sidecar as an HTTP MCP server in
|
|
the agent's claude-code config so the agent discovers the three
|
|
stuck-recovery MCP tools (cred-proxy-block, pipelock-block,
|
|
capability-block) at startup.
|
|
|
|
Uses `claude mcp add` rather than writing JSON directly. claude-code
|
|
owns the on-disk config format (`~/.claude.json` `mcpServers` shape,
|
|
field names, scope semantics) and changes it between versions; the
|
|
official command handles whatever the installed version expects.
|
|
|
|
No-op when bottle.supervise is False — bottles that haven't opted
|
|
into the supervise sidecar shouldn't get an MCP entry pointing at a
|
|
sidecar that isn't running.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import subprocess
|
|
|
|
from ....log import info, warn
|
|
from ....supervise import SUPERVISE_HOSTNAME, SUPERVISE_PORT
|
|
from ..bottle_plan import DockerBottlePlan
|
|
|
|
|
|
_SUPERVISE_MCP_NAME = "supervise"
|
|
|
|
|
|
def supervise_mcp_url() -> str:
|
|
return f"http://{SUPERVISE_HOSTNAME}:{SUPERVISE_PORT}/"
|
|
|
|
|
|
def provision_supervise(plan: DockerBottlePlan, target: str) -> None:
|
|
"""Run `claude mcp add` inside the agent container to register
|
|
the supervise sidecar in claude-code's user config. No-op when
|
|
bottle.supervise is False.
|
|
|
|
Failure is logged but not fatal: the bottle still works (you
|
|
just can't call supervise tools from the agent until the entry
|
|
is added manually). The operator sees the warning at launch."""
|
|
if plan.supervise_plan is None:
|
|
return
|
|
url = supervise_mcp_url()
|
|
argv = [
|
|
"docker", "exec", "-u", "node", target,
|
|
"claude", "mcp", "add",
|
|
"--scope", "user",
|
|
"--transport", "http",
|
|
_SUPERVISE_MCP_NAME,
|
|
url,
|
|
]
|
|
info(f"registering supervise MCP server in agent claude config → {url}")
|
|
r = subprocess.run(argv, capture_output=True, text=True, check=False)
|
|
if r.returncode != 0:
|
|
warn(
|
|
f"`claude mcp add supervise` failed (exit {r.returncode}): "
|
|
f"{(r.stderr or r.stdout or '').strip()}. Inside the bottle, "
|
|
f"register manually with: "
|
|
f"claude mcp add --scope user --transport http supervise {url}"
|
|
)
|
|
|
|
|
|
__all__ = ["provision_supervise", "supervise_mcp_url"]
|