Goal
Enable the user to reliably hit breakpoints in VS Code, debug an MCP Server running via stdio (usually started by npx @modelcontextprotocol/inspector ... ), and have a clear troubleshooting path when encountering issues like port conflicts, missing sourcemaps, or gray breakpoints.
Quick Start (shortest usage)
You already have a build output (for example build/internal/index.js or dist/index.js), and you want to set breakpoints for a stdio MCP Server:
- Make sure sourcemaps are enabled(you should see a
.mapfile next to the build output) - In the MCP project root,
.vscode/launch.jsonadd an attach configuration - In VS Code, first start this Attach debugging
- Run in the terminal (the port must match launch.json):
npx -y @modelcontextprotocol/inspector node --inspect=9229 build/internal/index.js
If you find that "breakpoints are not hit / breakpoints are gray", first change it to:
npx -y @modelcontextprotocol/inspector node --inspect-brk=9229 build/internal/index.js
How it works (plain language)
- Node's
--inspect: it opens a "debug port" on the local machine (by default this is 9229), and VS Code can connect to control breakpoints/stepping. - MCP Inspector: it is like a "temporary MCP client + UI"; it starts your server and talks to it via stdio .
- sourcemap: maps the "built JS" back to "your TS source code", so that breakpoints you set in TS are accurate.
Output format (always give the user this template)
Please output the following 3 sections (all must be copy-pasteable):
【1) VS Code Attach 配置(launch.json 片段)】
<给出一个完整 configuration 对象,提醒用户追加到 configurations 数组里;明确 port/outFiles 需要匹配>
【2) 启动命令(Inspector + Node --inspect)】
- 默认版:<一条命令>
- 稳定版(首行暂停):<一条命令>
- 换端口版(如果 9229 占用):<一条命令>
【3) 排障清单(从最可能到最少见)】
1. ...
2. ...
3. ...
Key constraints (to avoid "it looks right but just does not work")
- Ports must match:
launch.json'sport==--inspect=PORT's PORT. - Attach first, then start the process: for the first attempt, it is recommended to use
--inspect-brk, to avoid "starting too fast and missing the breakpoint". - Debugging the build output requires sourcemaps: otherwise you can only debug in the compiled JS, and TS breakpoints will most likely be inaccurate.
Overall process (follow step by step)
flowchart TD
A[确认入口与输出目录<br/>build/ dist/ 内的 index.js] --> B[确认 sourcemap 已生成<br/>能找到 *.map]
B --> C[追加 launch.json 的 attach 配置<br/>port/outFiles 对齐]
C --> D[VS Code 先启动 Attach<br/>处于 waiting]
D --> E[运行 inspector 命令启动 server<br/>建议用 --inspect-brk]
E --> F{断点命中?}
F -- 是 --> G[开始调试:单步/变量/调用栈]
F -- 否 --> H[按清单排查:端口/路径/outFiles/map/拼写错误]
H --> D
Step 0: Confirm the entry point and output directory (to avoid writing the path wrong)
First, determine "which js file do I actually need to run":
ls -la build dist 2>/dev/null
Common entry points:
build/internal/index.js(note: many people write it asinternel, this is a spelling trap)build/index.jsdist/index.js
Step 1: Enable sourcemaps (the prerequisite for "directly debugging the build output")
1.1 TypeScript(tsconfig)
If you have a TS project,tsconfig.json must at least contain:
{
"compilerOptions": {
"sourceMap": true,
"inlineSources": true
}
}
1.2 Bundler/build tool (choose the one you use)
The core principle is one sentence:The build output must include .map。
- tsup: configure
sourcemap: true - esbuild: add to the command or configuration
sourcemap: true(or--sourcemap) - rollup:
output.sourcemap: true - webpack: set an appropriate
devtool(for examplesource-map) - tsc direct output: confirm that under
outDirthere is.js.map
Self-check (you should be able to see .map):
find build dist -maxdepth 4 -name "*.map" -print 2>/dev/null | head
If you cannot find any
.map, do not rush to debug breakpoints: turn on sourcemaps and rebuild once.
Step 2: In .vscode/launch.json Add attach configuration (most important)
In the root directory of the MCP project, add/modify .vscode/launch.json, and add the following as a new configuration(do not overwrite your existing configuration).
You usually only need to change two places:
port: the debug port you plan to use (default 9229)outFiles: whether your output directory isbuild/ordist/
{
"version": "0.2.0",
"configurations": [
{
"name": "Attach: MCP stdio (Node Inspector)",
"type": "node",
"request": "attach",
"port": 9229,
"protocol": "inspector",
"restart": true,
"timeout": 30000,
"sourceMaps": true,
"smartStep": true,
"skipFiles": ["<node_internals>/**", "**/node_modules/**"],
"cwd": "${workspaceFolder}",
"outFiles": [
"${workspaceFolder}/build/**/*.js",
"${workspaceFolder}/dist/**/*.js"
]
}
]
}
Step 3: Start Attach debugging in VS Code
- Open the Run and Debug panel
- Select
Attach: MCP stdio (Node Inspector) - Click Run (or press F5)
At this point VS Code will enter the "waiting for connection" state.
Step 4: Run the Inspector command to start the server (stdio mode)
4.1 Most commonly used (recommended)
npx -y @modelcontextprotocol/inspector node --inspect=9229 build/internal/index.js
4.2 More stable for first-time debugging (recommended to use it first)
--inspect-brk will make Node pause on the first line, ensuring that after attach succeeds you continue execution:
npx -y @modelcontextprotocol/inspector node --inspect-brk=9229 build/internal/index.js
4.3 If you use pnpm / yarn
pnpm dlx @modelcontextprotocol/inspector node --inspect=9229 build/internal/index.js
yarn dlx @modelcontextprotocol/inspector node --inspect=9229 build/internal/index.js
Step 5: Common issues (troubleshoot in this order)
5.1 Port is occupied (9229 is unavailable)
Symptom:
- The terminal reports the port is occupied
- or VS Code attach fails/connects to the wrong process
Check (commonly used on macOS / Linux):
lsof -nP -iTCP:9229 -sTCP:LISTEN
Handling (choose one of two):
- Change the port: for example, change it to
9230- Change the
launch.jsonofportto9230 - Also change the command to
--inspect=9230(or--inspect-brk=9230)
- Change the
- Kill the process occupying the port: confirm it is the process you want to terminate before killing it
5.2 Breakpoints are gray / do not stop (most common)
Prioritize these three things:
- Use
--inspect-brkto stabilize first: make the process pause on the first line - Confirm that
.mapactually exists: the output directory must contain*.map - Confirm that
outFilescovers your output directory: for example, if you are actually inbuild/, it must include${workspaceFolder}/build/**/*.js
Additional checkpoints (if you see one, it means sourcemap is working):
- Build artifact
.jshas something like this at the end//# sourceMappingURL=xxx.js.map - VS Code's Call Stack can show your source paths, rather than a bunch of minified JS
5.3 Inspector can start, but the server exits immediately
Possible reasons:
- The entry file path is wrong (
build/internel/index.jsvsbuild/internal/index.jsthis kind of spelling is most common) - The server reports an error on startup and exits directly (check the terminal output)
First do two minimal actions:
node -p "require('fs').existsSync('build/internal/index.js')"
node --inspect-brk=9229 build/internal/index.js
If the second one can run, it means the problem is more likely in the inspector command or the arguments you pass.
Examples (examples)
Example 1: Standard build artifact debugging (build output)
npx -y @modelcontextprotocol/inspector node --inspect-brk=9229 build/internal/index.js
Example 2: Port conflict, switch to 9230
launch.jsonChangeportto9230- Command:
npx -y @modelcontextprotocol/inspector node --inspect-brk=9230 build/internal/index.js
Fallback strategy (when you do not want to / cannot adjust the build artifacts)
If you cannot get sourcemap working for now (or bundling is too complex), you can first debug by "running the source code directly in development mode" (provided your project allows starting this way).
For example, use tsx:
npx -y tsx --inspect-brk=9229 src/index.ts
This approach does not use the inspector; it is suitable for first confirming that the "breakpoint/logic/flow" itself is fine, and then going back to complete the sourcemap debugging chain for the build artifacts.