JavaScript VM
# JS-VM-MCP-Server
## Usage
```json
{
"mcpServers": {
"JavaScript VM": {
"command": "npx",
"args": [
"--allow-git",
"all",
"-y",
"github:Quicksilver0218/JS-VM-MCP-Server"
]
}
}
}
```
## Tools
There are 2 available tools:
- `run_javascript` — runs the snippet as-is.
- `run_typescript` — compiles the snippet with [esbuild](https://esbuild.github.io/) (`loader: "ts"`, `target: "esnext"`), then runs the emitted JavaScript through the exact same path as `run_javascript`.
Both VM tools behave identically; `run_typescript` just strips the types off first.
### Run
Executes the submitted snippet inside a fresh [`isolated-vm`](https://github.com/laverdet/isolated-vm) V8
isolate and returns the captured `stdout`, `stderr`, the top-level `return` value and any failure.
The first line of the response is always `Code execution finished in #.### seconds.`.
Per call:
| Limit | Value |
| --- | --- |
| V8 heap | 512 MB |
| Wall clock | 30 seconds |
| Buffered output | 262,144 characters per stream |
The isolate is a separate heap with no access to the host process, so there is no `require`,
`process`, timer or file-system API. Each call gets a brand new isolate, so nothing is
shared between runs. The snippet is evaluated as the body of an async function, which means
top-level `await` and top-level `return` both work. `import`/`export` module syntax is not
supported in either tool. `setTimeout` is unavailable.
### fetch
The isolate has no network stack of its own, so `fetch` is bridged to the host, which performs
the request and copies the buffered response back in:
| Limit | Value |
| --- | --- |
| Timeout per request | 10 seconds (rejects with `TimeoutError`) |
| Request/response body | 8,388,608 bytes |
Only `http:` and `https:` URLs are allowed and the host sends no cookies. A `Blob` or `FormData` can
be the fetch body: a Blob sends its bytes under its MIME type and a FormData is sent as multipart.
`Request` does not exist.
`run_typescript` caveats:
- Type annotations, interfaces, type aliases and generics are erased, and line numbers in runtime
stack traces stay exact.
- `enum`, `namespace` and constructor parameter properties emit new code, which can push a runtime
stack trace past the line you wrote.
- esbuild reports compile errors on the line numbers you submitted.
TDQS
Scored across 2 tools
The two tools are distinguished primarily by language (JavaScript vs TypeScript), which is a clear and predictable selection criterion. However, TypeScript is close to a superset of JavaScript, so a plain JS snippet often runs fine in either tool, leaving a narrow band of genuine overlap.
Both names follow the same verb_noun snake_case pattern (run_javascript, run_typescript) with a single shared verb and a language noun. The naming is fully predictable and symmetric.
Two tools is on the thin side, but the server's scope is narrowly 'execute code in a sandbox,' and one tool per supported language is a defensible, minimal surface. No tool feels redundant or padded.
Core execution, output capture, error reporting, and fetch are covered, which is the full lifecycle for a stateless code sandbox. Gaps are deliberate constraints rather than omissions: no session/state persistence, no module resolution, no package installation, and no way to pass structured inputs into a run.