Simple and secure JavaScript code execution using Deno sandbox. Run user code in isolation with custom function injection.
- π Secure Sandboxing: Uses Deno's permission system for isolated code execution
- π JSON-RPC IPC: Handler calls transmitted via JSON-RPC between sandbox and host
- π― Custom Functions: Inject any async functions into sandbox via
registerHandler() - π¦ Zero Config: Automatically locates Deno binary from npm package
- π‘οΈ Resource Limits: Configurable timeouts and memory limits
# npm
npm install @mcpc-tech/handle-sandbox
# jsr
npx jsr add @mcpc/handle-sandbox
deno add @mcpc/handle-sandbox --allow-scripts=npm:denoimport { Sandbox } from "@mcpc/handle-sandbox";
const sandbox = new Sandbox();
sandbox.start();
const result = await sandbox.execute(`
console.log("Hello from sandbox!");
return 1 + 1;
`);
console.log(result.logs); // ["Hello from sandbox!"]
console.log(result.result); // 2
sandbox.stop();import { Sandbox } from "@mcpc/handle-sandbox";
const sandbox = new Sandbox();
// Register custom functions
sandbox.registerHandler("fetchUser", async (userId) => {
return { id: userId, name: "Alice" };
});
sandbox.registerHandler("saveData", async (data) => {
// Save to database
return { success: true };
});
sandbox.start();
// Use custom functions in code
const result = await sandbox.execute(`
const user = await callHandler("fetchUser", 123);
console.log("User:", user.name);
await callHandler("saveData", { userId: user.id, action: "login" });
return user;
`);
sandbox.stop();import { Sandbox } from "@mcpc/handle-sandbox";
const sandbox = new Sandbox({
timeout: 5000, // 5 seconds
memoryLimit: 256, // 256 MB
permissions: ["--allow-net=api.example.com"],
});
sandbox.start();
const result = await sandbox.execute(`
return "code runs here";
`);
sandbox.stop();The sandbox uses bidirectional JSON-RPC communication:
- Host spawns Deno sandbox subprocess
- Host sends
executeCoderequest with user's JavaScript code - Sandbox runs the code
- When code calls
callHandler(name, ...args):- Sandbox sends
callHandlerrequest to host - Host executes the registered handler function
- Host sends response back to sandbox
- Sandbox receives result and continues code execution
- Sandbox sends
- Sandbox returns final execution result to host
Create a new sandbox instance.
Config Options:
timeout?: number- Execution timeout in milliseconds (default: 30000)memoryLimit?: number- Memory limit in MBpermissions?: string[]- Deno permission flags
Register a function that can be called from sandbox code.
sandbox.registerHandler("myFunction", async (arg1, arg2) => {
return result;
});Start the Deno subprocess. Call this before executing code.
Execute JavaScript code in the sandbox.
Returns: Promise<{ logs: string[], result?: any, error?: string }>
Parameters:
code: string- JavaScript code to executecontext?: Record<string, any>- Additional context (optional)
Stop the sandbox and clean up resources.
The Deno sandbox runs with minimal permissions by default. You control access by passing Deno permission flags directly:
// No permissions - can only call registered handlers
new Sandbox();
// Allow network access to specific domains
new Sandbox({
permissions: ["--allow-net=github.com,api.example.com"],
});
// Allow reading specific directories
new Sandbox({
permissions: ["--allow-read=/tmp,/var/log"],
});Permission Model
// Sandbox runs WITHOUT permissions by default
// β These operations will fail:
const code = `
await Deno.readTextFile('/file.txt'); // PermissionDenied: --allow-read needed
await fetch('https://api.com'); // PermissionDenied: --allow-net needed
Deno.env.get('SECRET'); // PermissionDenied: --allow-env needed
`;
// β
Operations must use registered handlers:
sandbox.registerHandler("readFile", async (path) => {
return await fs.readFile(path, "utf-8");
});
const code = `
const content = await callHandler("readFile", "/file.txt");
`;sequenceDiagram
participant Host as Host (Node)
participant Sandbox as Sandbox (Deno)
Host->>Sandbox: executeCode("fetch('https://google.com')")
activate Sandbox
Note over Sandbox: deno run --no-prompt
Sandbox--xHost: PermissionDenied: --allow-net needed
deactivate Sandbox
Host->>Sandbox: executeCode("callMCPTool('http.fetch', ...)")
activate Sandbox
Sandbox->>Host: callTool('http.fetch', {url: 'https://google.com'})
activate Host
Note over Host: Execute MCP tool
Host-->>Sandbox: {status: 200, body: "..."}
deactivate Host
Sandbox-->>Host: execution result
deactivate Sandbox
- Your app spawns Deno subprocess
- Sends code to execute via JSON-RPC
- Sandbox runs code in isolated environment
- When code calls registered handlers, sandbox sends request back
- Your app handles request and sends response
- Sandbox receives response and continues execution
See examples/ directory for complete examples:
basic-usage.ts- Simple code execution
# Run tests
deno test --allow-all tests/
# Run example
deno run --allow-all examples/basic-usage.tsMIT