A basic Model Context Protocol implementation for Node.js that provides tools to execute Node.js scripts, manage npm packages, run servers, and handle Node.js version management via NVM
NodeRunner has 11 tools with explicit registration, Zod schemas, and descriptions. Naming is generally action-verb-first (run-, start-, list-, get-, select-, fetch-), which follows conventions. Most tools have parameter descriptions and input schemas. However, several issues reduce the score: (1) Output schemas are not documented, tool responses are not specified, forcing LLMs to guess what fields they'll receive; (2) Error handling is inconsistent, some tools return structured error objects, others return raw exceptions; (3) Some parameters lack constraints (e.g., 'timeout' has no min/max bounds, 'args' arrays are unbounded); (4) Permission gate implementation relies on askPermission() but there's no evidence of scope declarations or audit trails; (5) Tool descriptions are terse (mostly 8-15 words) and lack 'when to use this' context; (6) Several tools that modify state (run-npm-script, run-npm-install, start-node-server, stop-server, select-node-version, run-node-script, run-node-eval) do not explicitly document their mutations in descriptions, violating the command-tool pattern. On the positive side: all 11 tools have names starting with action verbs, all have descriptions (albeit brief), all have input schemas with Zod validation, and the codebase shows defensive validation (checking file existence before execution).
Fetch documentation for an npm module, including README and metadata
Get the version of Node.js the scripts will be executed with
Get available Node.js versions installed via NVM
List all running Node.js servers started via MCP
Execute JavaScript code directly with Node.js eval. Optionally specify a directory to execute in.
Execute a Node.js script file locally
Execute npm install to install all dependencies or a specific package
No documented output schemas. Tool responses return untyped content arrays, forcing LLMs to infer structure. run-npm-script returns stdout/stderr as text; run-npm-install returns success/error messages; start-node-server returns a server ID. None of these are formally documented, violating the 'document output schema' requirement.
Destructive/mutation tools lack explicit state-change documentation in descriptions. run-npm-install installs packages; run-npm-script executes code; start-node-server spawns background processes; stop-server terminates processes; run-node-script and run-node-eval execute arbitrary code. Descriptions do not clearly state 'this modifies state' or 'this has side effects', violating the command-tool pattern.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | D | 56 | 2026-07-28+ | v2 |
| 2026-03-09 | D | 58 | - | v1 |
Execute an npm script from package.json
Select a specific Node.js version to use for subsequent script executions
Start a Node.js server that continues running in the background
Stop a running Node.js server
Parameters lack value constraints. 'timeout' (numeric) has no min/max specified; 'args' arrays are unbounded; 'version' string in select-node-version has no enum or pattern (accepts free-form strings). The description states 'e.g. v18.20.5, system, lts/*' but does not prevent invalid versions.
Error handling is inconsistent. Some tools return {isError: true, content: ...}; others rely on exception throws caught in top-level try-catch. The code shows execError extraction but there's no unified error classification (retryable vs. user-fixable vs. fatal) or recovery guidance (e.g., 'User not found. Try search_users().'). npm install failures lack actionable messages.
Permission gate implementation uses askPermission() but provides no scope declarations (e.g., 'requires exec:script' or 'requires write:npm'). Per the security pattern, tools should declare permissions. Additionally, no audit trail is visible, LLMs won't know what scopes they were granted or what actions were logged.
Tool descriptions are terse and lack context. Average length ~50 chars (below the 194-char baseline). Descriptions like 'Execute an npm script from package.json' and 'List all running Node.js servers started via MCP' are functional but don't explain when to use each tool, dependencies, or what happens next. E.g., does list-servers return server IDs suitable for stop-server? Unclear from the description alone.
start-node-server accepts arbitrary nodeArgs and args (unbounded arrays). No validation shown to prevent injection attacks or runaway arguments. Per the tool-gateway pattern, all agent-provided input should be sanitized against command injection.
run-node-eval accepts arbitrary JavaScript code and an 'evalDirectory' parameter. The description says '(must be an allowed directory)' but the code does not show an allowlist implementation. This is a critical security risk, arbitrary code execution without validation invites prompt injection attacks.
list-servers returns an optional 'showLogs' boolean that 'includes recent logs' but no pagination. If a server has megabytes of logs, the response could explode token count. Per the paginated-result pattern, list/search tools should cap results and offer pagination.
fetch-npm-docs returns README and metadata but no output schema documented. The response structure (is it JSON? plain text? HTML?) is unknown. If the README is large, token count could be excessive. Per the strip-api-responses pattern, irrelevant fields should be stripped and results capped.