nodejs-repl-mcp
This server provides a stateful, real Node.js REPL over MCP: you create persistent named sessions and evaluate JavaScript in them, with variables, imports, npm packages, and top-level await surviving across calls.
create_session: start a named REPL session in its own child process with isolated state and workspace.
exec: run arbitrary JS in a session, including
let/const,require, top-levelawait, and multi-line input continuation; returns output, console logs, and errors.history: review past evaluations (inputs, outputs, errors, console output) with optional limit.
list_sessions: show all sessions with PID, workspace, and liveness.
remove_session: kill a session's worker while keeping its workspace files and installed packages.
install_packages: npm install packages into a specific session's workspace so they can be required later.
Useful for AI agents that need to build up state across many small code evaluations, browser automation with preinstalled Playwright, and long-lived remote coding workbenches.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@nodejs-repl-mcpStart a REPL session and run JavaScript step by step, keeping variables between calls."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
nodejs-repl-mcp
Session-stateful Node.js REPL exposed as an MCP server, packaged as a Docker image.
Each named session keeps a real node:repl context alive in its own child process: let/const declarations, top-level await, required modules, and per-session npm packages persist across tool calls until the session is removed. Designed for AI agents and automation that need to build up state across many small evaluations — no terminal UI, no stdin scraping.
Architecture
MCP client ──stdio / streamable HTTP──> MCP server (this process)
│ session table (in-memory)
├── worker: node child process ── repl.REPLServer (own context)
├── worker: node child process ── repl.REPLServer (own context)
└── ...One worker process per session — crash isolation, independent
requirecache, killable individually.Native REPL semantics — the worker drives the server's own default evaluator (
REPLServer.prototype.eval), solet/constredeclaration, multi-line continuation (Recoverable), on-demand core-module loading, and top-levelawaitbehave exactly like the interactivenodeREPL.Runtime errors — the REPL's internal domain prints
Uncaught ...without invoking the eval callback; the worker taps its output stream and still returns a structured error, so the session survives bugs in evaluated code.Per-session workspace — each session gets
~/.nodejs-repl/sessions/<name>(override withNODEJS_REPL_SESSIONS_DIR); the worker'scwdandrequireresolution root live there.install_packagesrunsnpm installinto that directory only.
Related MCP server: jupyter-kernel-mcp
Tools
Tool | Arguments | Description |
|
| Create a persistent session ( |
|
| Evaluate JS in the session; syntax-incomplete input is buffered and continues on the next |
|
| Evaluation history (inputs, outputs, errors, captured console) |
| — | All sessions with pid, workspace, liveness |
|
| Kill the worker. Workspace dir is kept (deps/files survive) |
|
|
|
Usage
Docker (recommended)
The prebuilt image on GHCR (multi-arch: amd64 + arm64) needs no local build. MCP stdio transport needs stdin, so run with -i:
{
"mcpServers": {
"nodejs-repl": {
"command": "docker",
"args": ["run", "-i", "--rm", "ghcr.io/unfallenwill/nodejs-repl-mcp:latest"],
"volumes": ["nodejs-repl-data:/data"]
}
}
}Or with Claude Code:
claude mcp add nodejs-repl -- docker run -i --rm -v nodejs-repl-data:/data ghcr.io/unfallenwill/nodejs-repl-mcp:latestPin a release with :X.Y.Z instead of :latest for reproducible setups.
Build From Source
git clone https://github.com/unfallenwill/nodejs-repl-mcp.git
cd nodejs-repl-mcp
docker build -t nodejs-repl-mcp .Then use the config above with the local tag, e.g. args: ["run", "-i", "--rm", "nodejs-repl-mcp"]. A different Playwright version can be baked in with --build-arg PLAYWRIGHT_VERSION=x.y.z.
Releases
Pushing a tag vX.Y.Z triggers .github/workflows/publish.yml and publishes ghcr.io/unfallenwill/nodejs-repl-mcp:{X.Y.Z, X.Y, latest} (pre-release tags get the exact version only):
git tag v0.1.0 && git push origin v0.1.0The first push creates the package as private; flip it to public in the package's visibility settings for unauthenticated pulls.
Preinstalled Packages
The image ships with Playwright — package, headless Chromium build, and the system libraries it needs — so browser automation works out of the box, no install_packages required:
create_session { "name": "browser" }
exec { "name": "browser", "code": "const { chromium } = require('playwright'); const b = await chromium.launch(); const p = await b.newPage(); await p.setContent('<h1>hi</h1>'); await p.textContent('h1')" } // 'hi'The preinstalled set lives at /opt/session-deps and is exposed to sessions via NODE_PATH, so packages installed with install_packages (session-local node_modules) still take precedence. Pin a different version at build time with --build-arg PLAYWRIGHT_VERSION=x.y.z.
Local
npm install
npm run build
node dist/index.js # MCP stdio serverclaude mcp add nodejs-repl -- node /path/to/nodejs-repl-mcp/dist/index.jsHTTP Transport
Serve streamable HTTP instead of stdio — for remote workbenches, shared hosts, or clients that can't spawn a child process. REPL sessions live in the server's SessionManager, not in the MCP connection, so state survives across independent HTTP requests even though each request is served by a fresh MCP server instance.
node dist/index.js --transport http --host 127.0.0.1 --port 3000 --token my-secretOption | Env fallback | Default | Notes |
|
|
|
|
|
|
| Loopback binds get the SDK's Host/Origin DNS-rebinding guards |
|
|
| |
|
| none | Requires |
With Docker (note --host 0.0.0.0 — loopback inside the container is unreachable from outside):
docker run --rm -p 3000:3000 -v nodejs-repl-data:/data \
ghcr.io/unfallenwill/nodejs-repl-mcp:latest \
node dist/index.js --transport http --host 0.0.0.0 --port 3000 --token my-secretConnect any streamable-HTTP MCP client to http://127.0.0.1:3000/mcp; with a token configured, send it as a bearer header:
claude mcp add --transport http nodejs-repl http://127.0.0.1:3000/mcp --header "Authorization: Bearer my-secret"Use --token whenever the server is reachable beyond loopback. The server executes arbitrary code by design; a missing token on a non-loopback bind prints a warning at startup.
Example session
create_session { "name": "analysis" }
exec { "name": "analysis", "code": "let data = [3,1,2]; data.sort()" }
exec { "name": "analysis", "code": "data" } // state persists: [1,2,3]
exec { "name": "analysis", "code": "const r = await fetch('https://example.com').then(r => r.status); r" }
install_packages { "name": "analysis", "packages": ["lodash@^4"] }
exec { "name": "analysis", "code": "require('lodash').chunk([1,2,3,4], 2)" }
history { "name": "analysis" }
remove_session { "name": "analysis" }Design notes
Session-state persistence for REPLs has four known approaches (see research in .firecrawl/):
History replay (
.save/.load, Nesh) — simple, but replayingMath.random()or side-effecting statements yields wrong state.Runtime serialization — impractical in JS: closures are opaque; sockets/native handles can't be serialized.
Process snapshotting (VM/CRIU, the RunKit/Tonic approach) — perfect semantics ("time travel") but heavy infrastructure.
Session residency — keep the session process alive; state persists naturally. This is what nodejs-repl-mcp implements: the MCP server is already a long-lived process, so the coordinator layer of designs like
node-repl-clicollapses into the server itself, and each session is a detached worker with a real REPL context.
Limitations
Sessions are process state: they do not survive server restarts or container restarts (workspace directories do, via the
/datavolume).exechas no built-in timeout by design (long evaluations are legitimate). A wedged session can be killed withremove_session.Workers run with full Node.js capabilities — no sandboxing. Do not expose the server to untrusted input; for untrusted code use a container/gVM-level boundary (the
vmmodule is not a security boundary — vm2 escape). In HTTP mode, treat the endpoint as RCE-by-design: keep it on loopback or require--token.
Development
npm run build
node scripts/smoke.mjs # local stdio smoke test (22 assertions)
node scripts/smoke-http.mjs # local streamable HTTP smoke test (22 assertions)
SMOKE_CMD=docker SMOKE_ARGS="run -i --rm nodejs-repl-mcp" node scripts/smoke.mjs # against the container
SMOKE_PLAYWRIGHT=1 SMOKE_CMD=docker SMOKE_ARGS="run -i --rm nodejs-repl-mcp" node scripts/smoke.mjs # + preinstalled Playwright check
SMOKE_CMD=docker SMOKE_ARGS="run --rm -p 31007:31007 -v nodejs-repl-data:/data nodejs-repl-mcp node dist/index.js --transport http --host 0.0.0.0 --port 31007 --token smoke-secret" node scripts/smoke-http.mjs # HTTP against the containerThe HTTP smoke test covers bearer-token rejection (401 + WWW-Authenticate), the JSON-RPC/SSE stateless leg, state persistence across independent requests, the SDK client leg (skipped when devDependencies are absent, e.g. inside the image), and clean SIGTERM shutdown.
Available Tools
6 toolscreate_sessionCreate REPL sessionA
Create a named, persistent Node.js REPL session. Each session runs in its own child process with an isolated context: variables, let/const declarations, required modules and top-level await state persist across exec calls until the session is removed.
Inputs: name (required).
execEvaluate code in sessionA
Evaluate JavaScript in a named REPL session with full node:repl semantics. Syntax-incomplete input is buffered and continues on the next exec (multi-line). Returns the inspected result, captured console output, and any error. State persists between execs.
Inputs: code (required), name (required).
historySession historyA
Return the evaluation history of a session (inputs, outputs, errors, captured console) — useful for reviewing what state was built up.
Inputs: name (required), limit.
install_packagesInstall npm packages into sessionA
Run npm install in the session workspace so subsequent exec calls can require/import the packages. Installs into that session only.
Inputs: name (required), packages (required), saveDev.
list_sessionsList REPL sessionsA
List all REPL sessions with pid, workspace and liveness.
Inputs: no named parameters.
remove_sessionRemove REPL sessionA
Stop and remove a session (kills its worker process). The session workspace directory is kept, so installed dependencies and written files survive.
Inputs: name (required).
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
TDQS
Scored across 6 tools
Each tool maps to a distinct operation: session lifecycle (create/list/remove), code execution, history retrieval, and package installation. There is no meaningful overlap or risk of selecting the wrong tool.
Most tools follow a clear verb_noun pattern (create_session, list_sessions, remove_session, install_packages), but exec and history are terse single-word names that break the pattern. Overall still readable and predictable.
Six tools is a well-scoped set for managing persistent Node.js REPL sessions. Each tool serves a clear need without redundancy or bloat.
The surface covers the full lifecycle: create a session, execute code in it, inspect history, install dependencies, list sessions, and remove sessions. No obvious dead ends or missing critical operations.
Maintenance
Related MCP Connectors
Build Apps and run code in 30 languages — sandboxed, with persistent sessions for agent loops.
Persistent cloud workspaces for AI agents: run commands, edit files, use git and a browser.
Persistent Linux computers for AI agents: desktop, signed-in browser, vault, apps and shell.
A persistent Linux computer for your AI: what it installs and writes survives the session.
Related MCP Servers
- FlicenseAqualityDmaintenanceA secure Node.js execution environment that allows coding agents and LLMs to run JavaScript dynamically, install NPM packages, and retrieve results while adhering to the Model Control Protocol.7115 npm4-
- AlicenseAqualityDmaintenanceEnables AI agents to execute Python, TypeScript, and JavaScript code in persistent Jupyter kernels with stateful variables and imports across interactions.779 PyPI4MIT
- AlicenseNot gradedqualityDmaintenanceEnables creation and management of persistent REPL sessions for various languages and shells, with web-based monitoring and session recovery capabilities.32 npm3MIT
- FlicenseAqualityDmaintenanceEnables AI agents to start and manage pseudo-terminal sessions, run shell commands and interact with REPLs programmatically.7-