diff --git a/.github/hooks/devcontainer-guard.sh b/.github/hooks/devcontainer-guard.sh index bd400af..40a876f 100755 --- a/.github/hooks/devcontainer-guard.sh +++ b/.github/hooks/devcontainer-guard.sh @@ -11,7 +11,12 @@ # Host-safe commands (git, gh, curl, etc.) are allowlisted and always permitted # since they operate on the repo/host, not the project's build environment. # -# Bypass: include USER_CONFIRMED_HOST_OPERATION=1 in the command. +# Per-repo opt-out: create a `.devcontainer-mcp-disable` file at the repo root +# (next to the working directory) to turn this guard off entirely for that repo. +# Useful when a project ships a stale/unmaintained `.devcontainer` that you don't +# actually want to route work through. +# +# Bypass (single command): include USER_CONFIRMED_HOST_OPERATION=1 in the command. # # Supports both agent payload formats: # Claude Code: { tool_name, tool_input, cwd, ... } @@ -88,6 +93,12 @@ decide() { # No devcontainer in the working directory — allow through [ -f "${cwd}/.devcontainer/devcontainer.json" ] || return 0 + # Per-repo opt-out: a marker file at the repo root disables the guard so a + # stale/unmaintained devcontainer doesn't force everything through the MCP tools. + if [ -f "${cwd}/.devcontainer-mcp-disable" ]; then + return 0 + fi + # --- Devcontainer exists: allow only if every command is host-safe --- cmd_string=$(printf '%s' "$input" | jq -r '(.tool_input.command // .toolArgs.command // "") | tostring') diff --git a/.github/hooks/devcontainer-skill-loader.sh b/.github/hooks/devcontainer-skill-loader.sh index 187f0a3..2d8b3ed 100755 --- a/.github/hooks/devcontainer-skill-loader.sh +++ b/.github/hooks/devcontainer-skill-loader.sh @@ -5,6 +5,9 @@ # injects the devcontainer-mcp SKILL.md content as additionalContext so the # agent automatically knows how to use devcontainer-mcp tools. # +# Opt-out: a `.devcontainer-mcp-disable` marker file at the repo root skips the +# injection (mirrors the devcontainer-guard opt-out). +# # Supports both agent payload formats: # Claude Code: { tool_name, tool_input, cwd, ... } # Copilot CLI: { toolName, toolArgs, cwd, ... } @@ -38,6 +41,10 @@ build_context() { [ -n "$cwd" ] || return 0 [ -f "${cwd}/.devcontainer/devcontainer.json" ] || return 0 + # Per-repo opt-out: if the guard is disabled for this repo, don't inject the + # container-only skill context either — the agent is expected to work locally. + [ ! -f "${cwd}/.devcontainer-mcp-disable" ] || return 0 + # Look for SKILL.md in order of preference SEARCH_PATHS=( "${HOME}/.local/share/devcontainer-mcp/SKILL.md" diff --git a/README.md b/README.md index e920864..5179151 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,25 @@ Backend CLIs (`devpod`, `devcontainer`, `gh`) are detected at runtime — if one Binaries available for **linux-x64**, **linux-arm64**, **darwin-x64**, and **darwin-arm64**. +## Host Protection & Opt-Out + +The installer configures two agent hooks (Claude Code & GitHub Copilot CLI) that activate **only** in a directory containing `.devcontainer/devcontainer.json`: + +- **`devcontainer-guard`** (PreToolUse) — blocks shell/command execution on the host so the agent routes builds, tests, and runs through the MCP tools instead. Host-safe commands (`git`, `gh`) are allowlisted, and both hooks *fail open* — if `jq` is missing or a payload can't be parsed, commands are allowed rather than blocked. +- **`devcontainer-skill-loader`** (SessionStart) — injects the `SKILL.md` usage guide as context. + +### Turning it off for a repo + +Sometimes a project ships a stale or unmaintained `.devcontainer` that you don't actually want to route work through. To disable both hooks for that repo, drop a marker file at the repo root: + +```bash +touch .devcontainer-mcp-disable +``` + +When `.devcontainer-mcp-disable` is present, the guard allows host commands through and the skill-loader skips context injection — the agent works locally as if no devcontainer were declared. Commit it (or add it to `.gitignore` for a local-only opt-out). + +**One-off bypass:** to run a single host command without disabling the guard, include `USER_CONFIRMED_HOST_OPERATION=1` anywhere in the command. + ## Architecture ```mermaid diff --git a/SKILL.md b/SKILL.md index 9e09880..19221ac 100644 --- a/SKILL.md +++ b/SKILL.md @@ -60,6 +60,8 @@ You have access to `devcontainer-mcp`, an MCP server that manages dev container **If a project has `.devcontainer/devcontainer.json`, ALL work MUST happen inside a dev container — never install dependencies, run builds, or execute code directly on the host.** +> **Opt-out:** if a `.devcontainer-mcp-disable` marker file exists at the repo root, the host-protection guard is disabled for that repo — the devcontainer is treated as stale/unmaintained and you may work directly on the host. + **Use ONLY the MCP tools listed here.** Do not invoke `docker`, `devcontainer`, `devpod`, or `gh` CLI commands directly — the MCP tools wrap these CLIs with proper error handling, auth resolution, and escaping. Direct CLI usage bypasses these safeguards. ## Authentication