Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .github/hooks/devcontainer-guard.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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, ... }
Expand Down Expand Up @@ -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')

Expand Down
7 changes: 7 additions & 0 deletions .github/hooks/devcontainer-skill-loader.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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, ... }
Expand Down Expand Up @@ -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"
Expand Down
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down