Skip to main content
Glama
README.md
# 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.

### fetch

The isolate has no network stack of its own, so `fetch` is bridged to the host process, which
performs the actual request and copies the buffered response back into the isolate:

| Limit | Value |
| --- | --- |
| Timeout per request | 15 seconds (rejects with `TimeoutError`) |
| Request/response body | 8,388,608 bytes |

Only `http:` and `https:` URLs are allowed and the host sends no cookies. `fetch`, `Headers`,
`Response`, `AbortController`, `TextEncoder` and `TextDecoder` are minimal shims:
`response.text()`/`json()`/`arrayBuffer()`/`clone()` work, but `response.body` is `null`
(no streams, no `blob()`), `URL`/`Request`/`FormData`/`Blob` do not exist, and the isolate has
no timers, so `setTimeout` and `AbortSignal.timeout` are unavailable.

`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

A4.2/5.0

Scored across 2 tools

Disambiguation4/5

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.

Naming Consistency5/5

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.

Tool Count4/5

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.

Completeness4/5

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.

Maintenance

ActivityMaintained
ResponsivenessNo issues