dap-mcp-server 0.1.12 → 0.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +50 -10
  2. package/SKILL.md +39 -184
  3. package/build/adapters/builtin.js +25 -0
  4. package/build/adapters/builtin.js.map +1 -1
  5. package/build/adapters/detect.d.ts +12 -0
  6. package/build/adapters/detect.js +91 -0
  7. package/build/adapters/detect.js.map +1 -1
  8. package/build/adapters/recipes.d.ts +46 -0
  9. package/build/adapters/recipes.js +128 -0
  10. package/build/adapters/recipes.js.map +1 -0
  11. package/build/adapters/registry.js +35 -12
  12. package/build/adapters/registry.js.map +1 -1
  13. package/build/dap/types.d.ts +20 -0
  14. package/build/diagnostics/doctor.d.ts +70 -0
  15. package/build/diagnostics/doctor.js +268 -0
  16. package/build/diagnostics/doctor.js.map +1 -0
  17. package/build/server.js +432 -161
  18. package/build/server.js.map +1 -1
  19. package/build/session/inspect.d.ts +12 -2
  20. package/build/session/inspect.js +33 -20
  21. package/build/session/inspect.js.map +1 -1
  22. package/build/session/run.d.ts +2 -0
  23. package/build/session/run.js +13 -23
  24. package/build/session/run.js.map +1 -1
  25. package/build/session/session.d.ts +38 -2
  26. package/build/session/session.js +158 -12
  27. package/build/session/session.js.map +1 -1
  28. package/{src/test-fixtures/crash-adapter.js → build/session/state-format.d.ts} +11 -2
  29. package/build/session/state-format.js +62 -0
  30. package/build/session/state-format.js.map +1 -0
  31. package/integrations/claude/dap-debugging/.claude-plugin/plugin.json +9 -0
  32. package/integrations/claude/dap-debugging/.mcp.json +8 -0
  33. package/integrations/claude/dap-debugging/skills/dap-debugging/SKILL.md +51 -0
  34. package/package.json +8 -3
  35. package/.forgejo/workflows/publish.yml +0 -63
  36. package/CLAUDE.md +0 -71
  37. package/TODO.md +0 -32
  38. package/docs/plans/2026-03-05-dap-wrapper-mcp-design.md +0 -326
  39. package/docs/plans/2026-03-05-dap-wrapper-mcp-implementation.md +0 -2815
  40. package/src/adapters/builtin.ts +0 -75
  41. package/src/adapters/detect.test.ts +0 -36
  42. package/src/adapters/detect.ts +0 -36
  43. package/src/adapters/registry.test.ts +0 -74
  44. package/src/adapters/registry.ts +0 -86
  45. package/src/dap/dap-client.test.ts +0 -118
  46. package/src/dap/dap-client.ts +0 -588
  47. package/src/dap/protocol.test.ts +0 -82
  48. package/src/dap/protocol.ts +0 -78
  49. package/src/dap/ring-buffer.test.ts +0 -82
  50. package/src/dap/ring-buffer.ts +0 -64
  51. package/src/dap/types.ts +0 -115
  52. package/src/index.ts +0 -30
  53. package/src/integration/debug-lifecycle.test.ts +0 -131
  54. package/src/server.ts +0 -1096
  55. package/src/session/inspect.test.ts +0 -126
  56. package/src/session/inspect.ts +0 -160
  57. package/src/session/run.test.ts +0 -74
  58. package/src/session/run.ts +0 -144
  59. package/src/session/session-manager.test.ts +0 -74
  60. package/src/session/session-manager.ts +0 -67
  61. package/src/session/session.test.ts +0 -126
  62. package/src/session/session.ts +0 -482
  63. package/src/test-fixtures/mock-adapter.js +0 -164
  64. package/src/test-fixtures/no-stop-adapter.js +0 -77
  65. package/src/test-fixtures/silent-adapter.js +0 -24
  66. package/src/test-fixtures/tcp-mock-adapter.js +0 -87
  67. package/tsconfig.json +0 -16
package/README.md CHANGED
@@ -16,26 +16,42 @@ Any DAP-compliant debug adapter works. Built-in defaults for:
16
16
  |----------|---------|---------|
17
17
  | Python | debugpy | `pip install debugpy` |
18
18
  | Go | Delve | `go install github.com/go-delve/delve/cmd/dlv@latest` |
19
+ | Node.js / TypeScript | vscode-js-debug | Configure its `dapDebugServer.js` path or install it in the project |
19
20
  | C/C++ | lldb-dap | `apt install lldb` / `brew install llvm` |
21
+ | Rust | lldb-dap (same `cppdbg` adapter) | as above, plus the Rust LLDB formatters (bundled with rustup toolchains; the `rust-lldb` package on distro-packaged Rust) |
22
+ | PHP | vscode-php-debug + xdebug 3 | Install xdebug, then point the `php` adapter's `program` at `out/phpDebug.js` |
20
23
 
21
- Additional adapters (e.g. Node.js via js-debug) can be registered via the `configure_adapter` tool or config files.
24
+ Additional adapters, and project-specific paths for the built-ins, can be registered via `configure_adapter` or config files.
25
+
26
+ ### Launch recipes are built in
27
+
28
+ The per-language incantations that usually stand between an agent and its first breakpoint ship with the server:
29
+
30
+ - **PHP** — the `php` adapter's `launchDefaults` carry the xdebug `runtimeArgs` and listen port. They are merged into a user-configured `php` entry, so you only supply the `phpDebug.js` path. If PHP still runs past a breakpoint, the result carries a `hint` pointing at xdebug.
31
+ - **Rust** — sessions that break in `.rs` files load `lldb_lookup.py` from the active toolchain's sysroot, so `String`, `&str`, `Vec` and `HashMap` render as values instead of raw structs. `debug_at` defaults `program` to `target/debug/<package name>`.
32
+ - **Go** — `cwd` is mirrored to `dlvCwd` so delve builds from the module directory.
33
+ - **Java / JDWP** — `debug_doctor` prints the `-agentlib:jdwp=…` line to start the JVM with; `debug_attach` accepts `breakpoints` so they bind before a `suspend=y` VM resumes.
34
+
35
+ `debug_doctor` and `debug_at` detect the language from manifests (`pyproject.toml`, `go.mod`, `Cargo.toml`, `composer.json`, `pom.xml`, `build.gradle`, `CMakeLists.txt`, `package.json`), from the breakpoint file's extension, and — for manifest-less script directories — from the source files in `cwd` and `cwd/src`.
22
36
 
23
37
  ## MCP Tools
24
38
 
25
39
  | Tool | Description |
26
40
  |------|-------------|
27
- | `debug_inspect` | **One-shot:** launch → run to breakpoint → evaluate expressions → disconnect. Use first when you just need a value at a line. |
41
+ | `debug_doctor` | **Start here:** detect the project, recommend an adapter, verify its complete command, and return setup remediation. |
42
+ | `debug_at` | **Primary debugging call:** infer the adapter, stop at a line, evaluate expressions, and clean up. |
43
+ | `debug_inspect` | **One-shot:** launch → run to breakpoint → evaluate expressions → disconnect when the adapter is already known. |
28
44
  | `debug_run` | **One-shot:** launch → run to completion → return all output. Combine with logpoints for printf-style debugging without editing source. |
29
45
  | `debug_launch` | Start an interactive debug session (launch a program with breakpoints) |
30
- | `debug_attach` | Attach to a running process |
46
+ | `debug_attach` | Attach to a running process; pass `breakpoints` / `exceptionBreakpoints` to bind them before it resumes |
31
47
  | `debug_restart` | Restart with the same configuration |
32
48
  | `debug_disconnect` | End a debug session |
33
49
  | `set_breakpoints` | Set/replace breakpoints in a file (use `logMessage` for logpoints) |
34
50
  | `set_exception_breakpoints` | Break on exceptions |
35
51
  | `list_breakpoints` | List currently-registered breakpoints for a session |
36
- | `get_state` | Get threads, stack trace, scopes, and variables |
52
+ | `get_state` | Get threads, stack trace, scopes, and variables (`scopes: ["Registers"]` to opt into CPU registers, `detail: "compact"` for a slim view) |
37
53
  | `evaluate` | Evaluate an expression in the debuggee context |
38
- | `step` | Continue, next, stepIn, stepOut, or pause |
54
+ | `step` | Continue, next, stepIn, stepOut, or pause; answers compactly by default (`detail: "full"` for the `get_state` shape) |
39
55
  | `get_output` | Get buffered stdout/stderr |
40
56
  | `configure_adapter` | Register a custom debug adapter |
41
57
  | `list_adapters` | List available adapters |
@@ -57,7 +73,20 @@ npm run build
57
73
 
58
74
  ## Usage with Claude Code
59
75
 
60
- Add to your MCP configuration (`~/.claude/mcp.json` or project `.mcp.json`):
76
+ Register the server for the current user:
77
+
78
+ ```bash
79
+ claude mcp add --scope user dap -- npx -y dap-mcp-server
80
+ ```
81
+
82
+ The repository also contains a Claude Code plugin that bundles the MCP declaration and the activation skill. During local development, validate and load it with:
83
+
84
+ ```bash
85
+ claude plugin validate integrations/claude/dap-debugging
86
+ claude --plugin-dir ./integrations/claude/dap-debugging
87
+ ```
88
+
89
+ To configure MCP manually, use a project `.mcp.json`:
61
90
 
62
91
  ```json
63
92
  {
@@ -82,16 +111,27 @@ Or if installed globally:
82
111
  }
83
112
  ```
84
113
 
85
- A `SKILL.md` file is included in the package. Copy it to `.claude/skills/` in your project to teach Claude Code when and how to use the debugger:
114
+ A `SKILL.md` file is included in the package. After a local package install, copy it to the required skill directory layout:
86
115
 
87
116
  ```bash
88
- mkdir -p .claude/skills
89
- cp node_modules/dap-mcp-server/SKILL.md .claude/skills/dap-debugging.md
117
+ mkdir -p .claude/skills/dap-debugging
118
+ cp node_modules/dap-mcp-server/SKILL.md .claude/skills/dap-debugging/SKILL.md
90
119
  ```
91
120
 
121
+ For a global npm install, the source file is under `$(npm root -g)/dap-mcp-server/SKILL.md`, not the project's `node_modules`.
122
+
92
123
  ## Design
93
124
 
94
- Tools are deliberately coarse-grained. A single `get_state` call returns threads, stack trace, scopes, and variables — instead of requiring 4 separate calls. The `step` tool blocks synchronously until the debuggee stops again and returns the new state. This minimizes round-trips for AI agents that think in terms of "step forward and show me what happened."
125
+ Tools are deliberately coarse-grained. `debug_doctor` makes setup failures actionable and `debug_at` performs the common detect → launch → stop → inspect → cleanup workflow in one call. A single `get_state` call returns threads, stack trace, scopes, and variables. The `step` tool blocks until the debuggee stops again and returns the new state.
126
+
127
+ Responses are budgeted for an LLM's context window:
128
+
129
+ - CPU register scopes are never fetched unless asked for by name; skipped scopes are listed in `omittedScopes`.
130
+ - `step` returns a compact state: frame-local scopes only, a 5-frame stack, no thread list for single-threaded programs, minified JSON.
131
+ - A state carries only output that no earlier `debug_launch`/`step` response has reported (capped at the newest 50 entries, with `outputOmitted`); `get_output` always has the whole buffer.
132
+ - `debug_doctor` details only the selected adapter (`verbose: true` for all); a not-ready `debug_at` answers with a one-line summary.
133
+
134
+ Stops on an exception include `exception: { id, description, breakMode }`. When the program ends, `debug_run`, `debug_at`, `debug_launch` and `step` report its `exitCode` if the adapter provides one.
95
135
 
96
136
  Session IDs are optional when only one debug session is active. Breakpoints can be set inline with `debug_launch` for single-call debugging setup.
97
137
 
package/SKILL.md CHANGED
@@ -1,209 +1,64 @@
1
1
  ---
2
2
  name: dap-debugging
3
- description: Use when debugging a program — when you need to set breakpoints, step through code, inspect variables, or understand runtime behavior instead of guessing with print statements.
3
+ description: Use a real debugger for opaque test failures, crashes, swallowed exceptions, incorrect runtime state, or a bug that remains unexplained after static inspection or a failed fix. Prefer this over adding multiple print statements when actual values, branches, or call stacks are needed.
4
4
  ---
5
5
 
6
- # Debugging with DAP MCP Server
6
+ # Debug Code with DAP
7
7
 
8
- You have access to a real debugger via the `dap` MCP server. **Use it instead of adding print/log statements** whenever you need to understand runtime behavior, track down a bug, or verify a fix.
8
+ Use the `dap` MCP server as the default next step when static evidence is insufficient. It can stop a local program at a source line, inspect stack frames and variables, evaluate expressions, and collect output without editing the source.
9
9
 
10
- ## When to switch from print() to the debugger
10
+ Do not invoke a debugger for syntax errors, obvious type errors, or failures already explained completely by a short traceback. Debugging executes local code and may trigger the program's normal side effects; do not run untrusted targets.
11
11
 
12
- Reach for the debugger — don't keep guessing — when any of these are true:
12
+ ## Preferred Workflow
13
13
 
14
- - **Your first fix attempt was wrong.** Stop iterating on guesses. Inspect actual values.
15
- - **You're about to add 3+ print statements.** Use a logpoint instead (see below) — same information, no code edits.
16
- - **You need a value on iteration N of a loop.** Use `hitCondition: "N"` instead of conditional prints.
17
- - **A test fails with a non-obvious traceback.** Launch the test under the debugger; you'll see locals at the failure site.
18
- - **You're reasoning about state that's modified across many call sites.** A conditional breakpoint on the value beats grepping for assignments.
19
-
20
- Print debugging is fine for: trivial one-line checks, scripts you'll throw away in the next message, or when you genuinely just want to see "did this code path execute?".
21
-
22
- ## Logpoints — print debugging without editing code
23
-
24
- A **logpoint** is a breakpoint that doesn't pause execution. It just emits a formatted message every time it's hit. This is the single most useful feature for AI agents.
25
-
26
- Set it via the `logMessage` field on a breakpoint:
27
-
28
- ```
29
- set_breakpoints({
30
- file: "/abs/path/src/foo.py",
31
- breakpoints: [
32
- { line: 42, logMessage: "x={x} y={y} state={self.state}" },
33
- { line: 88, logMessage: "iteration {i}: items={len(items)}" }
34
- ]
35
- })
36
- ```
37
-
38
- Then `step({ action: "continue" })` and let the program run. Retrieve every log with `get_output`. You get the same information as inserting `print(f"x={x} y={y}")` at five places — without touching the source file, without re-running, and with the program's natural execution preserved.
39
-
40
- **When to use logpoints over breakpoints**: when you want to *observe* values across many hits rather than *interrupt* execution to inspect one. They are the right tool ~70% of the time.
41
-
42
- Expression syntax inside `{...}` depends on the adapter (debugpy: Python; node: JS). The expression is evaluated in the breakpoint's scope.
43
-
44
- ## Debugging a failing test
45
-
46
- This is the dominant case. Use the test runner as the program, with the specific test as an argument.
47
-
48
- ### pytest
49
-
50
- ```
51
- debug_launch({
52
- adapter: "python",
53
- program: "/abs/path/.venv/bin/pytest",
54
- args: ["tests/test_foo.py::test_bar", "-x", "--no-header"],
55
- cwd: "/abs/path",
56
- breakpoints: [{ file: "/abs/path/src/foo.py", line: 42 }],
57
- stopOnEntry: false,
58
- })
59
- ```
60
-
61
- For pytest, also consider setting `exceptionBreakpoints: ["raised"]` — pytest swallows exceptions before you see them, but the debugger will stop at the raise site.
62
-
63
- ### vitest / jest (Node.js)
64
-
65
- Requires the `node` adapter configured (see "Node.js setup" below). Use the runner binary from `node_modules/.bin`:
14
+ 1. Call `debug_doctor` with the project `cwd`. It detects project signals, recommends an adapter, validates the complete adapter command, and returns exact remediation if setup is incomplete.
15
+ 2. Select an executable code line close to where the first incorrect value is observable. Avoid comments, declarations without executable code, and lines that the failing path never reaches.
16
+ 3. Call `debug_at` with the source `file`, `line`, and only the expressions needed to test the current hypothesis. Omit `adapter` unless doctor recommends overriding detection.
66
17
 
67
- ```
68
- debug_launch({
69
- adapter: "node",
70
- program: "${cwd}/node_modules/.bin/vitest",
71
- args: ["run", "tests/foo.test.ts", "-t", "the failing test name"],
72
- cwd: "/abs/path",
73
- breakpoints: [{ file: "/abs/path/src/foo.ts", line: 42 }],
74
- stopOnEntry: false,
75
- })
76
- ```
77
-
78
- ### go test
79
-
80
- ```
81
- debug_launch({
82
- adapter: "go",
83
- program: "${cwd}", // delve takes a package dir, not a binary
84
- args: ["-test.run", "TestFoo", "-test.v"],
85
- cwd: "/abs/path",
86
- breakpoints: [{ file: "/abs/path/foo.go", line: 42 }],
87
- stopOnEntry: false,
88
- })
18
+ ```json
19
+ {
20
+ "cwd": "/workspace/project",
21
+ "program": "/workspace/project/app.py",
22
+ "file": "/workspace/project/app.py",
23
+ "line": 42,
24
+ "expressions": ["request.user", "result", "len(items)"]
25
+ }
89
26
  ```
90
27
 
91
- Delve uses `dlv test` semantics under the hood when the program is a package directory containing test files.
28
+ Interpret the result before changing code:
92
29
 
93
- ### Node.js setup
30
+ - `hit: true` includes the stopped location, evaluated values, and captured output.
31
+ - `status: terminated` means execution finished before reaching the line; verify the program, arguments, path, and branch.
32
+ - `timedOut: true` means it neither stopped nor terminated in time; inspect output and narrow the target.
33
+ - With `hit: false`, `breakpoint.verified: false` means the adapter could not bind that source location; move to executable code or verify source mapping.
94
34
 
95
- If `list_adapters` shows no `node` entry, register it once (path depends on installed extension):
35
+ Use `launchConfig` for adapter-specific DAP fields such as a module, request type, runtime executable, or source-map option.
96
36
 
97
- ```
98
- configure_adapter({
99
- type: "node",
100
- runtime: "node",
101
- program: "/abs/path/to/js-debug/src/dapDebugServer.js",
102
- args: [],
103
- scope: "global",
104
- })
105
- ```
106
-
107
- `js-debug-dap` ships with the VS Code Node debugger extension. Alternatively use `@vscode/js-debug` from npm.
37
+ ## Failing Tests
108
38
 
109
- ## Quick Start (single-file program)
39
+ Run the smallest failing test, not the full suite. Pass the test runner as `program` (or through adapter-specific `launchConfig`) and the test selector in `args`. Test-runner launch semantics vary by adapter, so use `debug_doctor` first and preserve known-good fields from `.vscode/launch.json` in `launchConfig`.
110
40
 
111
- 1. `list_adapters` — see what's installed.
112
- 2. Launch with breakpoints in one call:
41
+ Choose a breakpoint in application code before the assertion or exception boundary. For exceptions hidden by a test framework, use an interactive launch and `set_exception_breakpoints`.
113
42
 
114
- ```
115
- debug_launch({
116
- adapter: "python",
117
- program: "/abs/path/file.py",
118
- stopOnEntry: false,
119
- breakpoints: [{ file: "/abs/path/file.py", line: 42 }],
120
- })
121
- ```
122
-
123
- 3. The response already contains the full state (threads, stack, scopes, variables) when the breakpoint is hit. No follow-up `get_state` needed.
124
- 4. Step with `step({ action: "next" })` — response includes the new state.
125
- 5. `evaluate({ expression: "len(items)" })` for ad-hoc inspection.
126
- 6. `debug_disconnect` when done.
127
-
128
- ## Stepping strategy
129
-
130
- - **`next`** (step over) is the default choice. Use it unless you have a specific reason not to.
131
- - **`stepIn`** only when you suspect the bug is *inside* the function being called — not at the call site. Stepping into library code will get you lost fast.
132
- - **`stepOut`** to bail out of a function you stepped into by mistake.
133
- - **`continue`** to run to the next breakpoint. With logpoints set, this is how you collect runtime traces.
134
-
135
- If you find yourself stepping line-by-line for more than ~10 steps, you're probably doing it wrong. Set a breakpoint at the next interesting location and continue to it.
136
-
137
- ## Worked example
138
-
139
- Bug: `compute_total(items)` returns `0` when items contain a tax-exempt entry.
140
-
141
- ```
142
- # Launch under pytest with a breakpoint at the suspect line
143
- debug_launch({
144
- adapter: "python",
145
- program: "/proj/.venv/bin/pytest",
146
- args: ["tests/test_billing.py::test_tax_exempt", "-x"],
147
- cwd: "/proj",
148
- breakpoints: [{ file: "/proj/src/billing.py", line: 57 }],
149
- stopOnEntry: false,
150
- })
151
-
152
- # Response (paraphrased):
153
- # {
154
- # sessionId: "s-1",
155
- # status: "stopped",
156
- # location: { file: ".../billing.py", line: 57, functionName: "compute_total" },
157
- # scopes: [{ name: "Locals", variables: [
158
- # { name: "items", value: "[{...}, {...}]", ... },
159
- # { name: "subtotal", value: "100", ... },
160
- # { name: "tax", value: "0", ... }
161
- # ] }]
162
- # }
163
-
164
- # Inspect the exempt item more closely
165
- evaluate({ expression: "[i for i in items if i.get('tax_exempt')]" })
166
- # → [{name: 'book', price: 100, tax_exempt: True}]
167
-
168
- # Step over to see what compute_total actually returns
169
- step({ action: "next" })
170
- # → see that `total = subtotal + tax` returns 100, not 0. Bug is upstream.
171
-
172
- # Continue to verify nothing else changes the return
173
- step({ action: "continue" })
174
-
175
- debug_disconnect()
176
- ```
43
+ ## Interactive Exploration
177
44
 
178
- Fix locally, re-run the test, done.
45
+ Use `debug_launch` only when one-shot inspection is insufficient, such as multiple stops or step-by-step control. With breakpoints, it waits by default and explicitly reports stopped, terminated, or timeout.
179
46
 
180
- ## Key Rules
47
+ 1. `debug_launch` with breakpoints and optional `launchConfig`.
48
+ 2. Inspect the returned state; do not immediately duplicate it with `get_state`.
49
+ 3. Use `evaluate` for a focused question.
50
+ 4. Use `step` with `next` by default; use `stepIn` only when the called function is suspect.
51
+ 5. Use `set_breakpoints` to move or add breakpoints.
52
+ 6. Always finish with `debug_disconnect`.
181
53
 
182
- - **Do NOT call `get_state` after `step`** — `step` already returns the new stopped state.
183
- - **Omit `sessionId`** when only one debug session is active.
184
- - **Use absolute paths** for `program` and breakpoint `file` parameters.
185
- - **Prefer `stopOnEntry: false` with breakpoints** — it gets you directly to the interesting code.
186
- - **Use `get_output`** for program stdout/stderr (including logpoint output).
54
+ `step` answers compactly: frame-local scopes, a short stack, and only output not reported before. Call `get_state` when globals, a deeper stack, or `variableDepth` > 1 are needed; CPU registers are fetched only with `scopes: ["Registers"]`. Read the whole output buffer with `get_output`.
187
55
 
188
- ## Debugging Strategies
56
+ To debug a process that is already running or was started suspended (JVM with JDWP `suspend=y`, a server), use `debug_attach` and pass `breakpoints` in that same call so they bind before the process resumes.
189
57
 
190
- ### Narrowing down a bug
191
- 1. Set a breakpoint where state is known good.
192
- 2. Set another where state is wrong.
193
- 3. Use `step` with `next` to walk between them, or set intermediate logpoints.
58
+ Omit `sessionId` when exactly one session is active. Use absolute paths for programs and source files.
194
59
 
195
- ### Understanding a crash
196
- 1. Launch with `exceptionBreakpoints: ["uncaughtExceptions"]` (or `"raised"` for any raise).
197
- 2. When stopped at the exception, inspect stack and locals.
60
+ ## Output-First Debugging
198
61
 
199
- ### Verifying a fix
200
- 1. Set a breakpoint at the fixed code.
201
- 2. Run to it, inspect variables, confirm new behavior.
202
- 3. Continue to verify the program completes successfully.
62
+ Use `debug_run` when completion status and stdout/stderr are sufficient. For repeated observations without pausing, create logpoints with `set_breakpoints` and `logMessage`, continue execution, then read `get_output`. This provides runtime traces without modifying source files.
203
63
 
204
- ### Observing a hot loop without stopping it
205
- 1. Set logpoints at the relevant lines with `{i}` and any state expression.
206
- 2. `continue`.
207
- 3. Read the trace via `get_output`. For long-running programs, poll incrementally:
208
- pass the `nextSince` value from each call as the `since` parameter of the next call
209
- to receive only newly-emitted entries.
64
+ If doctor reports an unavailable adapter, follow its `nextSteps` before retrying. Do not repeatedly launch a configuration that readiness checks have already rejected.
@@ -68,5 +68,30 @@ export const BUILTIN_ADAPTERS = [
68
68
  args: [],
69
69
  installHint: "apt install lldb (Debian/Ubuntu) or brew install llvm (macOS)",
70
70
  },
71
+ // php adapter: vscode-php-debug's phpDebug.js. Like node, there is no
72
+ // universal install path, so the user points `program` at it; the
73
+ // launchDefaults below still apply to that override (the registry merges
74
+ // them across tiers).
75
+ {
76
+ type: "php",
77
+ runtime: "node",
78
+ program: "phpDebug.js",
79
+ args: [],
80
+ // The adapter listens on `port` and xdebug connects back to it. Without
81
+ // these runtimeArgs PHP runs to completion before xdebug ever connects,
82
+ // so no breakpoint is hit.
83
+ launchDefaults: {
84
+ port: 9003,
85
+ hostname: "127.0.0.1",
86
+ runtimeArgs: [
87
+ "-dxdebug.mode=debug",
88
+ "-dxdebug.start_with_request=yes",
89
+ "-dxdebug.client_host=127.0.0.1",
90
+ "-dxdebug.client_port=9003",
91
+ ],
92
+ },
93
+ installHint: "Install xdebug 3 and vscode-php-debug, then set the adapter's `program` to its out/phpDebug.js path " +
94
+ "(e.g. via `configure_adapter`). See https://github.com/xdebug/vscode-php-debug.",
95
+ },
71
96
  ];
72
97
  //# sourceMappingURL=builtin.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"builtin.js","sourceRoot":"","sources":["../../src/adapters/builtin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,MAAM,CAAC,MAAM,gBAAgB,GAAoB;IAC/C;QACE,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,SAAS;QAClB,OAAO,EAAE,IAAI;QACb,IAAI,EAAE,CAAC,iBAAiB,CAAC;QACzB,cAAc,EAAE;YACd,OAAO,EAAE,iBAAiB;SAC3B;QACD,WAAW,EAAE,qBAAqB;KACnC;IACD;QACE,IAAI,EAAE,IAAI;QACV,OAAO,EAAE,KAAK;QACd,IAAI,EAAE,CAAC,KAAK,EAAE,sBAAsB,CAAC;QACrC,SAAS,EAAE,KAAK;QAChB,sDAAsD;QACtD,WAAW,EAAE,yCAAyC;QACtD,WAAW,EAAE,qDAAqD;KACnE;IACD,2EAA2E;IAC3E,2EAA2E;IAC3E,2CAA2C;IAC3C;QACE,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,MAAM;QACf,OAAO,EAAE,mBAAmB;QAC5B,wEAAwE;QACxE,qEAAqE;QACrE,YAAY;QACZ,IAAI,EAAE,CAAC,GAAG,EAAE,WAAW,CAAC;QACxB,SAAS,EAAE,KAAK;QAChB,yEAAyE;QACzE,WAAW,EAAE,iCAAiC;QAC9C,wEAAwE;QACxE,2CAA2C;QAC3C,cAAc,EAAE;YACd,IAAI,EAAE,UAAU;YAChB,OAAO,EAAE,QAAQ;SAClB;QACD,WAAW,EACT,mGAAmG;YACnG,mFAAmF;KACtF;IACD;QACE,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,UAAU;QACnB,IAAI,EAAE,EAAE;QACR,WAAW,EAAE,+DAA+D;KAC7E;CACF,CAAC"}
1
+ {"version":3,"file":"builtin.js","sourceRoot":"","sources":["../../src/adapters/builtin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,MAAM,CAAC,MAAM,gBAAgB,GAAoB;IAC/C;QACE,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,SAAS;QAClB,OAAO,EAAE,IAAI;QACb,IAAI,EAAE,CAAC,iBAAiB,CAAC;QACzB,cAAc,EAAE;YACd,OAAO,EAAE,iBAAiB;SAC3B;QACD,WAAW,EAAE,qBAAqB;KACnC;IACD;QACE,IAAI,EAAE,IAAI;QACV,OAAO,EAAE,KAAK;QACd,IAAI,EAAE,CAAC,KAAK,EAAE,sBAAsB,CAAC;QACrC,SAAS,EAAE,KAAK;QAChB,sDAAsD;QACtD,WAAW,EAAE,yCAAyC;QACtD,WAAW,EAAE,qDAAqD;KACnE;IACD,2EAA2E;IAC3E,2EAA2E;IAC3E,2CAA2C;IAC3C;QACE,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,MAAM;QACf,OAAO,EAAE,mBAAmB;QAC5B,wEAAwE;QACxE,qEAAqE;QACrE,YAAY;QACZ,IAAI,EAAE,CAAC,GAAG,EAAE,WAAW,CAAC;QACxB,SAAS,EAAE,KAAK;QAChB,yEAAyE;QACzE,WAAW,EAAE,iCAAiC;QAC9C,wEAAwE;QACxE,2CAA2C;QAC3C,cAAc,EAAE;YACd,IAAI,EAAE,UAAU;YAChB,OAAO,EAAE,QAAQ;SAClB;QACD,WAAW,EACT,mGAAmG;YACnG,mFAAmF;KACtF;IACD;QACE,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,UAAU;QACnB,IAAI,EAAE,EAAE;QACR,WAAW,EAAE,+DAA+D;KAC7E;IACD,sEAAsE;IACtE,kEAAkE;IAClE,yEAAyE;IACzE,sBAAsB;IACtB;QACE,IAAI,EAAE,KAAK;QACX,OAAO,EAAE,MAAM;QACf,OAAO,EAAE,aAAa;QACtB,IAAI,EAAE,EAAE;QACR,wEAAwE;QACxE,wEAAwE;QACxE,2BAA2B;QAC3B,cAAc,EAAE;YACd,IAAI,EAAE,IAAI;YACV,QAAQ,EAAE,WAAW;YACrB,WAAW,EAAE;gBACX,qBAAqB;gBACrB,iCAAiC;gBACjC,gCAAgC;gBAChC,2BAA2B;aAC5B;SACF;QACD,WAAW,EACT,sGAAsG;YACtG,iFAAiF;KACpF;CACF,CAAC"}
@@ -19,4 +19,16 @@
19
19
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
20
  * SOFTWARE.
21
21
  */
22
+ import { AdapterConfig } from "../dap/types.js";
22
23
  export declare function checkAdapterAvailable(command: string): Promise<boolean>;
24
+ export interface AdapterAvailability {
25
+ available: boolean;
26
+ command: string;
27
+ reason?: string;
28
+ }
29
+ /**
30
+ * Validate the complete adapter command, not just its runtime. This prevents
31
+ * configurations such as `node missing-adapter.js` from being reported as
32
+ * available merely because Node itself is installed.
33
+ */
34
+ export declare function inspectAdapterAvailability(config: AdapterConfig): Promise<AdapterAvailability>;
@@ -20,6 +20,8 @@
20
20
  * SOFTWARE.
21
21
  */
22
22
  import { execFile } from "node:child_process";
23
+ import fs from "node:fs/promises";
24
+ import path from "node:path";
23
25
  import { promisify } from "node:util";
24
26
  const execFileAsync = promisify(execFile);
25
27
  export async function checkAdapterAvailable(command) {
@@ -32,4 +34,93 @@ export async function checkAdapterAvailable(command) {
32
34
  return false;
33
35
  }
34
36
  }
37
+ async function fileExists(filePath) {
38
+ try {
39
+ await fs.access(filePath);
40
+ return true;
41
+ }
42
+ catch {
43
+ return false;
44
+ }
45
+ }
46
+ /**
47
+ * Validate the complete adapter command, not just its runtime. This prevents
48
+ * configurations such as `node missing-adapter.js` from being reported as
49
+ * available merely because Node itself is installed.
50
+ */
51
+ export async function inspectAdapterAvailability(config) {
52
+ const command = config.runtime || config.program;
53
+ if (config.transport === "tcp") {
54
+ if (!config.portMatcher) {
55
+ return {
56
+ available: false,
57
+ command,
58
+ reason: "TCP adapters require a portMatcher with one capture group for the listening port.",
59
+ };
60
+ }
61
+ try {
62
+ new RegExp(config.portMatcher);
63
+ }
64
+ catch {
65
+ return {
66
+ available: false,
67
+ command,
68
+ reason: `The configured portMatcher is not a valid regular expression: ${config.portMatcher}`,
69
+ };
70
+ }
71
+ }
72
+ if (!(await checkAdapterAvailable(command))) {
73
+ return {
74
+ available: false,
75
+ command,
76
+ reason: `Executable '${command}' was not found on PATH.`,
77
+ };
78
+ }
79
+ if (!config.runtime) {
80
+ return { available: true, command };
81
+ }
82
+ if (config.program === "-m") {
83
+ const moduleName = config.args?.[0];
84
+ if (!moduleName) {
85
+ return {
86
+ available: false,
87
+ command,
88
+ reason: "The adapter uses '-m' but no module name is configured.",
89
+ };
90
+ }
91
+ try {
92
+ await execFileAsync(config.runtime, [
93
+ "-c",
94
+ "import importlib.util,sys; sys.exit(0 if importlib.util.find_spec(sys.argv[1]) else 1)",
95
+ moduleName,
96
+ ]);
97
+ return { available: true, command: `${config.runtime} -m ${moduleName}` };
98
+ }
99
+ catch {
100
+ return {
101
+ available: false,
102
+ command: `${config.runtime} -m ${moduleName}`,
103
+ reason: `Python module '${moduleName}' is not installed for '${config.runtime}'.`,
104
+ };
105
+ }
106
+ }
107
+ const looksLikeScript = path.isAbsolute(config.program) ||
108
+ /[\\/]/.test(config.program) ||
109
+ /\.(?:cjs|js|mjs|py|jar)$/i.test(config.program);
110
+ if (!looksLikeScript) {
111
+ return { available: true, command };
112
+ }
113
+ const resolved = path.resolve(config.program);
114
+ if (await fileExists(resolved)) {
115
+ return { available: true, command: `${config.runtime} ${resolved}` };
116
+ }
117
+ if (await checkAdapterAvailable(config.program)) {
118
+ return { available: true, command: `${config.runtime} ${config.program}` };
119
+ }
120
+ return {
121
+ available: false,
122
+ command: `${config.runtime} ${config.program}`,
123
+ reason: `Adapter program '${config.program}' was not found.`,
124
+ };
125
+ }
35
126
  //# sourceMappingURL=detect.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"detect.js","sourceRoot":"","sources":["../../src/adapters/detect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAE1C,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,OAAe;IACzD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;QAClE,MAAM,aAAa,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;QACzC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"detect.js","sourceRoot":"","sources":["../../src/adapters/detect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Copyright (c) 2026 Ivan Iraci <ivan.iraci@professioneit.com>
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+ import { AdapterConfig } from "../dap/types.js";
23
+ export interface LaunchArgOptions {
24
+ program?: string;
25
+ args?: string[];
26
+ cwd?: string;
27
+ env?: Record<string, string>;
28
+ /** Adapter-specific launch fields, merged below the explicit common options. */
29
+ launchConfig?: Record<string, unknown>;
30
+ stopOnEntry: boolean;
31
+ /** Source files the session will break in; selects language-specific recipes. */
32
+ sourceFiles?: string[];
33
+ }
34
+ /**
35
+ * Build the DAP launch arguments shared by debug_launch, debug_inspect/debug_at
36
+ * and debug_run. Merge order: adapter launchDefaults < launchConfig < explicit
37
+ * common fields, then per-adapter recipes that a caller would otherwise have
38
+ * to know by heart.
39
+ */
40
+ export declare function buildLaunchArgs(config: AdapterConfig, opts: LaunchArgOptions, resolveRustSysroot?: () => Promise<string | undefined>): Promise<Record<string, unknown>>;
41
+ /** `target/debug/<package name>` of the Cargo project in cwd, once it has been built. */
42
+ export declare function defaultCargoProgram(cwd: string): Promise<string | undefined>;
43
+ /** Why a program may have run to completion without the debugger ever binding a breakpoint. */
44
+ export declare function launchFailureHint(adapterType: string): string | undefined;
45
+ /** How to get a first stop with this language/adapter; surfaced by debug_doctor. */
46
+ export declare function adapterUsageNote(languageOrAdapter: string): string | undefined;