DevContainer Feature that enables AI agents to execute CLI commands (e.g., gh) on the host machine via MCP.
DevContainer Host Machine (macOS)
+------------------+ +------------------+
| AI Agent | | cmd2host daemon |
| ↓ | TCP:9876 | ↓ |
| cmd2host-mcp | ----or----> | cmd2host (Go) |
| (MCP server) | Unix socket | ↓ |
| ↓ | <---------- | gh (real CLI) |
| Operations API | | |
+------------------+ +------------------+
Connection modes:
- TCP (default): Uses
host.docker.internal:9876- works with most DevContainers - Unix socket: Uses mounted socket file - required for
--network nonecontainers
Note: Wrapper scripts (e.g., gh) are installed but display MCP usage instructions instead of executing commands directly.
curl -fsSL https://raw.githubusercontent.com/taisukeoe/cmd2host/main/host/scripts/install.sh | bashCreate a project config using a template:
# List available templates
cmd2host templates
# Create config from template
cmd2host config init --repo=owner/repo --template=readonly --repo-path=/path/to/repo
# Or create and allow in one step
cmd2host config init --repo=owner/repo --template=github_write --allow
# Or create a combined git + GitHub write config
cmd2host config init --repo=owner/repo --template=git_github_write --allowAvailable templates:
readonly- Read-only operations (git fetch, gh pr/issue view/list, review comments)github_write- + PR/Issue creation and PR comment/reply operationsgit_write- + git push (with strict constraints)git_github_write- + git push, PR creation, and PR comment/reply operations
Or create manually at ~/.cmd2host/projects/<owner_repo>/config.json (see Templates section below).
Note: config init resolves operation commands like gh and git to absolute host paths when available. This avoids daemon launch environments with a narrower PATH from failing to find Homebrew-installed CLIs.
cmd2host config allow owner_repo{
"initializeCommand": ".devcontainer/init-cmd2host.sh",
"mounts": [
"source=${localWorkspaceFolder}/.devcontainer/.session/token,target=/run/cmd2host-token,type=bind,readonly"
],
"features": {
"ghcr.io/taisukeoe/cmd2host/cmd2host:1": {
"commands": "gh"
}
}
}Copy host/scripts/init-cmd2host.sh to your project's .devcontainer/ directory.
Add MCP server configuration to your .devcontainer/devcontainer.json:
{
"customizations": {
"claude-code": {
"mcpServers": {
"cmd2host": {
"command": "cmd2host-mcp",
"args": ["-token-file", "/run/cmd2host-token"]
}
}
}
}
}Or copy src/cmd2host/mcp.json to .devcontainer/mcp.json for manual MCP client configuration.
.devcontainer/.session/
curl -fsSL https://raw.githubusercontent.com/taisukeoe/cmd2host/main/host/scripts/install.sh | bash# Check if daemon is running
lsof -i :9876
# View logs
tail -f ~/.cmd2host/cmd2host.logcurl -fsSL https://raw.githubusercontent.com/taisukeoe/cmd2host/main/host/scripts/uninstall.sh | bashcmd2host # Start daemon
cmd2host config diff <project-id> # Show config status and hash
cmd2host config allow <project-id> # Allow current config
cmd2host projects # List all configured projects
cmd2host --hash-token # Hash a token from stdin
cmd2host --version # Show version| Option | Type | Default | Description |
|---|---|---|---|
commands |
string | gh |
Comma-separated list of commands to proxy |
installMcpServer |
boolean | true |
Install cmd2host-mcp (MCP server for AI agent integration) |
connectionMode |
string | tcp |
Connection mode: tcp (default) or unix (for --network none containers) |
{
"features": {
"ghcr.io/taisukeoe/cmd2host/cmd2host:1": {
"commands": "gh,docker"
}
}
}{
"features": {
"ghcr.io/taisukeoe/cmd2host/cmd2host:1": {
"commands": "gh",
"installMcpServer": false
}
}
}For containers with --network none, use Unix socket instead of TCP:
{
"initializeCommand": ".devcontainer/init-cmd2host.sh",
"mounts": [
"source=${localWorkspaceFolder}/.devcontainer/.session/token,target=/run/cmd2host-token,type=bind,readonly",
"source=${localEnv:HOME}/.cmd2host/cmd2host.sock,target=/var/run/cmd2host.sock,type=bind"
],
"features": {
"ghcr.io/taisukeoe/cmd2host/cmd2host:1": {
"commands": "gh",
"connectionMode": "unix"
}
},
"customizations": {
"claude-code": {
"mcpServers": {
"cmd2host": {
"command": "cmd2host-mcp",
"args": ["-socket", "/var/run/cmd2host.sock", "-token-file", "/run/cmd2host-token"]
}
}
}
}
}Or copy src/cmd2host/mcp-unix.json to .devcontainer/mcp.json for manual MCP client configuration.
cmd2host uses session tokens to authenticate requests from containers:
- 256-bit tokens: Cryptographically secure, generated per DevContainer session
- 24-hour TTL: Tokens expire automatically after 24 hours
- BLAKE3 hashing: Tokens are hashed before storage (prevents leakage if token store is accessed)
- Brute-force protection: 1-second delay on authentication failure
Token flow:
initializeCommandgenerates a random token on the host- Token hash is stored in
~/.cmd2host/tokens/with repo binding - Raw token is mounted into container at
/run/cmd2host-token - Container reads token from file and includes it in requests
Each project has its own configuration with:
- allowed_operations: Whitelist of permitted operations (default deny)
- constraints: Branch patterns, path restrictions
- operations: Predefined command templates with typed parameters
Configuration changes require explicit allowance:
# View current config status
cmd2host config diff owner_repo
# Allow changes
cmd2host config allow owner_repoIf config is modified after being allowed, operations are denied until re-allowed.
Only operations listed in allowed_operations can be executed. All other operations are denied.
~/.cmd2host/
├── daemon.json # Daemon settings (port, limits)
└── projects/
├── owner_repo/
│ ├── config.json # Project configuration
│ └── allowed.sha256 # Allowed config hash
└── another_owner_another_repo/
└── config.json
{
"repo": "owner/repo",
"repo_path": "/absolute/path/to/repo",
"allowed_operations": ["op1", "op2"],
"constraints": {
"branch_allow": ["^pattern1", "^pattern2"],
"path_deny": ["glob1", "glob2"],
"remote_hosts_allow": ["github.com"]
},
"env": {
"CUSTOM_VAR": "value"
},
"git_config": {
"user.name": "AI Agent",
"user.email": "ai@example.com"
},
"operations": {
"operation_id": {
"command": "gh",
"args_template": ["arg1", "{param1}", "{repo}"],
"params": {
"param1": {"type": "string"}
},
"allowed_flags": ["--flag1", "--flag2"],
"description": "Operation description"
}
}
}Templates are embedded in the cmd2host binary. Use CLI commands to list and view them:
cmd2host templates # List available templates
cmd2host templates show <name> # Show template content| Template | Description | Operations |
|---|---|---|
readonly |
Read-only access | git_fetch, gh_pr_view, gh_pr_list, gh_pr_review_comments, gh_issue_view, gh_issue_list |
github_write |
+ GitHub write | readonly + gh_pr_create, gh_pr_comment, gh_pr_review_comment_reply, gh_issue_create |
git_write |
+ Git push | readonly + git_push (requires branch_allow constraint) |
git_github_write |
Git + GitHub write | git_write + gh_pr_create, gh_pr_comment, gh_pr_review_comment_reply |
The MCP server (cmd2host-mcp) enables AI agents (like Claude Code) to interact with the cmd2host daemon using predefined operation templates.
- Type-safe operations: Predefined command templates with typed parameters
- Project-based policies: Fine-grained control over what operations are allowed
- Repository binding (token → repo → project config)
- Branch allowlist (regex patterns)
- Path denylist (glob patterns)
- Git config overrides
- AI-friendly: Provides structured operations instead of raw shell access
cmd2host_list_operations- List available operations (optionalprefixfilter, e.g.,"gh_pr")cmd2host_describe_operation- Get detailed schema for a specific operationcmd2host_run_operation- Execute an operation with typed parameters
gh_pr_view- View a pull request by numbergh_pr_list- List pull requests with filtersgh_pr_review_comments- List inline pull request review commentsgh_pr_comment- Add a pull request summary commentgh_pr_review_comment_reply- Reply to an inline pull request review commentgh_issue_create- Create a new issuegit_fetch- Fetch from remotegit_push- Push to remote (requires branch_allow constraint)
The MCP server binary (cmd2host-mcp) is automatically installed in the DevContainer when the feature is enabled. It connects to the same cmd2host daemon on the host.
MCP server requests use token authentication. Operations are validated against:
- Token → repo binding
- Project config allowance status (hash verification)
- Allowed operations list (default deny)
- Parameter type checking and validation
- Constraint checks (branch patterns, path globs)
Set automatically by the feature:
| Variable | Default | Description |
|---|---|---|
HOST_CMD_PROXY_HOST |
host.docker.internal |
Host address (TCP mode) |
HOST_CMD_PROXY_PORT |
9876 |
Daemon port (TCP mode) |
HOST_CMD_PROXY_SOCKET |
/var/run/cmd2host.sock |
Unix socket path (Unix mode) |
HOST_CMD_PROXY_TOKEN_FILE |
/run/cmd2host-token |
Path to session token file |
Requires just command runner.
just # Show available commands
# Building
just build # Build daemon for current platform
just build-mcp # Build MCP server for current platform
just build-all # Build all release binaries
just build-mcp-linux-amd64 # Build MCP server for Linux (containers)
# Testing
just test # Run unit tests (daemon)
just test-mcp # Run unit tests (MCP server)
just test-host # Run host scenario tests
just test-devcontainer # Run devcontainer feature test
just test-e2e # Run E2E tests (daemon + devcontainer + MCP)
just test-e2e-clean # Run E2E tests with clean install
just test-e2e-quick # Run E2E tests without rebuilding
just test-all # Run all tests (unit + host scenario)Version lines are intentionally separate:
cmd2hostbinary andcmd2host-mcpuse their own semver track- The DevContainer feature follows the feature ecosystem convention and stays on
1.x+
Use separate tags when releasing:
# Binary + MCP release
git tag binary-v0.1.3
git push origin binary-v0.1.3
# DevContainer feature publish
git tag devcontainer-feature-v1.2.2
git push origin devcontainer-feature-v1.2.2Apache 2.0 - See LICENSE