6e46ca4478
The supervise sidecar (PRD 0013) has been serving MCP at http://supervise:9100/ since it landed, but the in-bottle Claude Code had no `.mcp.json` or settings pointing there — so the agent couldn't actually call cred-proxy-block / pipelock-block / capability-block as tools. To exercise the flow you had to curl the sidecar from a sibling container. This closes that last mile. - claude_bottle/backend/docker/provision/supervise.py (new): provision_supervise(plan, target) writes ~/.claude/settings.json into the running agent container with an mcpServers.supervise entry of type http pointing at the per-bottle sidecar. No-op when bottle.supervise is False. - BottleBackend.provision orchestrator gains provision_supervise as the last step (after CA, prompt, skills, git, cred-proxy). Default impl is a no-op so non-Docker backends aren't forced to implement it. - DockerBottleBackend wires it through to the new module. - Test covers the rendered settings shape so a future regression in the MCP entry format would surface in unit-level CI. To test the full flow end-to-end now: ./cli.py start <agent> --cwd # agent's claude sees supervise # agent calls cred-proxy-block via MCP ./cli.py dashboard # approve ./cli.py resume <identity> # restart with new capabilities Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
76 lines
2.6 KiB
Python
76 lines
2.6 KiB
Python
"""Supervise sidecar provisioning inside a running Docker bottle
|
|
(PRD 0013).
|
|
|
|
Writes ~/.claude/settings.json with an `mcpServers.supervise` entry
|
|
pointing at the per-bottle supervise sidecar so the in-bottle Claude
|
|
Code discovers the three stuck-recovery MCP tools (cred-proxy-block,
|
|
pipelock-block, capability-block) at startup.
|
|
|
|
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 json
|
|
import os
|
|
import subprocess
|
|
|
|
from ....log import info
|
|
from ....supervise import SUPERVISE_HOSTNAME, SUPERVISE_PORT
|
|
from .. import util as docker_mod
|
|
from ..bottle_plan import DockerBottlePlan
|
|
|
|
|
|
_AGENT_HOME_DEFAULT = "/home/node"
|
|
_SETTINGS_REL_PATH = ".claude/settings.json"
|
|
_SUPERVISE_MCP_NAME = "supervise"
|
|
|
|
|
|
def render_settings() -> str:
|
|
"""The settings.json content the agent reads on startup. Stable
|
|
shape — only the URL is parameterized in case CLAUDE_BOTTLE_*
|
|
env overrides change the supervise hostname/port someday."""
|
|
cfg = {
|
|
"mcpServers": {
|
|
_SUPERVISE_MCP_NAME: {
|
|
"type": "http",
|
|
"url": f"http://{SUPERVISE_HOSTNAME}:{SUPERVISE_PORT}/",
|
|
},
|
|
},
|
|
}
|
|
return json.dumps(cfg, indent=2) + "\n"
|
|
|
|
|
|
def provision_supervise(plan: DockerBottlePlan, target: str) -> None:
|
|
"""Drop ~/.claude/settings.json into the running agent container
|
|
when bottle.supervise is True. No-op otherwise."""
|
|
if plan.supervise_plan is None:
|
|
return
|
|
|
|
container_home = os.environ.get(
|
|
"CLAUDE_BOTTLE_CONTAINER_HOME", _AGENT_HOME_DEFAULT,
|
|
)
|
|
settings_in_container = f"{container_home}/{_SETTINGS_REL_PATH}"
|
|
settings_dir_in_container = settings_in_container.rsplit("/", 1)[0]
|
|
|
|
host_path = plan.stage_dir / "agent_claude_settings.json"
|
|
host_path.write_text(render_settings())
|
|
host_path.chmod(0o644)
|
|
|
|
info(f"writing {settings_in_container} (supervise MCP server entry)")
|
|
# The Dockerfile creates ~/.claude.json at the top of HOME but
|
|
# not the ~/.claude/ subdir, so make sure it exists before cp.
|
|
docker_mod.docker_exec_root(target, ["mkdir", "-p", settings_dir_in_container])
|
|
subprocess.run(
|
|
["docker", "cp", str(host_path), f"{target}:{settings_in_container}"],
|
|
stdout=subprocess.DEVNULL,
|
|
check=True,
|
|
)
|
|
docker_mod.docker_exec_root(target, ["chown", "-R", "node:node", settings_dir_in_container])
|
|
docker_mod.docker_exec_root(target, ["chmod", "644", settings_in_container])
|
|
|
|
|
|
__all__ = ["provision_supervise", "render_settings"]
|