An MCP server that lets AI agents running inside VMs execute whitelisted commands on the host machine.
Built for situations where tools like rkdeveloptool and upgrade_tool don't work properly inside a VM (USB passthrough issues, kernel module access, etc.) but the agent still needs to use them.
+-----------------------+ +----------------------------+
| VirtualBox VM | HTTP/SSE | Host |
| |------------->| |
| AI agent | | gatecmd |
| (Claude Code, etc.) | | - validates against YAML |
| |<-------------| - runs command |
| connects to | | - returns output |
| http://host:9222/mcp| | |
+-----------------------+ +----------------------------+
- Agent calls the
list_commandsMCP tool to see what's available - Agent calls
execute_commandwith a command name and arguments - gatecmd checks the arguments match an allowed pattern in the YAML config
- If valid, runs the command (no shell, direct exec) and returns stdout/stderr
- Allowlist-only -- commands must be explicitly listed in YAML
- Pattern-matched args -- arguments must match a defined pattern (e.g.
ld,wl {offset} {file}) - No shell -- uses
Command::new(), neversh -c - Metacharacter rejection --
; | & $ \( ) > <` etc. are all rejected - Absolute binary paths -- no PATH resolution
- Path traversal protection --
..rejected in file arguments - File root restriction -- all file arguments must resolve under a configured
file_rootdirectory (e.g. a VirtualBox shared folder), preventing the agent from reading or writing arbitrary host paths - Bearer token auth -- shared secret on every request
- Per-command timeouts -- prevents hangs
Edit allowed_commands.yaml:
bind: "0.0.0.0:9222"
auth_token: "your-secret-token-here"
file_root: "/path/to/shared/folder"
commands:
- name: rkdeveloptool
binary: /usr/bin/rkdeveloptool
description: "Rockchip USB development tool"
allowed_args:
- pattern: "ld"
- pattern: "rd {offset} {size}"
- pattern: "wl {offset} {file}"
file_args: ["file"]
- pattern: "db {file}"
file_args: ["file"]
- pattern: "ul {file}"
file_args: ["file"]
timeout_secs: 120
- name: upgrade_tool
binary: /path/to/upgrade_tool_v2.17_for_linux/upgrade_tool
description: "Rockchip command-line firmware development tool (v2.17)"
allowed_args:
- pattern: "ld"
- pattern: "db {file}"
file_args: ["file"]
- pattern: "ul {file}"
file_args: ["file"]
- pattern: "uf {file}"
file_args: ["file"]
- pattern: "wl {offset} {file}"
file_args: ["file"]
- pattern: "rl {offset} {size} {file}"
file_args: ["file"]
- pattern: "di -p {file}"
file_args: ["file"]
- pattern: "ef {file}"
file_args: ["file"]
- pattern: "rd {mode}"
- pattern: "rfi"
- pattern: "rci"
- pattern: "pl"
- pattern: "ssd"
timeout_secs: 120
- name: lsusb
binary: /usr/bin/lsusb
description: "List USB devices"
allowed_args:
- pattern: ""
- pattern: "-v -d {vid_pid}"
timeout_secs: 10Each pattern defines an exact argument structure. {name} slots accept any value (subject to sanitization). Parameters listed in file_args must be absolute paths under file_root with no .. traversal.
The file_root directory is where the VM agent places files that it wants to reference in commands. For a VirtualBox setup:
- Create a shared folder between the VM and host (e.g.
/home/user/vm-sharedon host,/mnt/sharedin VM) - Set
file_rootin the YAML to the host-side path - The agent writes files to the shared folder from inside the VM, then passes the host-side absolute path to commands
All file arguments are validated to be under file_root. Attempts to reference files outside this directory are rejected.
gatecmd includes built-in file management tools (list_files, copy_file, remove_file) scoped to the shared directory. These let the agent stage files for flashing without depending on the VM shared mount being perfectly in sync. All paths are relative to file_root and subject to the same traversal protections.
cargo build --release
./target/release/gatecmd allowed_commands.yamlAdd to your Claude Code MCP config (~/.claude.json or project settings):
{
"mcpServers": {
"gatecmd": {
"url": "http://HOST_IP:9222/mcp",
"headers": {
"Authorization": "Bearer your-secret-token-here"
}
}
}
}The agent can then use:
list_commands-- see all available commands, allowed argument patterns, and the file root pathexecute_command-- run a command, e.g.{"command": "upgrade_tool", "args": "ld"}list_files-- list files in the shared directory, e.g.{"path": ""}or{"path": "images"}copy_file-- copy a file within the shared directory, e.g.{"src": "fw/boot.img", "dst": "staging/boot.img"}remove_file-- delete a file from the shared directory, e.g.{"path": "staging/old.img"}
The standard Rockchip USB development tool (installed to /usr/bin). Supports device listing, boot download, loader/image flashing, and read/write by address.
Rockchip's command-line firmware development tool, bundled in upgrade_tool_v2.17_for_linux/. Provides a superset of rkdeveloptool functionality including full firmware upgrade (uf), partition image flashing (di), device erase (ef/el), device info (rfi/rci/pl), and multi-storage/multi-device support.
See upgrade_tool_v2.17_for_linux/upgrade_tool_user_guide.md for the full command reference (translated from the original Chinese PDF).
| Field | Description |
|---|---|
bind |
Address to listen on (e.g. 0.0.0.0:9222) |
auth_token |
Bearer token required for all requests |
file_root |
Absolute path to the directory all file arguments must be under |
commands[].name |
Command name (used in execute_command calls) |
commands[].binary |
Absolute path to the executable |
commands[].description |
Shown in list_commands output |
commands[].allowed_args[] |
List of permitted argument patterns |
commands[].allowed_args[].pattern |
Argument pattern ("" for no args, {name} for parameter slots) |
commands[].allowed_args[].file_args |
Which {name} parameters are file paths (validated against file_root) |
commands[].timeout_secs |
Max execution time in seconds (default: 30) |
RUST_LOG-- set log level (e.g.RUST_LOG=debug)
Requires Rust 1.75+.
cargo build --release
cargo test