A community-driven registry for Claude, Cursor, Windsurf, Cline & more. Not affiliated with Anthropic.
Are you the author? Sign in to claim
A fully-featured, GUI-powered local LLM Agent sandbox with complete MCP protocol support. Features both CLI and full d
💡 新项目推荐: 如果你需要更轻量化的内核级沙箱方案,查看 DAIMON —— 一个专为 AI Agent 设计的轻量级本地沙箱运行时。DAIMON 采用 Rust 编写,基于 Linux Landlock LSM 和 seccomp 实现 OS 内核级强制沙箱,提供细粒度的文件系统访问控制、网络隔离和非特权用户身份执行。相比传统 Docker 容器方案更加轻量,适合 MCP Server 的安全执行环境。
Empower your Large Language Models (LLMs) with true "Computer Use" capabilities.
EdgeBox is a powerful desktop application that brings the cloud-based sandbox capabilities of E2B (e2b.dev) to your local machine. Based on the open-source E2B Code Interpreter project, EdgeBox transforms the sandbox into a locally-running environment, giving you full control over your AI agent's development and execution environment.
What makes EdgeBox unique: While most open-source sandbox projects only provide a terminal/CLI, EdgeBox offers both a command-line shell AND a full graphical (GUI) desktop environment via an integrated VNC viewer. This means your LLM Agent is no longer just a code executor—it's a digital worker that can operate browsers, use VS Code, and interact with desktop applications, just like a human.
The EdgeBox Main Application Dashboard
Computer Use Demo: types "google.com", presses Enter, and captures screenshot - showing computer use capabilities.
| Feature | EdgeBox | Other OSS Sandboxes (e.g., codebox) |
|---|---|---|
| Environment | 🖥️ Local | 🖥️ Local |
| Interface | GUI + CLI | CLI-Only |
| Capability | Computer Use & Code Interpreter | Code Interpreter |
| Data Privacy | ✅ 100% Private | ✅ 100% Private |
| Latency | ⚡️ Near-Zero | ⚡️ Near-Zero |
| Integration | ✅ MCP Compliant | Proprietary API |
EdgeBox exposes all its capabilities through the MCP protocol, organized into three core modules for your LLM Agent.
An interactive VNC session with VS Code and a browser.
bash terminal allows the execution of any Linux command.x-session-id header.EdgeBox exposes its capabilities through MCP tools, organized into two categories:
Code Execution Tools - Execute code in various languages:
execute_python - Execute Python code in isolated environmentexecute_typescript - Execute TypeScript/JavaScript codeexecute_r - Execute R code for statistical analysisexecute_java - Execute Java codeexecute_bash - Execute Bash scriptsShell Commands - Interact with the Linux environment:
shell_run - Run shell commands (stateful, persistent environment)shell_run_background - Run commands in background with process managementFilesystem Operations - Manage files and directories:
fs_list - List files in directoriesfs_read - Read file contentsfs_write - Write content to filesfs_info - Get file metadata and informationfs_watch - Monitor directory changes in real-timeMouse Controls - Programmatic mouse interaction:
desktop_mouse_click - Perform mouse clicks (left/right/middle)desktop_mouse_double_click - Double-click actionsdesktop_mouse_move - Move cursor to coordinatesdesktop_mouse_scroll - Scroll up/down with configurable amountdesktop_mouse_drag - Drag from one position to anotherKeyboard Controls - Text input and key combinations:
desktop_keyboard_type - Type text with clipboard support for non-ASCIIdesktop_keyboard_press - Press specific keys (Return, Escape, Tab, etc.)desktop_keyboard_combo - Execute key combinations (Ctrl+C, Alt+Tab, etc.)Window Management - Control desktop applications:
desktop_get_windows - List all windows with titles and IDsdesktop_switch_window - Focus specific windowsdesktop_maximize_window - Maximize windowsdesktop_minimize_window - Minimize windowsdesktop_resize_window - Resize windows to specific dimensionsVisual & Application Control:
desktop_screenshot - Capture desktop screenshots (PNG format)desktop_launch_app - Launch applications by namedesktop_wait - Add delays between actionsNote: Desktop tools are only available when GUI Tools are enabled in EdgeBox settings. Core tools are always available regardless of GUI settings.
EdgeBox is designed to provide a seamless and powerful local execution environment for LLM agents.
[LLM Agent (Claude, GPT, etc.)] <- MCP (HTTP Stream) -> [EdgeBox App] <- Docker API -> [Isolated Sandbox Container (Desktop + Shell)]
Download EdgeBox Download the latest release for your platform from the Releases page.
Install & Run Docker Desktop Ensure Docker Desktop is installed and running before starting EdgeBox.
Run EdgeBox
EdgeBox.exeEdgeBox.app.deb/.rpm package.Add EdgeBox to your LLM client with this configuration:
{
"mcpServers": {
"edgebox": {
"url": "http://localhost:8888/mcp"
}
}
}
Once configured, you can give your LLM agent natural language instructions like:
main.py."Easily manage multiple isolated environments by specifying an x-session-id in your MCP request headers.
Example configuration for different tasks:
{
"mcpServers": {
"edgebox-default": {
"url": "http://localhost:8888/mcp"
},
"edgebox-data-analysis": {
"url": "http://localhost:8888/mcp",
"headers": {
"x-session-id": "data-analysis"
}
},
"edgebox-web-scraping": {
"url": "http://localhost:8888/mcp",
"headers": {
"x-session-id": "web-scraping"
}
}
}
}
You can connect to EdgeBox's MCP server programmatically from your own code. Below are quickstart examples for Python and TypeScript.
Use the FastMCP client to connect to EdgeBox from Python.
Install:
pip install fastmcp
Example:
import asyncio
from fastmcp import Client
EDGEBOX_MCP_URL = "http://localhost:8888/mcp"
async def main():
client = Client(EDGEBOX_MCP_URL)
async with client:
# List available tools
tools = await client.list_tools()
for tool in tools:
print(f" - {tool.name}: {tool.description}")
# Execute Python code in the sandbox
result = await client.call_tool(
"execute_python",
{"code": "import sys; print(f'Hello from EdgeBox! Python {sys.version}')"},
)
print(f"Result: {result}")
# Run a shell command
result = await client.call_tool(
"shell_run",
{"command": "uname -a && whoami"},
)
print(f"Shell: {result}")
# File operations
await client.call_tool(
"fs_write",
{"path": "/tmp/hello.txt", "content": "Hello from EdgeBox!"},
)
result = await client.call_tool("fs_read", {"path": "/tmp/hello.txt"})
print(f"File content: {result}")
# Execute TypeScript code in the sandbox
result = await client.call_tool(
"execute_typescript",
{"code": "console.log(`Node.js ${process.version}`)"},
)
print(f"TypeScript: {result}")
# Desktop automation (requires GUI Tools enabled)
# result = await client.call_tool("desktop_screenshot", {})
# result = await client.call_tool("desktop_keyboard_type", {"text": "hello"})
asyncio.run(main())
Use the MCP SDK client (as referenced by fastmcp) to connect to EdgeBox from TypeScript.
Install:
npm install @modelcontextprotocol/sdk fastmcp
Example:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
const EDGEBOX_MCP_URL = "http://localhost:8888/mcp";
async function main() {
const client = new Client(
{ name: "edgebox-quickstart", version: "1.0.0" },
{ capabilities: {} },
);
const transport = new StreamableHTTPClientTransport(
new URL(EDGEBOX_MCP_URL),
);
await client.connect(transport);
try {
// List available tools
const { tools } = await client.listTools();
for (const tool of tools) {
console.log(` - ${tool.name}: ${tool.description}`);
}
// Execute Python code in the sandbox
const pythonResult = await client.callTool({
name: "execute_python",
arguments: {
code: "import sys; print(f'Hello from EdgeBox! Python {sys.version}')",
},
});
console.log("Result:", pythonResult.content);
// Run a shell command
const shellResult = await client.callTool({
name: "shell_run",
arguments: { command: "uname -a && whoami" },
});
console.log("Shell:", shellResult.content);
// File operations
await client.callTool({
name: "fs_write",
arguments: { path: "/tmp/hello.txt", content: "Hello from EdgeBox!" },
});
const readResult = await client.callTool({
name: "fs_read",
arguments: { path: "/tmp/hello.txt" },
});
console.log("File content:", readResult.content);
// Execute TypeScript code in the sandbox
const tsResult = await client.callTool({
name: "execute_typescript",
arguments: { code: "console.log(`Node.js ${process.version}`)" },
});
console.log("TypeScript:", tsResult.content);
// Desktop automation (requires GUI Tools enabled)
// const screenshot = await client.callTool({ name: "desktop_screenshot", arguments: {} });
// const typed = await client.callTool({ name: "desktop_keyboard_type", arguments: { text: "hello" } });
} finally {
await client.close();
}
}
main().catch(console.error);
EdgeBox provides container lifecycle tools (container_list, container_create, container_stop, container_restart, container_delete) to manage sandbox containers programmatically.
container_stop ends the session/container mapping, so in typical workflows you should use either container_stop or container_delete as the final cleanup step (not both in sequence for the same session_id).
Python:
import asyncio
from fastmcp import Client
async def main():
client = Client("http://localhost:8888/mcp")
async with client:
# Create a new container (with 60-minute timeout)
result = await client.call_tool(
"container_create",
{"session_id": "my-session", "timeout": 60},
)
print(f"Create: {result}")
# List all active containers
result = await client.call_tool("container_list", {})
print(f"List: {result}")
# Restart the container
result = await client.call_tool(
"container_restart", {"session_id": "my-session"}
)
print(f"Restart: {result}")
# Cleanup option A: stop container for this session
result = await client.call_tool(
"container_stop", {"session_id": "my-session"}
)
print(f"Stop: {result}")
# Cleanup option B (alternative): delete directly instead of stop
# Use a different session_id here to keep the demo deterministic.
await client.call_tool(
"container_create",
{"session_id": "my-session-delete", "timeout": 60},
)
result = await client.call_tool(
"container_delete", {"session_id": "my-session-delete"}
)
print(f"Delete: {result}")
asyncio.run(main())
TypeScript:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
async function main() {
const client = new Client(
{ name: "edgebox-container-mgmt", version: "1.0.0" },
{ capabilities: {} },
);
const transport = new StreamableHTTPClientTransport(
new URL("http://localhost:8888/mcp"),
);
await client.connect(transport);
try {
// Create a new container (with 60-minute timeout)
const created = await client.callTool({
name: "container_create",
arguments: { session_id: "my-session", timeout: 60 },
});
console.log("Create:", created.content);
// List all active containers
const list = await client.callTool({
name: "container_list",
arguments: {},
});
console.log("List:", list.content);
// Restart the container
const restarted = await client.callTool({
name: "container_restart",
arguments: { session_id: "my-session" },
});
console.log("Restart:", restarted.content);
// Cleanup option A: stop container for this session
const stopped = await client.callTool({
name: "container_stop",
arguments: { session_id: "my-session" },
});
console.log("Stop:", stopped.content);
// Cleanup option B (alternative): delete directly instead of stop
// Use a different session_id here to keep the demo deterministic.
await client.callTool({
name: "container_create",
arguments: { session_id: "my-session-delete", timeout: 60 },
});
const deleted = await client.callTool({
name: "container_delete",
arguments: { session_id: "my-session-delete" },
});
console.log("Delete:", deleted.content);
} finally {
await client.close();
}
}
main().catch(console.error);
In EdgeBox, each session maps to its own isolated Docker container with a separate filesystem and runtime. When no x-session-id header is provided, all requests share a single "default_session" container. By passing different x-session-id headers, you can run fully isolated workloads.
Python:
import asyncio
from fastmcp import Client
from fastmcp.client.transports import StreamableHttpTransport
async def run_in_session(session_id: str):
"""Each session gets its own isolated container."""
transport = StreamableHttpTransport(
url="http://localhost:8888/mcp",
headers={"x-session-id": session_id},
)
client = Client(transport)
async with client:
# Write a file - only visible within this session's container
await client.call_tool(
"fs_write",
{"path": "/tmp/id.txt", "content": f"I am {session_id}"},
)
# Read it back
result = await client.call_tool("fs_read", {"path": "/tmp/id.txt"})
print(f"[{session_id}] /tmp/id.txt => {result}")
# Run a command in this session's container
result = await client.call_tool(
"shell_run", {"command": "hostname"}
)
print(f"[{session_id}] hostname => {result}")
async def main():
# Run sequentially for deterministic output in Python clients.
await run_in_session("session-alice")
await run_in_session("session-bob")
# session-alice's /tmp/id.txt contains "I am session-alice"
# session-bob's /tmp/id.txt contains "I am session-bob"
# They never interfere with each other.
asyncio.run(main())
TypeScript:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
async function runInSession(sessionId: string) {
const client = new Client(
{ name: "edgebox-session-demo", version: "1.0.0" },
{ capabilities: {} },
);
const transport = new StreamableHTTPClientTransport(
new URL("http://localhost:8888/mcp"),
{
requestInit: {
headers: { "x-session-id": sessionId },
},
},
);
await client.connect(transport);
try {
// Write a file - only visible within this session's container
await client.callTool({
name: "fs_write",
arguments: { path: "/tmp/id.txt", content: `I am ${sessionId}` },
});
// Read it back
const read = await client.callTool({
name: "fs_read",
arguments: { path: "/tmp/id.txt" },
});
console.log(`[${sessionId}] /tmp/id.txt =>`, read.content);
// Run a command in this session's container
const host = await client.callTool({
name: "shell_run",
arguments: { command: "hostname" },
});
console.log(`[${sessionId}] hostname =>`, host.content);
} finally {
await client.close();
}
}
async function main() {
// These two sessions run in completely separate containers
await Promise.all([
runInSession("session-alice"),
runInSession("session-bob"),
]);
// session-alice's /tmp/id.txt contains "I am session-alice"
// session-bob's /tmp/id.txt contains "I am session-bob"
// They never interfere with each other.
}
main().catch(console.error);
If you want to validate session isolation under light concurrency, keep concurrency low (e.g. <= 4) and verify each session reads back the value it wrote.
Python (4 sessions, unique paths):
import asyncio
from fastmcp import Client
from fastmcp.client.transports import StreamableHttpTransport
URL = "http://localhost:8888/mcp"
SESSIONS = ["s1", "s2", "s3", "s4"]
ROUNDS = 20
async def run_once(session_id: str, round_no: int) -> bool:
transport = StreamableHttpTransport(
url=URL,
headers={"x-session-id": session_id},
)
client = Client(transport)
path = f"/tmp/id-{session_id}.txt"
expected = f"{session_id}-r{round_no}"
async with client:
await client.call_tool("fs_write", {"path": path, "content": expected})
read = await client.call_tool("fs_read", {"path": path})
actual = read.content[0].text
return actual == expected
async def main():
bad = 0
for i in range(1, ROUNDS + 1):
results = await asyncio.gather(*[run_once(s, i) for s in SESSIONS])
bad += sum(0 if ok else 1 for ok in results)
print(f"BAD={bad}")
asyncio.run(main())
TypeScript (4 sessions, unique paths):
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
const URL = "http://localhost:8888/mcp";
const SESSIONS = ["s1", "s2", "s3", "s4"];
const ROUNDS = 20;
async function runOnce(sessionId: string, roundNo: number): Promise<boolean> {
const client = new Client(
{ name: "edgebox-isolation-check", version: "1.0.0" },
{ capabilities: {} },
);
const transport = new StreamableHTTPClientTransport(new URL(URL), {
requestInit: { headers: { "x-session-id": sessionId } },
});
await client.connect(transport);
const path = `/tmp/id-${sessionId}.txt`;
const expected = `${sessionId}-r${roundNo}`;
try {
await client.callTool({
name: "fs_write",
arguments: { path, content: expected },
});
const read = await client.callTool({
name: "fs_read",
arguments: { path },
});
const actual = (read.content?.[0] as any)?.text ?? "";
return actual === expected;
} finally {
await client.close();
}
}
async function main() {
let bad = 0;
for (let i = 1; i <= ROUNDS; i++) {
const results = await Promise.all(SESSIONS.map((s) => runOnce(s, i)));
bad += results.filter((ok) => !ok).length;
}
console.log(`BAD=${bad}`);
}
main().catch(console.error);
See the LICENSE file for details.
💻 A curated list of papers and resources for multi-modal Graphical User Interface (GUI) agents.
An AI-powered custom node for ComfyUI designed to enhance workflow automation and provide intelligent assistance
Deterministic multi-agent pipeline for end-to-end software development, orchestrating CLI-based AI tools (e.g. Gemini, C
Pocket Flow: Codebase to Tutorial