@justanothermldude/mcp-exec
v1.2.7
Published
MCP execution utilities for sandboxed code execution with OS-level isolation
Readme
@meta-mcp/exec
MCP execution utilities for sandboxed TypeScript/JavaScript code execution with OS-level isolation.
Installation
npm install @meta-mcp/execOverview
This package provides secure code execution capabilities with:
- Sandbox Isolation: OS-level sandboxing via
@anthropic-ai/sandbox-runtime(sandbox-exec on macOS, bubblewrap on Linux) - MCP Bridge: HTTP bridge allowing sandboxed code to call MCP tools
- Wrapper Generation: Auto-generate type-safe TypeScript wrappers for MCP tools
Quick Start
Basic Code Execution
import { executeCode } from '@meta-mcp/exec';
const result = await executeCode({
code: 'console.log("Hello from sandbox!")',
timeout_ms: 5000,
});
console.log(result.output); // ['Hello from sandbox!']
console.log(result.durationMs); // execution time in msUsing as MCP Server
# Start the mcp-exec server
npx @meta-mcp/exec
# Or with specific config
SERVERS_CONFIG=~/.meta-mcp/servers.json npx @meta-mcp/execAPI Reference
executeCode
Execute code in a sandboxed environment.
import { executeCode } from '@meta-mcp/exec';
const result = await executeCode({
code: string; // TypeScript/JavaScript code to execute
timeout_ms?: number; // Max execution time (default: 30000)
});
// Result type
interface ExecutionResult {
output: string[]; // stdout lines
error?: string; // stderr content
durationMs: number; // execution time
}SandboxExecutor
Create a custom executor with specific configuration:
import { SandboxExecutor, createExecutor } from '@meta-mcp/exec';
// Using factory function
const executor = createExecutor({
mcpBridgePort: 4000,
additionalWritePaths: ['/tmp/my-app'],
enableLogMonitor: true,
});
// Using class directly
const executor = new SandboxExecutor({
mcpBridgePort: 4000,
additionalWritePaths: ['/tmp/my-app'],
});
// Execute code
const result = await executor.execute('console.log(1 + 1)', 5000);
// Check status
executor.checkDependencies(); // true if sandbox-runtime available
executor.isSandboxingEnabled(); // true if OS sandboxing active
// Update config
executor.updateConfig({ mcpBridgePort: 5000 });
// Clean up
await executor.reset();MCPBridge
HTTP bridge for MCP tool access from sandboxed code:
import { MCPBridge } from '@meta-mcp/exec';
import { ServerPool, createConnection, getServerConfig } from '@meta-mcp/core';
// Create server pool
const connectionFactory = async (serverId: string) => {
const config = getServerConfig(serverId);
if (!config) throw new Error(`Server not found: ${serverId}`);
return createConnection(config);
};
const pool = new ServerPool(connectionFactory);
// Create and start bridge
const bridge = new MCPBridge(pool, {
port: 3000,
host: '127.0.0.1',
});
await bridge.start();
// Bridge endpoints:
// GET /health - Health check
// POST /call - Execute MCP tool call
// Check status
bridge.isRunning(); // true
bridge.getPort(); // 3000
bridge.getHost(); // '127.0.0.1'
// Stop bridge
await bridge.stop();Wrapper Generator
Generate type-safe TypeScript wrappers for MCP tools:
import { generateToolWrapper, generateServerModule } from '@meta-mcp/exec';
import type { ToolDefinition } from '@meta-mcp/core';
// Generate wrapper for single tool
const tool: ToolDefinition = {
name: 'read_file',
description: 'Read contents of a file',
inputSchema: {
type: 'object',
properties: {
path: { type: 'string', description: 'File path to read' },
},
required: ['path'],
},
};
const wrapper = generateToolWrapper(tool, 'filesystem');
// Generates TypeScript function that calls bridge endpoint
// Generate module for all server tools
const tools: ToolDefinition[] = [/* ... */];
const module = generateServerModule(tools, 'filesystem');
// Generates complete TypeScript module with all tool wrappersGenerated wrapper example:
export interface ReadFileInput {
/** File path to read */
path: string;
}
/**
* Read contents of a file
* @param input.path - File path to read
* @returns Promise resolving to tool result
*/
export async function read_file(input: ReadFileInput): Promise<unknown> {
const response = await fetch('http://localhost:3000/call', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
server_name: 'filesystem',
tool_name: 'read_file',
arguments: input,
}),
});
if (!response.ok) {
throw new Error(`Tool call failed: ${response.statusText}`);
}
return response.json();
}MCP Server
Create the mcp-exec MCP server programmatically:
import { createMcpExecServer } from '@meta-mcp/exec';
import { ServerPool, createConnection, getServerConfig } from '@meta-mcp/core';
// Create pool
const connectionFactory = async (serverId: string) => {
const config = getServerConfig(serverId);
if (!config) throw new Error(`Server not found: ${serverId}`);
return createConnection(config);
};
const pool = new ServerPool(connectionFactory);
// Create server
const { server, listToolsHandler, callToolHandler, shutdown } = createMcpExecServer(pool);
// List available tools
const tools = await listToolsHandler();
// => { tools: [{ name: 'execute_code_with_wrappers', ... }, ...] }
// Call execute_code_with_wrappers tool
const result = await callToolHandler({
name: 'execute_code_with_wrappers',
arguments: {
code: 'console.log("Hello!")',
wrappers: [], // optional: MCP server names to generate typed wrappers for
},
});
// Graceful shutdown
await shutdown();Types
import type {
ExecuteCodeInput,
ExecutionResult,
SandboxExecutorConfig,
MCPBridgeConfig,
CallRequest,
CallResponse,
} from '@meta-mcp/exec';
interface ExecuteCodeInput {
code: string;
timeout_ms?: number;
}
interface ExecutionResult {
output: string[];
error?: string;
durationMs: number;
}
interface SandboxExecutorConfig {
mcpBridgePort?: number;
additionalWritePaths?: string[];
enableLogMonitor?: boolean;
}
interface MCPBridgeConfig {
port?: number;
host?: string;
}
interface CallRequest {
server: string;
tool: string;
args?: Record<string, unknown>;
}
interface CallResponse {
success: boolean;
content?: unknown[];
isError?: boolean;
error?: string;
}CLI Usage
# Start the MCP server
mcp-exec
# Show version
mcp-exec --version
# Show help
mcp-exec --helpUsing Wrappers in Sandboxed Code
When code runs in the sandbox, it can call MCP tools via the HTTP bridge:
// Code running inside sandbox
const result = await fetch('http://localhost:3000/call', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
server: 'filesystem',
tool: 'read_file',
args: { path: '/tmp/test.txt' },
}),
});
const data = await result.json();
console.log(data.content[0].text);Or use generated wrappers:
// Import generated wrappers
import { read_file, write_file } from './filesystem-wrappers';
// Type-safe tool calls
const content = await read_file({ path: '/tmp/test.txt' });
await write_file({ path: '/tmp/output.txt', content: 'Hello!' });Case-Agnostic Access
Server and tool names support flexible, case-agnostic access via Proxy-based fuzzy resolution. This means you don't need to remember exact naming conventions—camelCase, snake_case, and kebab-case all work interchangeably.
Server Name Resolution
All of these access patterns resolve to the same server:
// Original server name: "corp-jira"
mcp['corp-jira'] // Bracket notation with original name
mcp.corpJira // camelCase
mcp.corp_jira // snake_case
mcp.corpjira // Lowercase without separatorsTool Name Resolution
Similarly, tool names within a server support fuzzy matching:
// Original tool name: "search_jira_issues"
const server = mcp['corp-jira'];
server.search_jira_issues // Original snake_case
server.searchJiraIssues // camelCase
server.searchjiraissues // Lowercase
server['search-jira-issues'] // kebab-case via bracket notationHow It Works
The resolution uses a two-step approach:
- Fast-path: If the exact property name exists, it's returned immediately
- Fuzzy match: The requested name is normalized (lowercased, hyphens/underscores removed) and compared against normalized versions of all available names
Helpful Error Messages
When a property doesn't match any available option, a TypeError is thrown with helpful suggestions:
const server = mcp['corp-jira'];
server.nonExistentTool;
// TypeError: Property "nonExistentTool" not found on corp-jira.
// Available: search_jira_issues, create_issue, get_issue, ...mcp.unknownServer;
// TypeError: Property "unknownServer" not found on mcp.
// Available: corp-jira, github, filesystem, ...This makes it easy to discover available tools and servers when debugging or exploring the API.
Security
- Network Isolation: Sandbox only allows connections to the local bridge (e.g.,
localhost:3000) - Filesystem Isolation: Write access restricted to configured paths
- Timeout Enforcement: Code execution automatically terminated after timeout
- OS-Level Sandboxing: Uses
sandbox-exec(macOS) orbubblewrap(Linux)
Environment Variables
| Variable | Default | Description |
|----------|---------|-------------|
| SERVERS_CONFIG | ~/.meta-mcp/servers.json | Path to servers configuration |
| MCP_DEFAULT_TIMEOUT | none | Global timeout for MCP tool calls (ms) |
License
MIT
