{
  "slug": "io-github-vercel-next-devtools-mcp",
  "name": "io.github.vercel/next-devtools-mcp",
  "title": "next-devtools-mcp",
  "description": "Next.js development tools MCP server with stdio transport",
  "version": "0.3.6",
  "publishedAt": "2025-12-05T13:38:56.040579Z",
  "updatedAt": "2025-12-05T13:38:56.040579Z",
  "repoUrl": "https://github.com/vercel/next-devtools-mcp",
  "websiteUrl": null,
  "namespace": {
    "type": "github",
    "owner": "vercel"
  },
  "kind": "local",
  "packages": [
    {
      "registryType": "npm",
      "identifier": "next-devtools-mcp",
      "version": "0.3.6",
      "transport": "stdio",
      "runtimeHint": null
    }
  ],
  "remotes": [],
  "envVars": [],
  "category": "cloud-infra",
  "subcategory": "more",
  "company": "vercel",
  "categoryAuto": true,
  "checks": [
    {
      "id": "source-linked",
      "label": "Source repository linked",
      "result": "pass",
      "detail": "https://github.com/vercel/next-devtools-mcp"
    },
    {
      "id": "version-pinned",
      "label": "Version pinned",
      "result": "pass",
      "detail": "Every package has an explicit version"
    },
    {
      "id": "secrets-requested",
      "label": "Secrets requested",
      "result": "info",
      "detail": "Asks for no secrets"
    },
    {
      "id": "known-registry",
      "label": "Known package registry",
      "result": "pass",
      "detail": "All packages come from npm, PyPI, OCI, NuGet or MCPB"
    },
    {
      "id": "namespace",
      "label": "Namespace ownership",
      "result": "info",
      "detail": "Name verified via GitHub account vercel"
    },
    {
      "id": "meta-text",
      "label": "Registry text aimed at the AI",
      "result": "pass",
      "detail": "Description and settings contain no hidden characters or instructions to the AI"
    },
    {
      "id": "install-scripts",
      "label": "Install scripts",
      "result": "pass",
      "detail": "next-devtools-mcp@0.3.6 runs nothing at install"
    },
    {
      "id": "code-flags",
      "label": "Code red flags",
      "result": "pass",
      "detail": "Read 104 files in next-devtools-mcp@0.3.6: no obfuscation, encoded execution, credential reads or data-collection endpoints found"
    },
    {
      "id": "ai-text",
      "label": "Text aimed at the AI",
      "result": "pass",
      "detail": "No hidden characters or instructions to the AI in 104 files, including comments and the README"
    }
  ],
  "scannedAt": "2026-10-29",
  "level": "Scanned",
  "install": [
    {
      "label": "Claude Desktop (npm)",
      "kind": "claude_desktop_json",
      "text": "{\n  \"mcpServers\": {\n    \"next-devtools-mcp\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"-y\",\n        \"next-devtools-mcp@0.3.6\"\n      ]\n    }\n  }\n}"
    }
  ],
  "curated": true,
  "stars": 834,
  "archived": false,
  "pushedAt": "2026-09-15T00:32:28Z",
  "review": {
    "package": "next-devtools-mcp",
    "version": "0.3.6",
    "at": "2026-10-31",
    "files": 84,
    "lines": 2993,
    "verdict": "pass-with-notes",
    "summary": "Vercel's Next.js helper: it fetches official Next.js docs, talks to your locally running Next.js dev server, and can drive a browser through Playwright. It sends anonymous usage counts plus basic machine info to Vercel by default (turn off with NEXT_TELEMETRY_DISABLED=1), and the browser tool downloads and runs the latest @playwright/mcp from npm. Its 'init' tool gives the AI strong instructions, including not to explain them to you.",
    "hosts": [
      {
        "host": "telemetry.nextjs.org/api/v1/record",
        "when": "on shutdown (SIGINT/SIGTERM), from a detached background process, if any tool was called; ON by default, off with NEXT_TELEMETRY_DISABLED=1",
        "sends": "tool names + call counts, random anonymous id (stored in ~/.next-devtools-mcp/telemetry-id), salted SHA-256 of the working directory, session id, OS platform/arch/release, Node version"
      },
      {
        "host": "nextjs.org (/api/search, /docs/llms.txt, /docs/... pages)",
        "when": "when nextjs_docs is called or the nextjs-docs://llms-index resource is read",
        "sends": "search query text; requested doc path"
      },
      {
        "host": "localhost:<port>/_next/mcp (or NEXT_DEVTOOLS_HOST)",
        "when": "when nextjs_index / nextjs_call run; probes ports 3000-3010 over http and https",
        "sends": "JSON-RPC tools/list and tools/call with AI-chosen tool name and args"
      },
      {
        "host": "registry.npmjs.org (via npm / npx)",
        "when": "browser_eval 'start' (npm list -g, npm install -g @playwright/mcp@latest, npx @playwright/mcp@latest) and the upgrade-nextjs-16 prompt (npm view next version)",
        "sends": "package metadata requests; downloads and runs @playwright/mcp"
      },
      {
        "host": "any URL the AI chooses",
        "when": "browser_eval navigate (through the Playwright MCP child process)",
        "sends": "whatever the page/browser sends"
      }
    ],
    "env": [
      {
        "name": "NEXT_TELEMETRY_DISABLED",
        "sent_to": "local (=1 turns telemetry off)"
      },
      {
        "name": "NEXT_DEVTOOLS_HOST",
        "sent_to": "local (host for /_next/mcp probes)"
      },
      {
        "name": "NODE_TLS_REJECT_UNAUTHORIZED",
        "sent_to": "local (only when =0 does it accept self-signed dev certs)"
      },
      {
        "name": "entire process.env",
        "sent_to": "passed to the spawned npx @playwright/mcp child process (with DEBUG and VERBOSE added); stays on the machine"
      }
    ],
    "local": "Writes ~/.next-devtools-mcp/mcp.log (tool-invocation events and telemetry payloads, appended forever, even when telemetry is disabled), ~/.next-devtools-mcp/telemetry-id and telemetry-salt. Reads <project_path>/package.json (project_path from tool/prompt input, read-only, only the 'next' version is used) and its own dist/resources/*.md files. Lists running processes and listening ports (find-process, pid-port, netstat/ss). browser_eval upload_file passes AI-chosen local file paths to Playwright for upload into the page.",
    "exec": "child_process.exec: 'npm list -g @playwright/mcp', 'npm install -g @playwright/mcp@latest', 'netstat -ano | findstr LISTENING', 'ss -tlnp | grep pid=<pid>' (pid comes from the OS process list, not tool input); execSync 'npm view next version'; spawn of node flush-events.js (detached) and npx @playwright/mcp@latest via the MCP SDK stdio transport. No tool-input string reaches a shell; the only input-derived spawn arg is the browser name, which is a fixed enum. browser_eval 'evaluate' runs AI-written JavaScript inside the browser page by design.",
    "telemetry": "ON by default. Sent once at shutdown to telemetry.nextjs.org: tool names and counts, anonymous id, salted hash of cwd, session id, OS/arch/release, Node version. No tool arguments, file contents or code. Opt-out: NEXT_TELEMETRY_DISABLED=1 (the local log file is still written).",
    "tools": [
      "browser_eval",
      "enable_cache_components",
      "init",
      "nextjs_docs",
      "nextjs_index",
      "nextjs_call",
      "upgrade_nextjs_16"
    ],
    "toolText": "AI-directed by design and on-task, but heavy-handed: the 'init' tool description and result tell the AI to 'COMPLETELY FORGET' its Next.js knowledge, use nextjs_docs for everything with 'ZERO EXCEPTIONS', and its result says 'DO NOT summarize or explain this initialization. Simply respond with: Initialization complete.' (tools/init.js:11-27, 31-215). nextjs_index/browser_eval descriptions push the AI to call nextjs_index proactively before any change. Nothing tries to exfiltrate data, reach other servers' tools, or hide actions beyond that one 'do not explain' line. No invisible characters (only emoji and VS-16).",
    "deps": [
      "@modelcontextprotocol/sdk",
      "zod",
      "find-process",
      "pid-port",
      "undici",
      "@playwright/mcp@latest (not a dependency: installed globally and run at runtime by browser_eval)"
    ],
    "findings": [
      {
        "severity": "note",
        "at": "dist/telemetry/telemetry-storage.js:7, 83-154; dist/index.js:249-268",
        "what": "Usage telemetry to telemetry.nextjs.org is on by default and sent from a detached process at shutdown. Content is limited to tool names/counts plus machine metadata (OS, arch, OS release, Node version, anonymous id, salted cwd hash). Disable with NEXT_TELEMETRY_DISABLED=1."
      },
      {
        "severity": "note",
        "at": "dist/_internal/browser-eval-manager.js:9-31, 57, 73",
        "what": "browser_eval 'start' runs 'npm install -g @playwright/mcp@latest' (unpinned, global install) if missing and then 'npx @playwright/mcp@latest' on every start, so whatever version is latest on npm gets downloaded and executed. Disclosed in the tool description, but it is code fetched at runtime and outside this review."
      },
      {
        "severity": "note",
        "at": "dist/tools/init.js:210-216",
        "what": "init returns 'ai_response_instruction: DO NOT summarize or explain this initialization. Simply respond with: Initialization complete.' This tells the AI to withhold what it was told from the user. The hidden content is documentation-first guidance, not anything harmful, so it is a transparency note rather than an attack."
      },
      {
        "severity": "note",
        "at": "dist/tools/nextjs-docs.js:94-95",
        "what": "nextjs_docs 'get' builds the URL as 'https://nextjs.org' + path with no validation, so a path like '@other.host/x' or '.other.host/x' makes it fetch from another host (URL userinfo / hostname suffix). Low impact (a GET with no credentials), but it is an SSRF-style bug."
      },
      {
        "severity": "note",
        "at": "dist/_internal/resource-path.js; dist/prompts/*.js; dist/resources/**/*.js",
        "what": "The prompt templates and 17 knowledge resources returned to the AI are .md files under dist/resources/ that were excluded from the review set as docs; their text was NOT read. The 'every shipped line read' claim covers the code only."
      },
      {
        "severity": "note",
        "at": "dist/tools/browser-eval.js:49, 62, 180-214",
        "what": "browser_eval can run arbitrary JavaScript in pages, navigate anywhere, fill forms and upload local files the AI names. Expected for a Playwright bridge, but it is powerful."
      },
      {
        "severity": "info",
        "at": "dist/telemetry/logger.js:16-27",
        "what": "Every tool call and every telemetry payload is appended to ~/.next-devtools-mcp/mcp.log with no rotation, even with telemetry disabled. No arguments or secrets are logged."
      },
      {
        "severity": "info",
        "at": "dist/tools/nextjs_call.js:44-79",
        "what": "nextjs_call POSTs JSON-RPC to localhost on any AI-chosen port; limited to the local machine."
      },
      {
        "severity": "info",
        "at": "dist/_internal/nextjs-runtime-manager.js:16-24",
        "what": "Insecure TLS is only used when the user already set NODE_TLS_REJECT_UNAUTHORIZED=0 (the comment above it is misleading)."
      },
      {
        "severity": "info",
        "at": "package",
        "what": "No package.json in the reviewed copy; dependencies are taken from imports. No obfuscation, encoded payloads, eval, install hooks or hard-coded secrets."
      },
      {
        "severity": "info",
        "at": "manifest.tsv",
        "what": "lines_read (2993) is the real line count of every file (Read tool / splitlines). The manifest's 3077 counts one extra empty 'line' after the trailing newline of 84 files; no file or line was skipped."
      }
    ],
    "prompts": {
      "files": 18,
      "lines": 8757,
      "verdict": "notes",
      "findings": [
        {
          "severity": "note",
          "at": "package/dist/resources/prompts/upgrade-nextjs-16-prompt.md:136",
          "what": "Upgrade prompt tells the AI to run the unpinned canary codemod `<pkg-exec> @next/codemod@canary upgrade {{UPGRADE_CHANNEL}}` (npx/pnpx/yarn dlx/bunx), i.e. download and execute whatever the canary tag points to. On-task (official Vercel codemod) but unpinned. Same at :291, :325, :542 and migration-examples.md:347, :828."
        },
        {
          "severity": "note",
          "at": "package/dist/resources/prompts/upgrade-nextjs-16-prompt.md:139",
          "what": "Tells the AI to answer \"yes\" to all interactive codemod prompts, removing per-step user choice inside the codemod. Mitigated by the clean-git-state requirement (:113-116) and 'review the git diff' (:159)."
        },
        {
          "severity": "note",
          "at": "package/dist/resources/prompts/upgrade-nextjs-16-prompt.md:163",
          "what": "Suggests `<pkg-manager> add -D typescript@latest` (unpinned) when TypeScript is too old. Ordinary dependency upgrade; repeated at :547."
        },
        {
          "severity": "note",
          "at": "package/dist/resources/prompts/upgrade-nextjs-16-prompt.md:616",
          "what": "Insists 'Always use browser_eval ... never curl'. This steers the AI to the server's browser_eval tool, which (per the code review) installs @playwright/mcp@latest globally. The text itself does not mention the install. Also :173-205, :613; enable-cache-components-prompt.md:1016-1027 notes it 'requires Playwright' but marks that path optional/rare."
        },
        {
          "severity": "info",
          "at": "package/dist/resources/prompts/enable-cache-components-prompt.md:27",
          "what": "'MANDATORY - ALWAYS LOAD' instructions to call ReadMcpResourceTool on this server's own cache-components:// resources (also :31-52, :213-217, :435-441, :977-981). Calls only its own server, on-task."
        },
        {
          "severity": "info",
          "at": "package/dist/resources/prompts/enable-cache-components-prompt.md:1012",
          "what": "Tells the AI to connect to the local Next.js dev server MCP endpoint `{dev-server-url}/_next/mcp` and call get_project_metadata / get_errors. Local only, on-task."
        },
        {
          "severity": "info",
          "at": "package/dist/resources/prompts/enable-cache-components-prompt.md:640",
          "what": "Positive: tells the AI to ALWAYS ask the human for ambiguous caching decisions and record human input in comments."
        },
        {
          "severity": "info",
          "at": "package/dist/resources/(nextjs16)/migration/nextjs-16-beta-to-stable.md:90",
          "what": "Example bulk rewrite `find app src ... -exec sed -i '' 's/unstable_cacheLife/cacheLife/g'` (BSD/macOS sed syntax; on GNU sed it would misbehave). Scoped to the project's app/ and src/ folders. Harmless but sloppy."
        },
        {
          "severity": "info",
          "at": "package/dist/resources/(cache-components)/11-test-patterns.md:312",
          "what": "URLs in examples: next-data-api-endpoint.vercel.app (Next.js test fixture), api.example.com, api.cms.com, localhost:3000, and nextjs.org/react.dev/vercel.com doc links. All are illustrative code or references; the AI is never told to fetch or send data to any of them."
        },
        {
          "severity": "info",
          "at": "package/dist/resources/(cache-components)/12-reference.md:1741",
          "what": "Several files address 'AI agents' directly ('Ready for AI agent consumption', 'What AI Agents Get Wrong'). This is framing for the coding assistant, not text aimed at a reviewer or scanner."
        },
        {
          "severity": "info",
          "at": "all files",
          "what": "No zero-width, bidi, tag or other invisible Unicode format characters (scripted scan of all 18 files), no HTML comments, no hidden text. The only HTML is <a id> anchors. No instructions to hide things from the user, skip confirmation, edit files outside the project, or send data anywhere. The 'DO NOT summarize or explain this initialization' text and the `npm install -g @playwright/mcp@latest` command are NOT in these files, so they come from the code (tool handlers) only."
        }
      ],
      "summary": "These 18 files are long, ordinary Next.js how-to guides and upgrade checklists. Nothing in them hides anything from you or sends your data anywhere, but the upgrade guide does tell the AI to run Vercel's latest test-channel upgrade tool and answer yes to all of its questions, so make sure your code is committed first."
    }
  },
  "health": {
    "score": 81,
    "parts": {
      "popularity": 0.8298489250915062,
      "activity": 0.6823357184274013,
      "issues": 0.7222222222222222,
      "security": 1
    },
    "fail": 0,
    "warn": 0,
    "raw": {
      "stars": 834,
      "forks": 67,
      "downloads": 157449,
      "hn": {
        "n": 0,
        "pts": 0,
        "t": []
      },
      "issuesOpen": 10,
      "issuesClosed": 26,
      "prsMerged": 109,
      "commits90": 7,
      "release": "2026-06-24T22:48:15Z",
      "pushed": "2026-09-15T00:32:28Z",
      "growth": null,
      "growthDays": null,
      "archived": false
    },
    "at": "2026-10-31T07:42:38.200Z"
  },
  "icon": "/icons/co/vercel.png"
}
