Gathering detailed insights and metrics for interpreter-tools
Gathering detailed insights and metrics for interpreter-tools
Gathering detailed insights and metrics for interpreter-tools
Gathering detailed insights and metrics for interpreter-tools
automata-tools
automata (FSM) parser/interpreter/visualiser
librechat-code-interpreter-mcp-server
A Model Context Protocol (MCP) server that wraps the [LibreChat Code Interpreter API](https://code.librechat.ai/docs), exposing code-execution and file-management endpoints as MCP tools. Easily integrate sandboxed code execution into any MCP-compatible cl
metaes
JavaScript (ECMAScript) in JavaScript interpreter for better tools.
intrear
Next-gen way to create your own programming language!
npm install interpreter-tools
Typescript
Module System
Node Version
NPM Version
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
Run AI-generated code on your own machine—locally, securely, at lightning speed.
Vercel AI tool provided out of the box.
Interpreter Tools is a drop-in "code-interpreter" backend for AI agents: it spins up lightweight Docker containers, executes untrusted snippets in < 100 ms (with pooling), streams the output, and can be extended to any language by registering a new config object.
Supports pooling, per-session containers, dependency caching, and real-time stdout/stderr—perfect for chat-based tools like GPT function calling, Jupyter-style notebooks, or autonomous agents that need to evaluate code on the fly.
The most powerful way to experience Interpreter Tools is through our interactive shell example. It provides a chat-like interface where you can:
Try it out:
1# Clone the repository 2git clone https://github.com/CatchTheTornado/interpreter-tools.git 3cd interpreter-tools 4 5# Install dependencies 6yarn install 7 8# Run the interactive shell 9yarn ts-node examples/interactive-shell-example.ts
Here are some examples showcasing different capabilities of Interpreter Tools:
⚡ Sub-100 ms average execution (with container pool & dep-cache). Run untrusted code fast without leaving Node!
🔌 Plug-in language architecture – add a new language by registering one object (see LanguageRegistry
). No engine edits required.
📦 Zero-install repeat runs – dependencies are installed once per container and skipped thereafter, saving seconds on every call.
🔒 Docker-level isolation – each snippet executes in its own constrained container (CPU, memory, no-new-privileges).
🖥️ Real-time streaming – stdout/stderr stream back instantly; ideal for REPL-like experiences.
Interpreter Tools is published on npm as interpreter-tools
.
Install the package and its dependencies in your Node.js project:
1# Using yarn 2yarn add interpreter-tools ai @ai-sdk/openai 3 4# Or using npm 5npm install interpreter-tools ai @ai-sdk/openai
example.js
in your project:1const { generateText } = require('ai'); 2const { openai } = require('@ai-sdk/openai'); 3const { createCodeExecutionTool } = require('interpreter-tools'); 4 5async function main() { 6 try { 7 // Create a code execution tool instance 8 const { codeExecutionTool } = createCodeExecutionTool(); 9 10 // Use generateText with codeExecutionTool to generate and execute code 11 const result = await generateText({ 12 model: openai('gpt-4'), 13 maxSteps: 10, 14 messages: [ 15 { 16 role: 'user', 17 content: 'Write a JavaScript function that calculates the sum of numbers from 1 to n and print the result for n=10. Make sure to include a test case.' 18 } 19 ], 20 tools: { codeExecutionTool }, 21 toolChoice: 'auto' 22 }); 23 24 console.log('AI Response:', result.text); 25 console.log('Execution Results:', result.toolResults); 26 27 } catch (error) { 28 console.error('Error:', error); 29 } 30} 31 32main();
1# Using yarn 2yarn add dotenv 3 4# Or using npm 5npm install dotenv
Create a .env
file in your project root:
1OPENAI_API_KEY=your_api_key_here
1require('dotenv').config(); 2// ... rest of the code remains the same
1node example.js
If you prefer to use the ExecutionEngine directly without the AI integration, here's how to do it:
direct-example.js
:1const { ExecutionEngine, ContainerStrategy } = require('interpreter-tools'); 2 3async function main() { 4 const engine = new ExecutionEngine(); 5 6 try { 7 // Create a session with per-execution strategy 8 const sessionId = await engine.createSession({ 9 strategy: ContainerStrategy.PER_EXECUTION, 10 containerConfig: { 11 image: 'node:18-alpine', 12 environment: { 13 NODE_ENV: 'development' 14 } 15 } 16 }); 17 18 // Execute JavaScript code 19 const result = await engine.executeCode(sessionId, { 20 language: 'javascript', 21 code: ` 22const numbers = [1, 2, 3, 4, 5]; 23const sum = numbers.reduce((a, b) => a + b, 0); 24const average = sum / numbers.length; 25 26console.log('Numbers:', numbers); 27console.log('Sum:', sum); 28console.log('Average:', average); 29 `, 30 streamOutput: { 31 stdout: (data) => console.log('Container output:', data), 32 stderr: (data) => console.error('Container error:', data) 33 } 34 }); 35 36 console.log('Execution Result:'); 37 console.log('STDOUT:', result.stdout); 38 console.log('STDERR:', result.stderr); 39 console.log('Exit Code:', result.exitCode); 40 console.log('Execution Time:', result.executionTime, 'ms'); 41 42 } catch (error) { 43 console.error('Error:', error); 44 } finally { 45 // Clean up resources 46 await engine.cleanup(); 47 } 48} 49 50main();
1node direct-example.js
This example demonstrates:
If you're using TypeScript, you can import the packages with type definitions:
1import { generateText } from 'ai'; 2import { openai } from '@ai-sdk/openai'; 3import { createCodeExecutionTool } from 'interpreter-tools'; 4import 'dotenv/config'; 5 6// Rest of the code remains the same
Interpreter Tools can support any language that has a runnable Docker image—just register a LanguageConfig
at runtime.
1import { LanguageRegistry, LanguageConfig } from 'interpreter-tools'; 2 3const rubyConfig: LanguageConfig = { 4 language: 'ruby', 5 defaultImage: 'ruby:3.2-alpine', 6 codeFilename: 'code.rb', 7 prepareFiles: (options, dir) => { 8 const fs = require('fs'); 9 const path = require('path'); 10 fs.writeFileSync(path.join(dir, 'code.rb'), options.code); 11 }, 12 buildInlineCommand: () => ['sh', '-c', 'ruby code.rb'], 13 buildRunAppCommand: (entry) => ['sh', '-c', `ruby ${entry}`] 14}; 15 16// Make the engine aware of Ruby 17LanguageRegistry.register(rubyConfig); 18 19// From this point you can use `language: 'ruby'` in `executeCode` or the AI tool.
See examples/ruby-example.ts
for a full working script that:
ruby:3.2-alpine
containerTo set up the project for local development:
1# Clone the repository 2git clone https://github.com/CatchTheTornado/interpreter-tools.git 3cd interpreter-tools 4 5# Install dependencies 6yarn install
The /examples
directory contains several example scripts demonstrating different use cases of the interpreter tools:
examples/ai-example.ts
Demonstrates how to:
Run it with:
1yarn ts-node examples/ai-example.ts
examples/basic-usage.ts
Shows how to:
Run it with:
1yarn ts-node examples/basic-usage.ts
examples/python-example.ts
Demonstrates how to:
Run it with:
1yarn ts-node examples/python-example.ts
examples/python-chart-example.js
Demonstrates how to:
Run it with:
1node examples/python-chart-example.js
examples/python-json-sort-example.js
Demonstrates how to:
Run it with:
1node examples/python-json-sort-example.js
examples/shell-json-example.ts
Demonstrates how to:
jq
Run it with:
1yarn ts-node examples/shell-json-example.ts
examples/nodejs-project-example.ts
Shows how to:
Run it with:
1yarn ts-node examples/nodejs-project-example.ts
examples/shell-example.ts
A simple example that:
Run it with:
1yarn ts-node examples/shell-example.ts
examples/ruby-example.ts
Shows how to:
Run it with:
1yarn ts-node examples/ruby-example.ts
examples/benchmark-pool.ts
– JavaScript/TypeScript pool benchmark (20 rounds)
1yarn ts-node examples/benchmark-pool.ts
examples/benchmark-pool-python.ts
– Python pool benchmark
1yarn ts-node examples/benchmark-pool-python.ts
Average times on a MacBook M2 Pro: JS 40 ms / round, Python 60 ms / round after first run (deps cached).
The main components of this project are:
ExecutionEngine
: Manages code execution in containersSessionManager
: Manages session state, container metadata, and container history across executionsContainerManager
: Handles Docker container lifecycleCodeExecutionTool
: Provides a high-level interface for executing code1import { createCodeExecutionTool } from 'interpreter-tools'; 2 3const { codeExecutionTool } = createCodeExecutionTool(); 4 5// Isolated workspace (default) 6const result = await codeExecutionTool.execute({ 7 language: 'javascript', 8 code: 'console.log("Hello, World!");', 9 streamOutput: { 10 stdout: (data) => console.log(data), 11 stderr: (data) => console.error(data) 12 } 13}); 14 15// Shared workspace between executions 16const result2 = await codeExecutionTool.execute({ 17 language: 'javascript', 18 code: 'console.log("Hello again!");', 19 workspaceSharing: 'shared' // Share workspace between executions 20});
The workspaceSharing
option controls how workspaces are managed:
isolated
(default): Each execution gets its own workspaceshared
: All executions in a session share the same workspace, allowing file persistence between runsThis is particularly useful when you need to:
Interpreter Tools is composed of four core layers:
Layer | Responsibility |
---|---|
ExecutionEngine | High-level façade that orchestrates container lifecycle, dependency caching, and code execution. |
SessionManager | Manages session state, container metadata, and container history across executions. |
ContainerManager | Low-level wrapper around Dockerode that creates, starts, pools, and cleans containers. |
LanguageRegistry | Pluggable store of LanguageConfig objects that describe how to build/run code for each language. |
All user-facing helpers (e.g. the codeExecutionTool
for AI agents) are thin wrappers that forward to ExecutionEngine
.
1graph TD; 2 subgraph Runtime 3 A[ExecutionEngine] 4 B[SessionManager] 5 C[ContainerManager] 6 D[Docker] 7 end 8 E[LanguageRegistry] 9 A --> B --> C --> D 10 A --> E
ExecutionEngine
1new ExecutionEngine() 2 3createSession(config: SessionConfig): Promise<string> 4executeCode(sessionId: string, options: ExecutionOptions): Promise<ExecutionResult> 5cleanupSession(sessionId: string): Promise<void> 6cleanup(): Promise<void> 7getSessionInfo(sessionId: string): Promise<SessionInfo>
ContainerStrategy
(PER_EXECUTION
, POOL
, PER_SESSION
) and passes a containerConfig
(image, env, mounts, limits).{ stdout, stderr, dependencyStdout, dependencyStderr, exitCode, executionTime }
.dependencyStdout
and dependencyStderr
capture any output produced while installing the declared dependencies
(e.g. npm
, pip
, apk
) before your code starts executing. They are empty when no dependency phase was required.1interface SessionInfo { 2 sessionId: string; 3 config: SessionConfig; 4 currentContainer: { 5 container: Docker.Container | undefined; 6 meta: ContainerMeta | undefined; 7 }; 8 containerHistory: ContainerMeta[]; 9 createdAt: Date; 10 lastExecutedAt: Date | null; 11 isActive: boolean; 12}
Each container's state is tracked through the ContainerMeta
interface:
1interface ContainerMeta { 2 sessionId: string; 3 depsInstalled: boolean; 4 depsChecksum: string | null; 5 baselineFiles: Set<string>; 6 workspaceDir: string; 7 generatedFiles: Set<string>; 8 sessionGeneratedFiles: Set<string>; 9 isRunning: boolean; 10 createdAt: Date; 11 lastExecutedAt: Date | null; 12 containerId: string; 13 imageName: string; // docker image tag used 14 containerName: string; // friendly name assigned by ExecutionEngine 15}
This metadata provides:
createCodeExecutionTool()
Factory that exposes the engine as an OpenAI function-calling-friendly tool. It validates parameters with Zod and returns the same ExecutionResult
.
1import { createCodeExecutionTool } from 'interpreter-tools'; 2 3const { codeExecutionTool } = createCodeExecutionTool(); 4const { stdout } = await codeExecutionTool.execute({ 5 language: 'python', 6 code: 'print("Hello")' 7});
LanguageRegistry
1LanguageRegistry.get(name): LanguageConfig | undefined 2LanguageRegistry.register(config: LanguageConfig): void 3LanguageRegistry.names(): string[]
LanguageConfig
fields:
1interface LanguageConfig { 2 language: string; // identifier 3 defaultImage: string; // docker image 4 codeFilename: string; // filename inside /workspace for inline code 5 prepareFiles(options, dir): void; // write code + metadata into temp dir 6 buildInlineCommand(depsInstalled): string[]; 7 buildRunAppCommand(entry, depsInstalled): string[]; 8 installDependencies?(container, options): Promise<void>; // optional pre-exec hook 9}
LanguageConfig
object (see the Ruby example).LanguageRegistry.register(config)
once at startup.No changes to ExecutionEngine
are required.
Strategy | Description | When to use |
---|---|---|
PER_EXECUTION | New container per snippet; removed immediately. | Maximum isolation; slowest. |
POOL | Containers are pooled per session and reused—workspace is wiped between runs. | Best latency / resource trade-off for chat bots. |
PER_SESSION | One dedicated container for the whole session; not pooled. | Long-running interactive notebooks. |
Note
ContainerStrategy.POOL
is optimised for fast startup: it re-uses pre-warmed containers and therefore always clears/workspace
between executions.
TheworkspaceSharing: "shared"
option is not available in this mode.
If you need persistent state you can either:
- switch to PER_SESSION (keeps the internal workspace), or
- mount a host directory via
containerConfig.mounts
to share specific data/files across pooled runs.
containerConfig
accepts:
1{ 2 image?: string; // overrides language default 3 mounts?: ContainerMount[]; // { type: 'directory' | 'tmpfs', source, target } 4 environment?: Record<string, string>; 5 cpuLimit?: string; // e.g. '0.5' 6 memoryLimit?: string; // e.g. '512m' 7}
ExecutionOptions
let you override CPU and memory for a single run:
1await engine.executeCode(id, { 2 language: 'python', 3 code: 'print(\"hi\")', 4 cpuLimit: '0.5', // half a CPU core 5 memoryLimit: '256m' // 256 MB RAM 6});
Under the hood the engine calls container.update({ CpuPeriod, CpuQuota, Memory })
just before execution, so the limits apply even when pooling.
Pass streamOutput: { stdout?, stderr?, dependencyStdout?, dependencyStderr? }
in ExecutionOptions
to receive data chunks in real time while the process runs.
1await engine.executeCode(id, { 2 language: 'shell', 3 code: 'for i in 1 2 3; do echo $i; sleep 1; done', 4 streamOutput: { 5 stdout: (d) => process.stdout.write(d) 6 } 7});
dependencyStdout
/ dependencyStderr
fire during the dependency-installation phase (e.g. when pip install
or npm install
runs) before the user code starts. This lets you surface progress logs or errors related to package installation separately from your program's own output.
Sometimes your code needs additional assets (datasets, JSON files, images, etc.). There are two primary ways to make them available inside the container:
Mount a host directory – supply a mounts
array in containerConfig
when you create a session. This is the easiest way to share many files or large directories.
1const sessionId = await engine.createSession({ 2 strategy: ContainerStrategy.PER_EXECUTION, 3 containerConfig: { 4 image: 'python:3.11-alpine', 5 mounts: [ 6 { 7 type: 'directory', // always "directory" for now 8 source: path.resolve(__dirname, 'assets'), // host path 9 target: '/workspace/assets' // inside the container 10 } 11 ] 12 } 13});
Programmatically copy / create individual files – use one of the helper methods that work after you have a session:
1// (a) copy an existing file from the host 2await engine.copyFileIntoWorkspace(sessionId, './input/data.json', 'data.json'); 3 4// (b) create a new file from a base64-encoded string 5await engine.addFileFromBase64( 6 sessionId, 7 'notes/hello.txt', 8 Buffer.from('hi there').toString('base64') 9);
Both helpers write directly to /workspace
, so your script can reference the files with just the relative path.
Interpreter Tools automatically tracks new files created in /workspace
during an execution:
ExecutionResult.generatedFiles
– list of absolute paths to the files created in that run.ExecutionResult.workspaceDir
– host path of the temporary workspace directory.Example:
1const result = await engine.executeCode(sessionId, { 2 language: 'python', 3 code: 'with open("report.txt", "w") as f:\n f.write("done")', 4}); 5 6console.log('New files:', result.generatedFiles);
You can retrieve file contents with:
1const pngBuffer = await engine.readFileBinary(sessionId, 'charts/plot.png'); 2fs.writeFileSync('plot.png', pngBuffer);
By default, calling engine.cleanupSession()
or engine.cleanup()
removes the containers and their workspaces. Pass true
to keep only the files that were detected as generated:
1await engine.cleanupSession(sessionId, /* keepGenerated = */ true);
All non-generated files are removed, the generated ones stay on disk so you can move or process them later. If you mounted a host directory, the files are already on the host and no special flag is needed.
Happy hacking!
MIT
No vulnerabilities found.
No security vulnerabilities found.