Files
bot-bottle/tests/unit/test_provision_supervise.py
T
didericis 6e46ca4478
test / unit (pull_request) Successful in 17s
test / integration (pull_request) Successful in 1m30s
feat(supervise): provision agent-side MCP config so Claude sees the sidecar
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>
2026-05-25 06:22:25 -04:00

39 lines
1.3 KiB
Python

"""Unit: supervise MCP settings renderer (PRD 0013 follow-up).
The docker cp / chown side of provision_supervise is exercised by
the existing supervise integration test once the agent container is
brought up; here we cover the pure render path so a settings.json
shape regression would surface in unit-level CI."""
import json
import unittest
from claude_bottle.backend.docker.provision.supervise import render_settings
from claude_bottle.supervise import SUPERVISE_HOSTNAME, SUPERVISE_PORT
class TestRenderSettings(unittest.TestCase):
def test_output_is_valid_json(self):
json.loads(render_settings())
def test_has_mcp_servers_supervise_http_entry(self):
cfg = json.loads(render_settings())
servers = cfg["mcpServers"]
self.assertIn("supervise", servers)
sv = servers["supervise"]
self.assertEqual("http", sv["type"])
self.assertEqual(
f"http://{SUPERVISE_HOSTNAME}:{SUPERVISE_PORT}/",
sv["url"],
)
def test_only_supervise_server_is_emitted(self):
cfg = json.loads(render_settings())
# Keep the provisioner narrowly scoped — it owns just the
# supervise entry, no other tools/servers.
self.assertEqual({"supervise"}, set(cfg["mcpServers"].keys()))
if __name__ == "__main__":
unittest.main()