This guide covers setup, configuration, and usage of the RunboxJS MCP (Model Context Protocol) server. The MCP server exposes RunboxJS capabilities (virtual filesystem, shell, console, inspector) to AI assistants via the standardized JSON-RPC 2.0 protocol.
- Overview
- Quick Start
- Client Configuration
- Server Architecture
- Transport Types
- Available Tools
- Resources
- Prompts
- MCP Client (Outbound)
- Protocol Reference
- Logging and Debugging
- Examples
The RunboxJS MCP server implements the Model Context Protocol specification (version 2024-11-05), enabling any MCP-compatible AI client to:
- Execute commands in the RunboxJS sandbox (bun, npm, git, python, shell builtins)
- Read and write files in the virtual filesystem
- Search code across the project
- Access console logs from command execution
- List running processes
- Use prompt templates for common tasks (explain files, fix errors, scaffold projects)
The server exposes three MCP capability categories:
- Tools -- executable actions (8 tools)
- Resources -- readable data sources (console, processes, VFS files)
- Prompts -- reusable prompt templates (3 prompts)
cargo build --release
# Binary at: target/release/runbox# Standard mode (reads JSON-RPC from stdin, responds on stdout)
./target/release/runbox
# List available tools
./target/release/runbox --list-toolsecho '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | cargo runAdd to your claude_desktop_config.json (typically at ~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
{
"mcpServers": {
"runbox": {
"command": "runbox",
"args": []
}
}
}If the runbox binary is not in your PATH, use the full path:
{
"mcpServers": {
"runbox": {
"command": "/path/to/runbox",
"args": []
}
}
}In Cursor settings, add an MCP server:
{
"mcp": {
"servers": {
"runbox": {
"command": "runbox",
"args": []
}
}
}
}In your Zed settings (~/.config/zed/settings.json):
{
"language_models": {
"mcp_servers": {
"runbox": {
"command": "runbox",
"args": []
}
}
}
}In your Continue configuration (.continue/config.json):
{
"mcpServers": [
{
"name": "runbox",
"command": "runbox",
"args": []
}
]
}Any MCP-compatible client can connect by spawning the runbox binary and communicating via stdin/stdout using JSON-RPC 2.0.
┌────────────────────┐ JSON-RPC 2.0 ┌─────────────────────┐
│ MCP Client │ ◄──────────────────────► │ McpServer │
│ (Claude, Cursor, │ (stdin/stdout by │ │
│ Zed, Continue) │ default) │ ┌───────────────┐ │
└────────────────────┘ │ │ VFS │ │
│ │ (in-memory FS)│ │
│ ├───────────────┤ │
│ │ ProcessManager│ │
│ │ (process reg) │ │
│ ├───────────────┤ │
│ │ Console │ │
│ │ (structured │ │
│ │ logging) │ │
│ └───────────────┘ │
└─────────────────────┘
The server owns three core components:
- VFS (Virtual Filesystem) -- all file operations happen here
- ProcessManager -- tracks running process metadata
- Console -- collects structured log entries from command execution
The primary transport for MCP. The server reads JSON-RPC messages from stdin (one per line) and writes responses to stdout.
- Logs go to stderr to avoid contaminating the JSON-RPC channel
- Log level is controlled via the
RUNBOX_LOGenvironment variable
RUNBOX_LOG=debug runboxThe server supports HTTP with Server-Sent Events for web-based clients:
- Client sends requests via
POST {base_url}/message - Server sends responses via SSE stream at
GET {base_url}/sse
This is handled by the SseTransport in native builds. In WASM, use the browser's native EventSource API.
Full-duplex communication via WebSocket, handled by the WebSocketTransport using tungstenite.
For testing and embedding, InProcessTransport accepts a closure-based handler:
let transport = InProcessTransport {
handler: Box::new(|msg| {
server.handle(msg)
}),
};Executes a shell command in the sandbox.
Input Schema:
{
"type": "object",
"properties": {
"command": {
"type": "string",
"description": "Command to execute (bun, npm, python, git, ls, cat...)"
}
},
"required": ["command"]
}Examples:
{"name": "exec", "arguments": {"command": "npm init -y"}}
{"name": "exec", "arguments": {"command": "git init"}}
{"name": "exec", "arguments": {"command": "ls /"}}
{"name": "exec", "arguments": {"command": "python -c \"print('hello')\""}}Response:
- Success: stdout text (and stderr in
[stderr]section if present) - Error:
isError: truewith error message
Reads a file from the VFS.
Input Schema:
{
"type": "object",
"properties": {
"path": { "type": "string" }
},
"required": ["path"]
}Example:
{"name": "read_file", "arguments": {"path": "/package.json"}}Creates or overwrites a file in the VFS.
Input Schema:
{
"type": "object",
"properties": {
"path": { "type": "string" },
"content": { "type": "string" }
},
"required": ["path", "content"]
}Example:
{"name": "write_file", "arguments": {"path": "/index.js", "content": "console.log('hello');"}}Lists entries in a directory.
Input Schema:
{
"type": "object",
"properties": {
"path": { "type": "string", "default": "/" }
},
"required": []
}Example:
{"name": "list_dir", "arguments": {"path": "/src"}}Removes a file or directory.
Input Schema:
{
"type": "object",
"properties": {
"path": { "type": "string" }
},
"required": ["path"]
}Searches text across files in the project.
Input Schema:
{
"type": "object",
"properties": {
"query": { "type": "string" },
"path": { "type": "string", "default": "/" },
"ext": { "type": "string", "description": "Filter by extension, e.g., .ts" }
},
"required": ["query"]
}Example:
{"name": "search", "arguments": {"query": "TODO", "path": "/src", "ext": ".ts"}}Gets console log entries with optional filtering.
Input Schema:
{
"type": "object",
"properties": {
"level": { "type": "string", "enum": ["log", "info", "warn", "error", "debug"] },
"since_id": { "type": "number" }
},
"required": []
}Examples:
{"name": "console_logs", "arguments": {}}
{"name": "console_logs", "arguments": {"level": "error"}}
{"name": "console_logs", "arguments": {"since_id": 42}}Lists active processes in the sandbox.
Input Schema:
{
"type": "object",
"properties": {},
"required": []
}MCP resources are readable data sources that clients can query.
| Property | Value |
|---|---|
| URI | runbox://console/logs |
| Name | Console logs |
| MIME type | application/json |
Returns all console entries as a JSON array.
| Property | Value |
|---|---|
| URI | runbox://process/list |
| Name | Process list |
| MIME type | application/json |
Returns running processes as a JSON array.
Every file in the VFS root is exposed as a resource with:
| Property | Value |
|---|---|
| URI | file:///<filename> |
| Name | Filename |
| MIME type | Auto-detected (js, ts, json, html, css, md, etc.) |
MIME type detection supports common web development file extensions.
MCP prompts are reusable templates that guide AI interactions.
Generates a prompt asking the AI to explain a file's contents.
Arguments:
| Name | Required | Description |
|---|---|---|
path |
Yes | Path to the file to explain |
Generated Prompt:
The user wants an explanation of the following file:
--- <path> ---
<file contents>
---
Explain the purpose and structure of this file clearly.
Generates a prompt to analyze console errors and propose fixes.
Arguments: None
Generated Prompt:
The following errors appeared in the sandbox console:
<recent error-level log entries>
Analyze the root cause and propose a concrete fix.
Generates a prompt to create a new project structure.
Arguments:
| Name | Required | Description |
|---|---|---|
type |
Yes | Project type: bun-api, python-script, or fullstack |
name |
No | Project name |
Generated Prompt:
Scaffold a new <type> project named <name>.
Create the directory structure, config files, and a basic entry point.
Use best practices for the chosen stack.
RunboxJS can also act as an MCP client, connecting to external MCP servers to extend its capabilities.
The McpRegistry manages multiple concurrent server connections:
use runbox::mcp::registry::McpRegistry;
use runbox::mcp::client::McpServerConfig;
let mut registry = McpRegistry::new();
// Add a server
registry.add(McpServerConfig {
name: "filesystem".into(),
transport: TransportConfig::Stdio {
command: "npx".into(),
args: vec!["@modelcontextprotocol/server-filesystem".into(), "/tmp".into()],
},
env: HashMap::new(),
})?;Three transport types are supported for outbound connections:
Stdio:
{
"name": "filesystem",
"transport": { "type": "stdio", "command": "npx", "args": ["@mcp/server-filesystem", "/tmp"] }
}HTTP/SSE:
{
"name": "api-server",
"transport": { "type": "sse", "url": "https://api.example.com/mcp" }
}WebSocket:
{
"name": "ws-server",
"transport": { "type": "websocket", "url": "wss://mcp.example.com" }
}When multiple servers are connected, tools are namespaced to avoid collisions:
server_name/tool_name
Example: filesystem/read_file, github/create_issue
If a tool name is unique across all servers, it can be called without the prefix:
// Explicit namespace
registry.call_tool("filesystem/read_file", args)?;
// Implicit (only if unambiguous)
registry.call_tool("read_file", args)?;All communication follows JSON-RPC 2.0:
Request:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "exec",
"arguments": { "command": "ls /" }
}
}Response (success):
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [{ "type": "text", "text": "src\npackage.json\n" }],
"isError": false
}
}Response (error):
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32601,
"message": "unknown method: foo/bar"
}
}Notification (no response expected):
{
"jsonrpc": "2.0",
"method": "notifications/initialized",
"params": {}
}- Client sends
initializewith protocol version, capabilities, and client info - Server responds with its capabilities and server info
- Client sends
notifications/initializednotification - Server marks the connection as ready
Example:
// Client -> Server
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": { "roots": { "listChanged": false } },
"clientInfo": { "name": "my-client", "version": "1.0.0" }
}
}
// Server -> Client
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2024-11-05",
"capabilities": {
"tools": { "listChanged": true },
"resources": { "subscribe": false, "listChanged": true },
"prompts": { "listChanged": false }
},
"serverInfo": { "name": "runbox", "version": "0.3.8" },
"instructions": "RunBox: sandbox with VFS, shell, console, and inspector..."
}
}
// Client -> Server (notification, no response)
{
"jsonrpc": "2.0",
"method": "notifications/initialized",
"params": {}
}| Code | Name | Description |
|---|---|---|
-32700 |
Parse Error | Invalid JSON |
-32600 |
Invalid Request | Not a valid JSON-RPC request |
-32601 |
Method Not Found | Unknown method name |
-32602 |
Invalid Params | Missing or invalid parameters |
-32603 |
Internal Error | Server-side error |
The MCP server logs to stderr to avoid contaminating the JSON-RPC channel on stdout.
Control via the RUNBOX_LOG environment variable:
RUNBOX_LOG=error runbox # Only errors
RUNBOX_LOG=warn runbox # Warnings and above (default)
RUNBOX_LOG=info runbox # Informational messages
RUNBOX_LOG=debug runbox # Detailed debug output
RUNBOX_LOG=trace runbox # Maximum verbosity- Inspect raw messages: Set
RUNBOX_LOG=debugto see all incoming/outgoing JSON-RPC messages - Test manually: Pipe JSON to stdin and read stdout
- Check initialization: Look for "MCP client connected" in stderr logs
- Verify tools: Use
runbox --list-toolsto confirm tool availability
# Terminal 1: Start the MCP server with debug logging
RUNBOX_LOG=debug cargo run 2>mcp.log
# Terminal 2: Send requests
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | cargo run
echo '{"jsonrpc":"2.0","method":"notifications/initialized","params":{}}' | cargo run
echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"write_file","arguments":{"path":"/hello.js","content":"console.log(42);"}}}' | cargo run
echo '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"exec","arguments":{"command":"node /hello.js"}}}' | cargo run// Request
{"jsonrpc":"2.0","id":4,"method":"resources/list","params":{}}
// Response
{
"jsonrpc": "2.0",
"id": 4,
"result": {
"resources": [
{
"uri": "runbox://console/logs",
"name": "Console logs",
"description": "All console entries from the sandbox",
"mimeType": "application/json"
},
{
"uri": "runbox://process/list",
"name": "Process list",
"mimeType": "application/json"
},
{
"uri": "file:///hello.js",
"name": "hello.js",
"mimeType": "application/javascript"
}
]
}
}// Request
{
"jsonrpc": "2.0",
"id": 5,
"method": "prompts/get",
"params": {
"name": "scaffold",
"arguments": { "type": "bun-api", "name": "my-api" }
}
}
// Response
{
"jsonrpc": "2.0",
"id": 5,
"result": {
"messages": [
{
"role": "user",
"content": {
"type": "text",
"text": "Scaffold a new bun-api project named my-api.\nCreate the directory structure, config files, and a basic entry point.\nUse best practices for the chosen stack."
}
}
]
}
}