Sandbox MCP Server
by nagaozen
README.md
# Sandbox
LLM-first sandboxing for agents.
This repository defines a small semantic MCP surface over isolated Docker runtimes and ships itself as an [Agent Plugin](https://agent-plugins.org/). The public contract is designed for models first: eight tools, progressive capability disclosure, a single `/workspace`, and explicit sandbox lifecycle.
## Principles
- **LLM semantics over REST completeness.** Models see task-oriented tools, not every low-level runtime endpoint.
- **Progressive disclosure.** `sandbox_discover` reveals details only when an agent needs them.
- **One workspace.** Shell, files, browser downloads, generated artifacts, and future tools share `/workspace`.
- **Runtime independence.** MCP semantics do not depend on Docker. Docker is the first runtime adapter.
- **Explicit lifecycle.** Sandboxes are created, inspected, used, and destroyed deliberately.
- **Artifacts are first-class.** Files produced in `/workspace` can be enumerated and returned without leaking host details.
- **Defense in depth.** The runtime is container-isolated, capability-dropped, resource-limited, and never receives the Docker socket.
- **Observable work.** Every sandbox has a noVNC desktop and code-server; the headed Chromium shown on the desktop is the same browser controlled by `sandbox_browser`.
## Tool contract
The MCP server exposes exactly eight tools in v0.2:
| Tool | Purpose |
| --- | --- |
| `sandbox_discover` | Progressive disclosure of capabilities and usage semantics |
| `sandbox_create` | Create an isolated sandbox |
| `sandbox_inspect` | Inspect state and runtime capabilities |
| `sandbox_exec` | Run a bounded command in the sandbox |
| `sandbox_files` | Read/write/list/stat/mkdir/remove/move/copy workspace files |
| `sandbox_browser` | Navigate, inspect, click, type, screenshot, and evaluate in Chromium |
| `sandbox_artifacts` | List, inspect, and read generated workspace artifacts |
| `sandbox_destroy` | Destroy the runtime while optionally retaining workspace files |
Human-facing desktop/VS Code access is returned as metadata by `sandbox_create` and `sandbox_inspect`; it does not add model tools.
## Architecture
```text
Agent / Client
|
| Agent Plugins v1 + MCP
v
+-----------------------------+
| Sandbox MCP |
| 8 semantic tools |
| progressive disclosure |
+-------------+---------------+
|
| Runtime interface
v
+-----------------------------+
| DockerRuntime |
| lifecycle + daemon client |
+-------------+---------------+
|
| localhost-only mapped port
v
+-----------------------------+
| Sandbox runtime container |
| daemon |
| shell + filesystem |
| headed Chromium / Playwright|
| Xvfb + Openbox + noVNC |
| code-server |
| /workspace |
+-----------------------------+
```
The container daemon is intentionally *not* the model-facing API. It is an internal runtime protocol. Future adapters can implement the same runtime interface using Kubernetes, Firecracker, cloud sandboxes, or another isolation backend without changing the MCP tool contract.
## Requirements
- Node.js 24+
- pnpm 12+
- Docker Engine / Docker Desktop
## Development
```bash
corepack enable
pnpm install
pnpm check
pnpm image:build
pnpm dev:mcp
```
The MCP server uses `PLUGIN_DATA` when launched as an Agent Plugin. During local development it defaults to `.sandbox-data`.
## Build the runtime image
```bash
pnpm image:build
```
This creates both local tags:
```text
ghcr.io/nagaozen/sandbox-runtime:latest
nagaozen/sandbox-runtime:dev
```
Override the image selected by the MCP server with:
```bash
SANDBOX_IMAGE=my-registry/sandbox-runtime:tag
```
## Human desktop and VS Code
Every created sandbox returns two loopback-only interfaces:
- `interfaces.desktop.url` — noVNC desktop showing the headed Chromium session the LLM is controlling.
- `interfaces.vscode.url` — code-server opened directly on `/workspace`.
This intentionally preserves the useful observability of the older AIO container without increasing the LLM tool catalog. For a server-hosted chat interface, proxy `interfaces.desktop.hostPort` and `interfaces.vscode.hostPort` through authenticated application routes with WebSocket support. See [`docs/interfaces.md`](docs/interfaces.md).
## Agent Plugin
The repository root is an Agent Plugins v1 package:
```text
plugin.json
mcp.json
skills/
```
Build the distributable plugin:
```bash
pnpm plugin:package
```
This creates `release/plugin/` with the standard `plugin.json`, `mcp.json`, Skill, license, and a single bundled `bin/sandbox.mjs`. A compatible client can install that directory and launch the MCP server over `stdio`.
The plugin requires **Node.js 24+ and Docker** on the client host, but it does not require `pnpm`, a source checkout, or `node_modules`. The MCP process manages sandbox containers through the host Docker CLI.
Agent Plugins deliberately leaves installation and credential handling to clients. This plugin therefore does not embed secrets.
## Example agent flow
```text
sandbox_discover()
sandbox_create()
sandbox_exec("git clone ...")
sandbox_files(read package.json)
sandbox_exec("pnpm test")
sandbox_browser(navigate http://...)
sandbox_artifacts(list)
sandbox_destroy()
```
Agents should normally create one sandbox per task and reuse it until the task is complete.
## Security model
The sandbox container is untrusted execution space.
The Docker adapter:
- drops Linux capabilities,
- enables `no-new-privileges`,
- enforces CPU, memory, PID, and shared-memory limits,
- binds the internal daemon only to `127.0.0.1` on a random host port,
- does not mount the Docker socket into the sandbox,
- mounts only the task workspace,
- supports disabling network access.
Commands can do anything allowed *inside the container*. They cannot be treated as a policy boundary by themselves.
See [`docs/security.md`](docs/security.md).
## Repository layout
```text
apps/
mcp/ model-facing MCP server
daemon/ internal runtime daemon
packages/
protocol/ shared schemas and semantic types
runtime/ runtime abstraction
client/ daemon client
runtime-docker/ Docker adapter
skills/
sandbox/ Agent Skill
image/
Dockerfile
docs/
tests/
```
## Status
`0.2.0` is intentionally small. The architecture is meant to stabilize before adding more tool surface.
## License
Apache-2.0.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues