README
¶
HostMCP
Controlled Host OS Access for AI Agents via MCP
HostMCP is an MCP server that runs on the host OS, giving AI assistants (Claude Code, Gemini Code Assist, etc.) inside an AI Sandbox controlled access to the host environment — Docker containers, host tools, and host OS commands — through configurable security policies.
For the AI Sandbox template that uses HostMCP, see ai-sandbox.
Table of Contents
- Features
- Installation
- Server Startup
- Connecting AI Assistants
- CLI Commands
- Security Modes
- Authentication
- Configuration Reference
- Architecture
- Design Philosophy
- Provided MCP Tools
- Troubleshooting
- License
- Acknowledgments
Features
- 🐳 Docker container access — Logs, exec, inspect, stats, lifecycle (start/stop/restart)
- 🔧 Host tool execution — Run approved scripts in
.sandbox/host-tools/with a human approval workflow - 💻 Host OS commands — Execute whitelisted CLI commands on the host OS
- 🔒 Security-first design — Whitelist-based access control, output masking, blocked paths
- 🤖 Multi-AI support — Works with Claude Code, Gemini Code Assist
- 🚀 Zero dependencies — Single binary, no runtime requirements
- 🌐 Cross-platform — Windows, macOS (Intel & Apple Silicon), Linux
- 📝 Audit logging — All operations can be logged for compliance
Installation
Run on the host OS.
Prerequisites: Go 1.25 or later (only needed for go install / building from source), and Docker Engine (Docker Desktop or OrbStack) running on the host.
Go Install (Recommended)
go install github.com/YujiSuzuki/hostmcp@latest
Prebuilt Binary (no Go required)
macOS (Apple Silicon)
curl -L https://github.com/YujiSuzuki/hostmcp/releases/latest/download/hostmcp_darwin_arm64 -o hostmcp
chmod +x hostmcp
sudo mv hostmcp /usr/local/bin/
macOS (Intel)
curl -L https://github.com/YujiSuzuki/hostmcp/releases/latest/download/hostmcp_darwin_amd64 -o hostmcp
chmod +x hostmcp
sudo mv hostmcp /usr/local/bin/
Windows
- Download
hostmcp_windows_amd64.exefrom Releases - Place in a folder (e.g.,
C:\tools\) - Add to PATH or use the full path
Linux
curl -L https://github.com/YujiSuzuki/hostmcp/releases/latest/download/hostmcp_linux_amd64 -o hostmcp
chmod +x hostmcp
sudo mv hostmcp /usr/local/bin/
Build from Source
git clone https://github.com/YujiSuzuki/hostmcp.git
cd hostmcp
make install # Installs to ~/go/bin/
Server Startup
Preparing the Configuration File
Use hostmcp init to generate a config file inside your workspace:
hostmcp init --workspace /path/to/workspace
This creates .sandbox/config/hostmcp.yaml in the workspace directory. To set a custom port at the same time:
hostmcp init --workspace /path/to/workspace --port 18080
| Flag | Description |
|---|---|
--workspace |
Target workspace directory (required) |
--port |
Port number to set in the generated config (default: 18080) |
--force |
Overwrite an existing config file |
Alternatively, copy the example config manually:
cp configs/hostmcp.example.yaml hostmcp.yaml
nano hostmcp.yaml
Example configuration:
server:
port: 18080
host: "127.0.0.1"
security:
mode: "moderate" # strict, moderate, or permissive
allowed_containers:
- "myapp-*"
- "mydb-*"
exec_whitelist:
"myapp-api":
- "npm test"
- "pytest /app/tests"
"*":
- "pwd"
Starting the Server
# Run on host OS
hostmcp serve --config .sandbox/config/hostmcp.yaml
Alternative: If you copied the example config manually instead of using
hostmcp init, point--configat that file instead, e.g.hostmcp serve --config hostmcp.yaml.
Sponsor message:
hostmcp serveprints a short sponsor message at startup. Pass--no-thanksto suppress it.
Output looks like:
2026-01-22 12:55:17 INFO Starting HostMCP server version=dev security_mode=moderate port=18080 log_level=info
2026-01-22 12:55:17 INFO Found accessible containers count=3
2026-01-22 12:55:17 INFO MCP server listening url=http://127.0.0.1:18080 health_check=http://127.0.0.1:18080/health sse_endpoint=http://127.0.0.1:18080/sse
2026-01-22 12:55:17 INFO Press Ctrl+C to stop
Verbosity Levels
Use the -v flag to increase log verbosity for debugging:
hostmcp serve --config hostmcp.yaml -v # Level 1: JSON request/response output
hostmcp serve --config hostmcp.yaml -vv # Level 2: DEBUG level + JSON output
hostmcp serve --config hostmcp.yaml -vvv # Level 3: Full debug (all noise shown)
hostmcp serve --config hostmcp.yaml -vvvv # Level 4: Full debug + HTTP headers
| Level | Flag | Description |
|---|---|---|
| 0 | (none) | Normal INFO level, minimal output |
| 1 | -v |
JSON request/response display, uninitialized connections filtered |
| 2 | -vv |
DEBUG level + JSON output, uninitialized connections filtered |
| 3 | -vvv |
Full debug, all connections shown including noise |
| 4 | -vvvv |
Full debug plus raw HTTP headers |
Note: "Noise" refers to uninitialized SSE connections (e.g., VS Code extension probes). Levels 0-2 filter these to keep logs clean.
Logging Features
Request numbers: Each request is assigned a unique number [#N] for tracking when multiple requests are processed concurrently:
═══ [#1] ═══════════════════════════════════════════════════════════
▼ REQUEST client=claude-code method=tools/call tool=list_containers id=2
...
═══ [#1] ═══════════════════════════════════════════════════════════
Client identification: The server displays client names (from MCP clientInfo) in logs:
claude-code— Claude Code extensionhostmcp-go-client— HostMCP CLI client (with--client-suffixfor custom suffixes)
Graceful shutdown: When the server stops (Ctrl+C):
- Waits up to 2 seconds for active connections to close
- Force-closes remaining connections after timeout
- Displays a summary of uninitialized connection User-Agents:
Uninitialized connection summary: claude-code/2.1.7: 81, node: 1
Running Multiple Instances
Run multiple HostMCP servers simultaneously by using different ports and config files:
# Development instance (permissive)
hostmcp serve --port 18080 --config dev.yaml
# Another project (strict)
hostmcp serve --port 8081 --config strict.yaml
Connecting AI Assistants
HostMCP works with any MCP-compatible AI client. Choose the pattern that fits your setup.
Pattern A: With ai-sandbox template
The ai-sandbox template handles MCP registration automatically via setup-hostmcp.sh. See the ai-sandbox README for details.
Pattern B: Any DevContainer (without ai-sandbox)
HostMCP works with any existing DevContainer — no special template required.
1. Start HostMCP on the host OS
hostmcp init --workspace /path/to/your-project
hostmcp serve --config /path/to/your-project/.sandbox/config/hostmcp.yaml
2. Register inside the DevContainer
# Claude Code
claude mcp add --transport sse --scope user hostmcp \
http://host.docker.internal:18080/sse
# Gemini CLI
gemini mcp add --transport sse hostmcp \
http://host.docker.internal:18080/sse
3. Reconnect
In Claude Code: /mcp → "Reconnect"
host.docker.internalresolves to the host OS from inside any Docker container (Docker Desktop / OrbStack). No extra configuration needed.
Pattern C: Claude Desktop (and other desktop AI apps)
Claude Desktop can only reach external tools via MCP — it cannot run docker commands directly. HostMCP fills that gap.
1. Start HostMCP on the host OS (same as above)
2. Add to Claude Desktop
Open Claude Desktop settings → MCP Servers → Add server:
{
"mcpServers": {
"hostmcp": {
"url": "http://localhost:18080/sse"
}
}
}
Or via CLI:
claude mcp add --transport sse hostmcp http://localhost:18080/sse
Use
localhost(nothost.docker.internal) since Claude Desktop runs on the host OS directly.
Once connected, AI assistants can access your containers:
User: "Check the myapp-api container logs"
Claude: [Uses HostMCP] "I can see 500 errors in the logs..."
User: "Run tests in the API container"
Claude: [Uses HostMCP] "Running npm test... 3 tests passed"
CLI Commands
HostMCP provides two types of CLI commands:
Setup Commands
# Generate a config file for a workspace
hostmcp init --workspace /path/to/workspace
# With a custom port
hostmcp init --workspace /path/to/workspace --port 18080
# Overwrite an existing config
hostmcp init --workspace /path/to/workspace --force
--configvs--workspace:hostmcp serve,hostmcp tools sync, andhostmcp tools listall require either--config <path>or--workspace <path>(mutually exclusive). If--workspaceis given, the config file is derived as{workspace}/.sandbox/config/hostmcp.yaml.workspace_root(see Host Tools) is the directory HostMCP treats as the project root for staging/approving host tools and resolving relative paths — normally it comes from--workspace, or from the config file's ownhost_access.workspace_rootwhen--configis used directly. To reuse the samehostmcp.yaml(via--config) while pointing at a different workspace directory, use--workspace-rootinstead — it only overridesworkspace_rootand does not affect config resolution.
Current Container Commands
The "current container" feature (cli.current_container in config, enabled by default) lets you set a default container so you don't need -c/--container on every command:
# Set the current container
hostmcp use myapp-api
# logs/stats/exec now default to the current container
hostmcp logs
hostmcp stats
hostmcp exec "npm test"
# Clear the current container
hostmcp use --clear
Security note: In non-sandboxed environments where an AI assistant could run CLI commands directly on the host OS (not through MCP), the current-container feature could lead to unintended behavior. Set
cli.current_container.enabled: falsein that case.
Host Tools Management Commands
Host tools placed in staging directories (e.g. .sandbox/host-tools/) must be approved before HostMCP will run them:
# Interactively review and approve staged host tools
hostmcp tools sync
# Same, but starting the server immediately after sync
hostmcp serve --sync
# List currently approved and pending host tools
hostmcp tools list
Note: If
workspace_rootwas not given explicitly via--workspace/--workspace-root(i.e. it came from the--configfile as-is),hostmcp tools syncfirst asks you to confirm the resolved workspace (Continue with this workspace? [y/N]) before approving anything. It defaults to no on empty input or EOF, so running it non-interactively (e.g. in CI) without--workspace/--workspace-rootwill abort rather than sync.
Development mode:
hostmcp serve --devalso loads tools straight fromstaging_dirs, bypassing thehostmcp tools syncapproval step (precedence: staging > approved > common). Convenient while iterating on a host tool locally; avoid in shared or production-like environments since it skips human review.
Host OS Commands (Direct Docker Access)
These access the Docker socket directly and must be run on the host OS:
# List accessible containers
hostmcp list
# Get container logs
hostmcp logs myapp-api --tail 100
# Execute a whitelisted command
hostmcp exec -c myapp-api "npm test"
# Show container details with summary (default)
hostmcp inspect myapp-api
# Show container details as full JSON
hostmcp inspect myapp-api --json
# Get container stats
hostmcp stats myapp-api
Note: Unlike
logs/inspect/stats,exectakes the command to run as its positional argument, so the container must be given via-c/--container(or omitted if a current container is set).
list output example:
NAME ID IMAGE STATE STATUS PORTS
myapp-api 4a2e541171d9 node:18-alpine running Up 5 minutes 0.0.0.0:3000->3000/tcp
myapp-proxy 8b3f621283e1 nginx:alpine running Up 5 minutes 0.0.0.0:80->80/tcp
inspect summary output example:
=== Container Summary: myapp-api ===
State: running
Started: 2024-01-15T10:30:00Z
Image: node:18-alpine
--- Network ---
bridge:
IP: 172.17.0.2
Gateway: 172.17.0.1
--- Ports ---
0.0.0.0:3000 -> 3000/tcp
--- Mounts ---
/path/to/workspace -> /app (rw)
--- Full Details (JSON) ---
{ ... }
Client Commands (Via HTTP API)
These connect to the HostMCP server via HTTP and can be used inside an AI Sandbox:
# List containers via HostMCP server
hostmcp client list
# Get container logs via server
hostmcp client logs securenote-api --tail 100
# Show container details via server (default: summary)
hostmcp client inspect securenote-api
# Show container details via server (full JSON)
hostmcp client inspect securenote-api --json
# Get container stats via server
hostmcp client stats securenote-api
# Execute a command via server
hostmcp client exec securenote-api "npm test"
# Custom server URL
hostmcp client list --url http://localhost:18080
# Or use an environment variable
export HOSTMCP_SERVER_URL=http://host.docker.internal:18080
hostmcp client list
# Set a custom timeout (seconds)
hostmcp client --timeout 120 exec securenote-api "npm run build"
# Or use an environment variable
export HOSTMCP_TIMEOUT=120
hostmcp client exec securenote-api "npm run build"
Client command flags:
| Flag | Env var | Default | Description |
|---|---|---|---|
--url |
HOSTMCP_SERVER_URL |
server.port from the resolved workspace's hostmcp.yaml, else http://host.docker.internal:18080 |
HostMCP server URL |
--client-suffix / -s |
HOSTMCP_CLIENT_SUFFIX |
(none) | Suffix appended to client name |
--timeout |
HOSTMCP_TIMEOUT |
30 |
Timeout in seconds for HTTP requests and tool call responses |
About timeout:
--timeoutapplies to both sending HTTP requests and waiting for responses via SSE. Increase it for long-running commands likenpm run build. The SSE connection itself (session keep-alive) is not affected by this timeout.
Which to use:
- Host OS commands: When you have direct Docker socket access
- Client commands: Inside AI Sandbox, or environments without Docker socket access
Security Modes
Strict Mode
- Read-only operations (logs, inspect, stats)
- No command execution
- Most restrictive and safest
Moderate Mode (Recommended)
- Read operations allowed
- Command execution limited to whitelisted commands
- Good balance of safety and functionality
Permissive Mode
- All operations allowed
- Use only in trusted development environments
Authentication
The current version does not implement authentication.
HostMCP is designed for local development environments and binds to localhost by default.
Future plans:
- Optional authentication via configuration file
- Token-based authentication for remote access
- Implementation based on user demand
If you need authentication, please start a Discussion.
Configuration Reference
For complete configuration options, see configs/hostmcp.example.yaml:
- Container whitelist patterns
- Per-container command whitelists
- Audit logging
- Port and host settings
File Access Blocking (blocked_paths)
Configuration Example
security:
blocked_paths:
# Manually blocked paths
manual:
"securenote-api":
- "/.env"
- "/secrets/*"
"*": # Applies to all containers
- "*.key"
- "*.pem"
# Auto-import from DevContainer settings
auto_import:
enabled: true
workspace_root: "."
# Files to scan
scan_files:
- ".devcontainer/docker-compose.yml"
- ".devcontainer/devcontainer.json"
# Global patterns (applied to all containers)
global_patterns:
- ".env"
- "*.key"
- "secrets/*"
# Import from Claude Code settings
claude_code_settings:
enabled: true
max_depth: 1 # Depth for scanning subdirectories
settings_files:
- ".claude/settings.json"
- ".claude/settings.local.json"
# Import from Gemini Code Assist exclusion files (.aiexclude, .geminiignore)
# Same shape as claude_code_settings above; enabled by default.
gemini_settings:
enabled: true
max_depth: 1
settings_files:
- ".aiexclude"
- ".geminiignore"
max_depth Behavior
max_depth controls the depth for scanning Claude Code settings files:
/workspace/ ← where hostmcp serve is launched
├── .claude/settings.json ← ✅ scanned (depth 0)
├── demo-apps/
│ └── .claude/settings.json ← ✅ scanned (depth 1)
├── demo-apps-ios/
│ └── .claude/settings.json ← ✅ scanned (depth 1)
└── demo-apps/subproject/
└── .claude/settings.json ← ❌ not scanned (depth 2)
| max_depth | Behavior |
|---|---|
| 0 | workspace_root only |
| 1 | Up to 1 level deep |
| 2 | Up to 2 levels deep |
Integration with Claude Code Settings
Patterns from permissions.deny in Claude Code's .claude/settings.json can be automatically imported:
{
"permissions": {
"deny": [
"Read(securenote-api/.env)",
"Read(**/*.key)",
"Read(**/secrets/**)"
]
}
}
This unifies Claude Code's DevContainer settings with HostMCP's blocking policy.
Blocked Access Response
When access is blocked, a detailed reason is returned:
{
"blocked": true,
"reason": "claude_code_settings_deny",
"pattern": "**/*.key",
"source": "demo-apps/.claude/settings.json",
"hint": "This path is blocked by Claude Code settings (permissions.deny)..."
}
Output Masking
HostMCP automatically masks sensitive data (passwords, API keys, tokens) in tool output before returning it to AI assistants.
security:
output_masking:
enabled: true
replacement: "[MASKED]"
# Regex patterns to detect sensitive data
patterns:
- '(?i)(password|passwd|pwd)\s*[=:]\s*["'']?[^\s"''\n]+["'']?'
- '(?i)(api[_-]?key|secret[_-]?key)\s*[=:]\s*["'']?[^\s"''\n]+["'']?'
- '(?i)bearer\s+[a-zA-Z0-9._-]+'
- 'sk-[a-zA-Z0-9]{20,}'
- '(?i)(postgres|mysql|mongodb|redis)://[^:]+:[^@]+@'
# Which outputs to mask
apply_to:
logs: true # get_logs, search_logs
exec: true # exec_command
inspect: true # inspect_container (environment variables)
Example:
# Raw output
DATABASE_URL=postgres://admin:secret123@db:5432/app
# After masking
DATABASE_URL=[MASKED]db:5432/app
Host Path Masking
When host OS paths contain the user's home directory, the home directory portion is masked to hide it from AI.
security:
host_path_masking:
enabled: true # Enable path masking (default: true)
replacement: "[HOST_PATH]"
Supported paths:
- macOS:
/Users/username/...→[HOST_PATH]/... - Linux:
/home/username/...→[HOST_PATH]/... - Windows:
C:\Users\username\...→[HOST_PATH]\...
Example (inspect_container output):
// Raw output
{"Source": "/Users/john/workspace/myapp/.env"}
// After masking
{"Source": "[HOST_PATH]/workspace/myapp/.env"}
Note: This masking applies only to MCP tool output. CLI commands (
hostmcp inspect) show full paths since they are user-facing.
Permissions
Control globally allowed operations:
security:
permissions:
logs: true # Allow log retrieval (get_logs, search_logs)
inspect: true # Allow container inspection
stats: true # Allow resource statistics
exec: true # Allow exec execution (subject to exec_whitelist)
lifecycle: false # Allow start/stop/restart_container (disabled by default)
Default Commands (exec_whitelist "*")
Using "*" as the container name defines commands available to all containers:
security:
exec_whitelist:
# Container-specific commands
"myapp-api":
- "npm test"
- "npm run lint"
# Default commands for all containers
"*":
- "pwd"
- "whoami"
- "date"
Security warning: Do not add
env,printenv, orecho *to the default whitelist. These can expose all environment variables, including secrets.
Temporary commands:
hostmcp serve --allow-exec <container:command>adds a command toexec_whitelistfor the running server only, without editing the config file. Useful for a one-off debugging session. Repeat the flag for multiple entries.
Dangerous Mode (exec_dangerously)
When commands like tail, grep, or cat that are not whitelisted are needed for debugging, HostMCP provides a "dangerous mode" that allows these commands while maintaining blocked_paths restrictions.
Why Is Dangerous Mode Needed?
Docker's get_logs only shows stdout/stderr. To view log files like /var/log/app.log, you need tail or cat. However, adding these to exec_whitelist would allow reading arbitrary files, including those containing secrets.
Dangerous mode solves this:
- Allows specific commands (e.g.,
tail,cat,grep) - File paths are still checked against
blocked_paths - Pipes (
|), redirects (>), and path traversal (..) are blocked
Configuration
security:
exec_dangerously:
enabled: false # Global enable/disable
commands:
# Container-specific commands
"securenote-api":
- "tail"
- "head"
- "cat"
- "grep"
# Global commands (all containers)
"*":
- "tail"
- "ls"
Server Startup Flags
Enable dangerous mode at startup without modifying the configuration file:
# Enable for specific containers
hostmcp serve --dangerously=securenote-api,demo-app
# Enable for all containers
hostmcp serve --dangerously-all
These flags:
- Set
exec_dangerously.enabled = true - Add default commands:
tail,head,cat,grep,less,wc,ls,find
| Flag | Behavior |
|---|---|
--dangerously=container1,container2 |
Clears existing exec_dangerously.commands and enables only for specified containers |
--dangerously-all |
Merges with existing settings and adds commands to "*" (all containers) |
To strictly limit dangerous mode to specific containers, use
--dangerously=container. To broadly enable it while preserving container-specific settings, use--dangerously-all.
Usage
MCP tools (Claude Code):
{
"tool": "exec_command",
"arguments": {
"container": "securenote-api",
"command": "tail -100 /var/log/app.log",
"dangerously": true
}
}
CLI:
# Direct (host OS)
hostmcp exec --dangerously -c securenote-api "tail -100 /var/log/app.log"
# Client (AI Sandbox)
hostmcp client exec --dangerously --url http://host.docker.internal:18080 securenote-api "tail -100 /var/log/app.log"
Security Model
Request with dangerously=true
↓
1. Is exec_dangerously.enabled = true? (server config)
↓
2. Is the base command in exec_dangerously.commands?
↓
3. Check for pipes (|), redirects (>), path traversal (..)
↓
4. Extract file paths from command
↓
5. Check each path against blocked_paths
↓
Execute if all checks pass
Examples blocked by design:
cat /secrets/key.pem→ blocked byblocked_pathscat /etc/passwd | grep root→ pipes not allowedcat ../secrets/key→ path traversal not allowedrm /var/log/app.log→rmis not inexec_dangerously.commands
Security note: Clients must explicitly set
dangerously=true. This "opt-in" design ensures conscious acknowledgment when using dangerous mode.
Hint Messages on Errors
When trying to execute a command that isn't whitelisted but is available in dangerous mode, a hint is shown:
command not whitelisted: tail (hint: this command is available with dangerously=true)
Checking Available Commands
Use hostmcp client commands to see both whitelisted and dangerous commands:
$ hostmcp client commands
CONTAINER ALLOWED COMMANDS
--------- ----------------
* (all containers) pwd
whoami
securenote-api npm test
npm run lint
CONTAINER DANGEROUS COMMANDS (requires dangerously=true)
--------- ----------------------------------------------
* (all containers) tail
ls
securenote-api tail
cat
grep
Note: Commands with '*' wildcard match any suffix. Dangerous commands require dangerously=true parameter.
Host Tools (host_access.host_tools)
Host tools are approved scripts that AI assistants can run on the host OS via run_host_tool. Tools staged in a workspace directory must be approved with hostmcp tools sync before they can run (see Host Tools Management Commands).
host_access:
workspace_root: "."
host_tools:
enabled: true
# Approved tools directory (outside workspace, not writable by AI)
# Tools are organized per-project: <approved_dir>/<project-id>/
# <project-id> is derived automatically from the workspace path
# (format: <dir-name>-<short-hash>, e.g. "my-project-a1b2c3d4") — run
# `hostmcp tools list` to see the resolved value for your workspace.
approved_dir: "~/.hostmcp/host-tools"
# Staging directories where new tools are proposed (inside workspace)
staging_dirs:
- ".sandbox/host-tools"
# Load tools from _common/ subdirectory of approved_dir (shared across projects)
common: true
allowed_extensions:
- ".sh"
- ".go"
- ".py"
timeout: 60 # seconds
max_output_bytes: 102400 # 100 KB; set to 0 to disable
large_output_dir: ".sandbox/tmp" # relative to workspace root
Large Output Handling
When a host tool produces output exceeding max_output_bytes, HostMCP saves the full output to a file and returns a path and preview to the AI instead. This prevents large build logs or test reports from overflowing the AI's context window.
The AI receives a message like this:
Tool: my-build.sh
Exit Code: 0
⚠️ Output was large (N bytes) and has been saved to a file.
File: [HOST_PATH]/workspace/.sandbox/tmp/hostmcp-my-build-last.log
Use the Read or Grep tool to inspect the full output.
--- Preview (first/last 20 lines) ---
...
Note: Each tool run overwrites the previous file (
hostmcp-<toolname>-last.log), so only the most recent output is kept.
Host Commands (host_access.host_commands)
Separately from host tools, HostMCP can allow whitelisted CLI commands to be executed directly on the host OS via the exec_host_command MCP tool. This is disabled by default.
host_access:
host_commands:
enabled: false
# Whitelisted commands (base command → allowed argument patterns)
# Use "*" suffix for prefix matching, e.g. "-i *" matches "-i :8080"
whitelist:
"df":
- "-h"
"free":
- "-m"
"lsof":
- "-i *"
# Deny list (overrides whitelist)
# deny:
# "some-command":
# - "dangerous-subcommand *"
# Dangerous mode: allowed only when exec_host_command is called with dangerously=true
dangerously:
enabled: false
commands:
"kill":
- "*"
Server startup flag:
hostmcp serve --host-dangerouslysetshost_access.host_commands.dangerously.enabled = trueat startup, without editing the config file. Same opt-in intent as--dangerously/--dangerously-allforexec_dangerously.
Note:
docker/docker-composecommands are not needed here — inspection/monitoring is already covered bylist_containers,get_logs,get_stats, andinspect_container. For lifecycle operations (docker-compose up/down/build), use host tools instead (e.g. wrap them in.sandbox/host-tools/app-up.sh).
Logging
Server logging is configured via CLI flags rather than the config file's logging.level key alone:
hostmcp serve --config hostmcp.yaml --log-level debug --log-file server.log --log-also-stdout
| Flag | Description |
|---|---|
--log-level |
debug, info, warn, or error |
--log-file |
Write logs to this file path |
--log-also-stdout |
Also print logs to stdout when --log-file is set |
Audit Logging (audit)
Audit logs record security-relevant events (tool calls, access denials, client connections, security policy queries) as structured JSON for monitoring and compliance. Disabled by default.
audit:
enabled: false
# Required when enabled is true. Always written to a file (never stdout).
# Supports "~/" prefix for home directory expansion.
file: "~/.hostmcp/audit.log"
# Rotation on server startup: audit.log -> audit.log.1 -> audit.log.2 ...
# Files beyond 'keep' are deleted. Set keep: 0 to disable rotation.
rotation:
keep: 3
events:
tool_calls: true # exec_command, get_logs, read_file, etc.
access_denied: true # blocked paths, disallowed commands, permission errors
client_connections: true # client connect/disconnect
security_policy: false # get_security_policy, get_blocked_paths queries
Example audit log entry:
{
"time": "2024-01-15T10:30:45.123Z",
"level": "INFO",
"msg": "audit_event",
"event_type": "tool_call",
"tool": "exec_command",
"container": "myapp-api",
"result": "success",
"details": {"command": "npm test", "duration_ms": 1234}
}
Architecture
┌─────────────────────────────────┐
│ Host OS │
│ ┌──────────────────────────┐ │
│ │ HostMCP (Go binary) │ │
│ │ - MCP server (HTTP/SSE) │ │
│ │ - Security policies │ │
│ └────────┬─────────────────┘ │
│ │ :18080 │
│ ┌────────┴─────────────────┐ │
│ │ Docker Engine │ │
│ │ ├─ AI Sandbox │ │
│ │ │ └─ Claude Code ─┐ │ │
│ │ ├─ app-api ←─────────┘ │ │
│ │ └─ app-db │ │
│ └─────────────────────────┘ │
└─────────────────────────────────┘
Design Philosophy
Why doesn't HostMCP support docker-compose up/down or image rebuilds?
HostMCP is built with a clear separation of responsibilities: AI observes and suggests, humans handle infrastructure changes. Access is granted in graduated levels, with each level opt-in.
Core Design Principle
AI = eyes and mouth (observe, suggest)
Human = hands (execute infrastructure changes)
What AI can do (by default):
- Read logs, stats, and container information
- Execute whitelisted commands (tests, linting)
- Read files (with
blocked_pathsprotection) - Suggest changes and solutions
What AI can do (opt-in):
- Start/stop/restart containers (
lifecycle: true) - Run approved host tools (host_tools — enabled by default)
- Execute whitelisted host commands (host_commands)
What humans do:
- Rebuild images (
docker-compose build) - Recreate containers (
docker-compose up) - Approve host tools (
hostmcp tools sync) - Make infrastructure changes
Graduated Access Model
HostMCP provides five levels of access, each more permissive than the last:
| Level | Operations | Default | Risk |
|---|---|---|---|
| Read | Logs, stats, inspect, file listing | Enabled | None |
| Execute | Whitelisted commands in containers | Enabled (moderate mode) | Low |
| Lifecycle | Start/stop/restart containers | Disabled | Medium |
| Host tools | Approved host tool scripts | Enabled | Medium |
| Host commands | Whitelisted host CLI commands | Disabled | High |
Lifecycle and host commands are disabled by default and require explicit opt-in via hostmcp.yaml. Host tools are enabled by default but require human approval (hostmcp tools sync) before any tool can run.
Why Build/Recreate Remains Human-Only
1. Dockerfile Changes Require Rebuilds
When a Dockerfile is modified, a simple restart won't reflect the changes:
# This won't apply Dockerfile changes
docker restart myapp # still uses the old image
# What's actually needed
docker-compose build myapp
docker-compose up -d myapp
Container restart is useful for recovering a crashed container or applying config changes, but it cannot replace a full rebuild. HostMCP does not provide raw docker-compose build or docker-compose up as MCP tools to prevent the false assumption that restart solves everything.
Note: Host tools can wrap these operations in human-reviewed scripts (e.g.,
demo-build.sh,demo-up.sh). This gives AI controlled access while ensuring the scripts are explicitly approved.
2. Most Development Work Doesn't Need Container Operations
| Action | Solution | Container ops needed? |
|---|---|---|
| Code changes | Hot reload / exec npm run dev |
No |
| Config file changes | App reload command | No |
| Run tests | exec npm test |
No |
| Check logs | get_logs |
No |
| Container crashed | restart_container (opt-in) |
Yes |
| Dockerfile changes | Rebuild + recreate | Yes, by humans |
Cases that truly require image rebuilds (Dockerfile changes, docker-compose.yml changes) are infrastructure changes and should go through human review.
3. Risk vs. Frequency Trade-off
| Operation Level | Risk | Frequency During Development |
|---|---|---|
| Reading logs/stats | None | Very high |
| Whitelisted command execution | Low | High |
| Container restart | Medium | Low |
| Build/recreate | High | Very low |
Container restart is available as opt-in for the cases where it's genuinely useful (recovering crashed containers, applying environment variable changes). Build/recreate remains human-only due to its high risk and low frequency.
4. AI Investigates, Humans Act on Infrastructure
Good workflow:
- AI investigates logs, stats, and error patterns
- AI identifies the problem and suggests a solution
- AI restarts the container if
lifecycleis enabled and it's a simple recovery - For infrastructure changes, humans decide and execute
Risky workflow:
- AI detects an error and immediately rebuilds/recreates the container
- The build takes minutes, and the problem isn't resolved
- Humans can't understand what changed
About exec_command
exec_command lets you restrict allowed commands via whitelist:
exec_whitelist:
"myapp-api":
- "npm test"
- "npm run lint"
- "npm run dev" # Can restart the dev server
This enables:
- Running tests and linting
- Restarting development servers (via process manager)
- Health checks and debug commands
Not allowed:
- Arbitrary command execution
- File system modifications
- Network configuration changes
Summary
HostMCP's design provides graduated access:
- Read-only access to container information (logs, stats, inspect)
- Controlled command execution via whitelists
- File access with
blocked_pathsprotection - Container lifecycle (start/stop/restart) — opt-in, disabled by default
- Host tools — enabled by default (requires human approval per tool)
- Host commands — opt-in, disabled by default
- No image build/recreate operations — always human-only
Each level can be enabled independently, letting you choose the right balance of AI autonomy and human control for your environment.
Provided MCP Tools
| Tool | Description |
|---|---|
list_containers |
List accessible containers |
get_logs |
Get container logs |
get_stats |
Get resource usage statistics |
exec_command |
Execute whitelisted commands (dangerously mode supported) |
inspect_container |
Get detailed container information |
get_allowed_commands |
List whitelisted commands per container |
get_security_policy |
Show current security settings |
search_logs |
Search container logs by pattern |
list_files |
List files in a container directory (with blocking) |
read_file |
Read a file from a container (with blocking) |
get_blocked_paths |
Show blocked file paths |
restart_container |
Restart a container (requires lifecycle: true) |
stop_container |
Stop a running container (requires lifecycle: true) |
start_container |
Start a stopped container (requires lifecycle: true) |
list_host_tools |
List available host tools |
get_host_tool_info |
Get detailed info about a host tool |
run_host_tool |
Execute an approved host tool |
exec_host_command |
Execute a whitelisted host CLI command |
Dynamic capability info: The MCP
initializeresponse also includes aninstructionsfield summarizing the live host tool status (enabled tools, newly staged tools awaiting approval, and tools whose staged content changed since approval), generated fresh on each connection instead of relying on this static table alone.
Troubleshooting
HostMCP Server Not Recognized
-
Verify HostMCP is running on host:
curl http://localhost:18080/health # Should return 200 OK -
Check MCP configuration inside AI Sandbox:
cat ~/.claude.json | jq '.mcpServers.hostmcp' # Should show: "url": "http://host.docker.internal:18080/sse" -
Try MCP reconnect: In Claude Code, run
/mcp→ select "Reconnect" -
Fully restart VS Code: macOS:
Cmd + Q/ Windows/Linux:Alt + F4
After Restarting the HostMCP Server
Restarting the HostMCP server drops SSE connections, so the AI assistant needs to reconnect:
- Claude Code:
/mcp→ select "Reconnect" - If that doesn't work: Fully restart VS Code (Cmd+Q / Alt+F4)
"Connection refused" Error
- Is HostMCP running on host? →
ps aux | grep hostmcp - Are you using
host.docker.internalin the URL? (localhostwon't work from AI Sandbox) - Is port 18080 blocked by a firewall? →
lsof -i :18080
"Container not in allowed list"
Add the container name or pattern to allowed_containers in the config:
security:
allowed_containers:
- "your-container-name"
"Command not whitelisted"
Add the command to exec_whitelist in the config:
security:
exec_whitelist:
"container-name":
- "your command here"
License
MIT License - See LICENSE
Acknowledgments
- Built on Model Context Protocol
- Docker integration via docker/docker
- CLI powered by spf13/cobra
Note: HostMCP provides controlled access, but use it responsibly. Always review your security settings before exposing containers to AI assistants.
Documentation
¶
Overview ¶
Package main is the entry point for the HostMCP application. HostMCPアプリケーションのエントリーポイントとなるパッケージです。
HostMCP is an MCP (Model Context Protocol) server that provides controlled access to Docker containers for AI assistants. HostMCPは、AIアシスタントがDockerコンテナに制御されたアクセスを 提供するMCP(Model Context Protocol)サーバーです。
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
audit
Package audit provides audit logging for HostMCP.
|
Package audit provides audit logging for HostMCP. |
|
cli
backend.go defines the Backend interface and its implementations for container operations.
|
backend.go defines the Backend interface and its implementations for container operations. |
|
client
Package client provides an HTTP client for interacting with the HostMCP server.
|
Package client provides an HTTP client for interacting with the HostMCP server. |
|
config
Package config provides configuration management for HostMCP.
|
Package config provides configuration management for HostMCP. |
|
docker
Package docker provides a secure Docker client wrapper for HostMCP.
|
Package docker provides a secure Docker client wrapper for HostMCP. |
|
hosttools
sync.go implements the tool approval workflow for secure mode.
|
sync.go implements the tool approval workflow for secure mode. |
|
mcp
Package mcp provides response helper functions for formatting MCP tool responses.
|
Package mcp provides response helper functions for formatting MCP tool responses. |
|
security
blocked_paths.go provides file path blocking functionality for HostMCP.
|
blocked_paths.go provides file path blocking functionality for HostMCP. |