Skip to content

Commit b947888

Browse files
authored
Add script debugger MCP tools, CLI REPL, and RPC mode (#395)
* Add script debugger support to MCP tools, CLI, and docs - Add 13 MCP debug tools (debug_start_session, debug_set_breakpoints, debug_wait_for_stop, debug_get_stack, debug_get_variables, debug_evaluate, debug_continue, debug_step_over/into/out, debug_end_session, debug_list_sessions, debug_capture_at_breakpoint) to CARTRIDGES, SCAPI, and STOREFRONTNEXT toolsets - Add ServerContext for persistent server-scoped state across MCP tool invocations (debug sessions, future log watches) - Add DebugSessionRegistry with TTL cleanup and multi-session support - Add `b2c debug cli` command with interactive REPL and --rpc mode for JSONL-over-stdio headless/agent integration - Add resolveBreakpointPath utility in SDK for flexible path normalization (server paths, local paths, cartridge-prefixed paths) - Add debug command docs and b2c-debug agent skill * Add MCP diagnostics tools doc page and sidebar sub-group Group debug tools on a single Diagnostics page instead of individual pages. Add Diagnostics sub-heading under MCP Tools in the sidebar for future tools like log tailing. * Group MCP tools sidebar by use case Organize into Cartridges, SCAPI, PWA Kit, Storefront Next, and Diagnostics sub-groups. * Collapse MCP tools sidebar groups by default * Clarify that debug_wait_for_stop and debug_capture_at_breakpoint block Make it explicit in tool descriptions and docs that these tools block until a breakpoint is hit or timeout expires, and that the caller must trigger a request externally while waiting. * Improve MCP debug tools based on real-world agent feedback 1. capture_at_breakpoint: add trigger_url parameter so the tool can fire the HTTP request itself after arming the breakpoint, avoiding the blocking coordination problem with LLM tool-calling. 2. set_breakpoints: surface warnings when a path cannot be round-trip mapped back to a local file. Set verified=false for unmapped paths instead of silently accepting them. 3. Tool descriptions: steer toward the non-blocking workflow (set_breakpoints → trigger → list_sessions → inspect) rather than leading with blocking tools. 4. list_sessions: include active breakpoints with resolved server paths so agents can inspect what's armed on the server. 5. start_session: return cartridge_mappings (name → local path) so agents can verify cartridge discovery and path mapping. * Fix resolveBreakpointPath treating absolute local paths as server paths Absolute local paths like /Users/.../app_mysite/cartridge/controllers/Loyalty.js start with / and were returned as-is (treated as server paths) before the source mapper had a chance to map them. Now the source mapper runs first — if it matches, the local path is correctly converted to a server path. The / passthrough only applies if the mapper doesn't match. * Exclude diagnostics tools from MCP coverage threshold The new debug tools have no unit tests yet (they require mocking the DebugSessionManager and SDAPI). Excluding from coverage until tests are added to avoid blocking the draft PR. * Add tests for diagnostics tools (session registry + tool handlers) Tests cover DebugSessionRegistry (register, duplicate rejection, get, destroy, halt waiter cleanup) and tool handlers (list_sessions, end_session, continue, get_stack, evaluate). Diagnostics tools excluded from coverage threshold until full coverage is added. * Fix lint errors in diagnostics test files * Remove debugger tools from STOREFRONTNEXT toolset * Add comprehensive tests for diagnostics tools, remove coverage exclusion Covers all 13 debug tool handlers including set_breakpoints (path mapping + warnings), get_variables (scope filtering, object_path, truncation), wait_for_stop (immediate halt, timeout, waiter resolution), capture_at_breakpoint (full orchestration, trigger_url, eval errors, timeout), start_session (real fixture cartridge + stubbed manager), and all step_* variants. Registry tests cover idle cleanup and disconnect error swallowing. Coverage is now 99.81% statements/lines, 94.65% branches, 98.94% functions — meeting all thresholds without excluding diagnostics. * Refactor debug MCP tools: shared projections, registry helpers, doc updates Tier 1 cleanup based on PR review feedback (patricksullivansf, yhsieh1) and self-review. DRY: - Extract shared projection helpers to SDK (projections.ts): truncateValue, isPrimitiveType, projectFrame, projectVariable, projectBreakpoint, projectThreadLocation, plus exported types MappedFrame/MappedVariable/MappedBreakpoint/MappedLocation. Used by both MCP debug tools and CLI RPC mode — eliminates 5+ copies of the same projection code and the MAX_VALUE_LENGTH/PRIMITIVE_TYPES constants. - Add getSessionEntry/getRegistry helpers to session-registry. Replace the 4-line "registry not available" + getSessionOrThrow boilerplate in 11 tools. - Move halt-waiter promise pattern into DebugSessionRegistry.waitForHalt. Used by debug_wait_for_stop and debug_capture_at_breakpoint instead of duplicating the promise/timer/cleanup logic. - Replace step-action switch with a lookup map. API: - DebugSessionRegistry.registerSession now takes an options object (yhsieh1 nit) instead of 5 positional parameters. Docs: - Add "Recovery from broken or orphaned sessions" section to docs/mcp/tools/diagnostics.md (patricksullivansf concern). - Link to main authentication and configuration guides. - Add JSDoc to ServerContext explaining stdio-per-client isolation vs shared transport caveats (patricksullivansf concern). Tool descriptions (yhsieh1 audit): - Drop user-facing prose, sharpen for agent consumption. - debug_start_session: explicitly mention SFRA controllers, custom API scripts, hooks, jobs as use cases. - debug_end_session: IMPORTANT call-out to always end sessions. Coverage stays at 99.81% statements/lines, 94.81% branches, 98.93% functions — all above thresholds. * Align diagnostics auth section with other MCP tool docs Match the cartridge-deploy.md pattern: structured Required block, Configuration priority line, link to ../configuration (MCP config) and ../../guide/authentication#webdav-access (main auth guide with WebDAV anchor).
1 parent b1600fa commit b947888

37 files changed

Lines changed: 4261 additions & 25 deletions
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@salesforce/b2c-cli': minor
3+
---
4+
5+
Add `b2c debug cli` command for interactive terminal-based script debugging. Includes a REPL with commands for breakpoints, stepping, variable inspection, and expression evaluation. Use `--rpc` for JSONL-over-stdio mode suitable for headless scripts and agents.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@salesforce/b2c-dx-mcp': minor
3+
---
4+
5+
Add script debugger MCP tools to the CARTRIDGES and STOREFRONTNEXT toolsets. Includes `debug_start_session`, `debug_end_session`, `debug_list_sessions`, `debug_set_breakpoints`, `debug_wait_for_stop`, `debug_get_stack`, `debug_get_variables`, `debug_evaluate`, `debug_continue`, `debug_step_over`, `debug_step_into`, `debug_step_out`, and `debug_capture_at_breakpoint`.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@salesforce/b2c-dx-docs': patch
3+
'@salesforce/b2c-agent-plugins': patch
4+
---
5+
6+
Add debug command documentation and b2c-debug agent skill covering interactive REPL, RPC mode, and DAP usage.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@salesforce/b2c-dx-mcp': minor
3+
---
4+
5+
Add `ServerContext` for persistent server-scoped state across MCP tool invocations. Enables stateful tools (debug sessions, log watches) while preserving per-call config reloading for existing tools.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@salesforce/b2c-tooling-sdk': minor
3+
---
4+
5+
Add `resolveBreakpointPath` utility that normalizes user-provided file paths to SDAPI script paths. Accepts server paths, absolute/relative local paths, and cartridge-name-prefixed paths with helpful error messages on failure.

docs/.vitepress/config.mts

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -128,18 +128,45 @@ const referenceSidebar = [
128128
{
129129
text: 'MCP Tools',
130130
items: [
131-
{text: 'cartridge_deploy', link: '/mcp/tools/cartridge-deploy'},
132-
{text: 'mrt_bundle_push', link: '/mcp/tools/mrt-bundle-push'},
133-
{text: 'pwakit_get_guidelines', link: '/mcp/tools/pwakit-get-guidelines'},
134-
{text: 'scapi_schemas_list', link: '/mcp/tools/scapi-schemas-list'},
135-
{text: 'scapi_custom_api_generate_scaffold', link: '/mcp/tools/scapi-custom-api-generate-scaffold'},
136-
{text: 'scapi_custom_apis_get_status', link: '/mcp/tools/scapi-custom-apis-get-status'},
137-
{text: 'sfnext_get_guidelines', link: '/mcp/tools/sfnext-get-guidelines'},
138-
{text: 'sfnext_start_figma_workflow', link: '/mcp/tools/sfnext-start-figma-workflow'},
139-
{text: 'sfnext_analyze_component', link: '/mcp/tools/sfnext-analyze-component'},
140-
{text: 'sfnext_match_tokens_to_theme', link: '/mcp/tools/sfnext-match-tokens-to-theme'},
141-
{text: 'sfnext_add_page_designer_decorator', link: '/mcp/tools/sfnext-add-page-designer-decorator'},
142-
{text: 'sfnext_configure_theme', link: '/mcp/tools/sfnext-configure-theme'},
131+
{
132+
text: 'Cartridges',
133+
collapsed: true,
134+
items: [{text: 'cartridge_deploy', link: '/mcp/tools/cartridge-deploy'}],
135+
},
136+
{
137+
text: 'SCAPI',
138+
collapsed: true,
139+
items: [
140+
{text: 'scapi_schemas_list', link: '/mcp/tools/scapi-schemas-list'},
141+
{text: 'scapi_custom_api_generate_scaffold', link: '/mcp/tools/scapi-custom-api-generate-scaffold'},
142+
{text: 'scapi_custom_apis_get_status', link: '/mcp/tools/scapi-custom-apis-get-status'},
143+
],
144+
},
145+
{
146+
text: 'PWA Kit',
147+
collapsed: true,
148+
items: [
149+
{text: 'mrt_bundle_push', link: '/mcp/tools/mrt-bundle-push'},
150+
{text: 'pwakit_get_guidelines', link: '/mcp/tools/pwakit-get-guidelines'},
151+
],
152+
},
153+
{
154+
text: 'Storefront Next',
155+
collapsed: true,
156+
items: [
157+
{text: 'sfnext_get_guidelines', link: '/mcp/tools/sfnext-get-guidelines'},
158+
{text: 'sfnext_start_figma_workflow', link: '/mcp/tools/sfnext-start-figma-workflow'},
159+
{text: 'sfnext_analyze_component', link: '/mcp/tools/sfnext-analyze-component'},
160+
{text: 'sfnext_match_tokens_to_theme', link: '/mcp/tools/sfnext-match-tokens-to-theme'},
161+
{text: 'sfnext_add_page_designer_decorator', link: '/mcp/tools/sfnext-add-page-designer-decorator'},
162+
{text: 'sfnext_configure_theme', link: '/mcp/tools/sfnext-configure-theme'},
163+
],
164+
},
165+
{
166+
text: 'Diagnostics',
167+
collapsed: true,
168+
items: [{text: 'Script Debugger', link: '/mcp/tools/diagnostics'}],
169+
},
143170
],
144171
},
145172
];

docs/cli/debug.md

Lines changed: 218 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
---
2-
description: Start a DAP debug adapter for B2C Commerce server-side script debugging.
2+
description: DAP debug adapter, interactive REPL, and JSONL RPC mode for B2C Commerce server-side script debugging.
33
---
44

5-
# Debug Command
5+
# Debug Commands
66

7-
The `b2c debug` command launches a [Debug Adapter Protocol (DAP)](https://microsoft.github.io/debug-adapter-protocol/) adapter that bridges your IDE to the B2C Commerce script debugger. It's designed to be invoked by an IDE (VS Code, JetBrains, etc.) over stdio, not run directly in a shell.
7+
Commands for connecting to the B2C Commerce Script Debugger API (SDAPI) to set breakpoints, inspect variables, and step through server-side code.
8+
9+
- **`b2c debug`** — Debug Adapter Protocol (DAP) adapter for IDE integrations (VS Code, JetBrains, etc.).
10+
- **`b2c debug cli`** — interactive terminal REPL, or JSONL-over-stdio RPC mode for headless scripts and agents.
811

912
## Authentication
1013

@@ -14,11 +17,15 @@ The script debugger uses **Basic auth** (Business Manager username and password)
1417
- `SFCC_USERNAME` / `SFCC_PASSWORD` environment variables
1518
- `username` / `password` fields in `dw.json`
1619

20+
The script debugger must also be enabled on the instance: Business Manager > Administration > Development Configuration > Script Debugger > Enable.
21+
1722
See the [Authentication Guide](/guide/authentication) for details.
1823

24+
---
25+
1926
## b2c debug
2027

21-
Start the DAP adapter for the configured B2C instance.
28+
The `b2c debug` command launches a [Debug Adapter Protocol (DAP)](https://microsoft.github.io/debug-adapter-protocol/) adapter that bridges your IDE to the B2C Commerce script debugger. It's designed to be invoked by an IDE over stdio, not run directly in a shell.
2229

2330
### Usage
2431

@@ -52,8 +59,214 @@ b2c debug --client-id my-debugger
5259

5360
Most IDEs spawn DAP adapters automatically based on a launch configuration. The adapter speaks DAP over **stdin/stdout**, so direct shell invocation will appear to hang — that's expected. Configure your IDE's debug launcher to invoke `b2c debug` and supply the appropriate environment.
5461

55-
## Notes
62+
### Notes
5663

5764
- A warning is emitted if no cartridges are found at `--cartridge-path`.
5865
- Source maps are derived from the discovered cartridge layout; ensure your local cartridge tree matches what's deployed to the instance, otherwise breakpoints may not bind.
5966
- The adapter exits when its stdin stream closes.
67+
68+
---
69+
70+
## b2c debug cli
71+
72+
Start an interactive CLI debug session with a REPL interface. Provides a terminal-based debugging experience without requiring a DAP client. Add `--rpc` to switch to JSONL-over-stdio mode for headless scripts and agents.
73+
74+
### Usage
75+
76+
```bash
77+
b2c debug cli [--cartridge-path <PATH>] [--client-id <ID>] [--rpc]
78+
```
79+
80+
### Flags
81+
82+
| Flag | Description | Default |
83+
|------|-------------|---------|
84+
| `--cartridge-path` | Path to directory containing cartridges | `.` |
85+
| `--client-id` | Client ID for the debugger API | `b2c-cli` |
86+
| `--rpc` | Run in RPC mode (JSONL over stdin/stdout) | `false` |
87+
88+
Inherits the [global instance and authentication flags](./index#global-flags).
89+
90+
### REPL Commands
91+
92+
| Command | Alias | Description |
93+
|---------|-------|-------------|
94+
| `break <file>:<line> [if <cond>]` | `b` | Set breakpoint |
95+
| `breakpoints` | `bl` | List active breakpoints |
96+
| `delete <id>` | `d` | Delete breakpoint |
97+
| `continue` | `c` | Resume current thread |
98+
| `step` | `s` | Step over |
99+
| `stepin` | `si` | Step into |
100+
| `stepout` | `so` | Step out |
101+
| `stack` | `bt` | Show call stack |
102+
| `frame <n>` | `f` | Select stack frame |
103+
| `vars` | `v` | Show variables in current frame |
104+
| `members <path>` | `m` | Expand object members |
105+
| `eval <expr>` | `e` | Evaluate expression |
106+
| `threads` | `t` | List known threads |
107+
| `thread <id>` | | Switch to thread |
108+
| `help` | `h` | Show commands |
109+
| `quit` | `q` | Disconnect and exit |
110+
111+
### Examples
112+
113+
```bash
114+
# Start interactive debugger
115+
b2c debug cli
116+
117+
# Specify cartridge directory
118+
b2c debug cli --cartridge-path ./cartridges
119+
120+
# Use a custom client ID (for concurrent sessions)
121+
b2c debug cli --client-id my-session
122+
123+
# Start in RPC mode for headless scripts
124+
b2c debug cli --rpc
125+
```
126+
127+
### Interactive Session Example
128+
129+
```
130+
debug> break Cart.js:42
131+
Breakpoint #1 set at ./cartridges/app_storefront/cartridge/controllers/Cart.js:42
132+
133+
debug> break Checkout.js:100 if basket.totalGrossPrice > 100
134+
Breakpoint #2 set at ./cartridges/app_storefront/cartridge/controllers/Checkout.js:100
135+
136+
● Thread 5 halted at ./cartridges/app_storefront/cartridge/controllers/Cart.js:42 in show()
137+
138+
debug> vars
139+
request: dw.system.Request = [object Request] [local]
140+
basket: dw.order.Basket = [object Basket] [local]
141+
142+
debug> eval basket.productLineItems.length
143+
3
144+
145+
debug> stack
146+
→ #0 show ./cartridges/app_storefront/cartridge/controllers/Cart.js:42
147+
#1 execute /app_storefront/cartridge/controllers/Cart.js:1
148+
149+
debug> continue
150+
Thread 5 resumed.
151+
```
152+
153+
---
154+
155+
## RPC Mode
156+
157+
When started with `--rpc`, the debug CLI runs as a JSONL-over-stdio RPC server. This enables headless scripts, agents, and other tools to drive the debugger programmatically.
158+
159+
### Protocol
160+
161+
- **Input** (stdin): One JSON object per line (JSONL)
162+
- **Output** (stdout): One JSON object per line — either a response or an async event
163+
164+
### Request Format
165+
166+
```json
167+
{"id": 1, "command": "set_breakpoints", "args": {"breakpoints": [{"file": "Cart.js", "line": 42}]}}
168+
```
169+
170+
| Field | Type | Description |
171+
|-------|------|-------------|
172+
| `id` | number or string | Optional. Echoed back in the response for correlation. |
173+
| `command` | string | Required. The command to execute. |
174+
| `args` | object | Optional. Command-specific arguments. |
175+
176+
### Response Format
177+
178+
```json
179+
{"id": 1, "result": {"breakpoints": [{"id": 1, "file": "Cart.js", "line": 42, "script_path": "/app_storefront/cartridge/controllers/Cart.js"}]}}
180+
```
181+
182+
On error:
183+
184+
```json
185+
{"id": 1, "error": "No thread selected. Wait for a thread_stopped event."}
186+
```
187+
188+
### Event Format
189+
190+
Events are emitted asynchronously (not in response to a command):
191+
192+
```json
193+
{"event": "ready", "data": {}}
194+
{"event": "thread_stopped", "data": {"thread_id": 5, "location": {"file": "Cart.js", "line": 42, "function_name": "show", "script_path": "/app_storefront/cartridge/controllers/Cart.js"}}}
195+
```
196+
197+
### Available Commands
198+
199+
| Command | Args | Description |
200+
|---------|------|-------------|
201+
| `set_breakpoints` | `breakpoints: [{file, line, condition?}]` | Replace all breakpoints |
202+
| `list_breakpoints` | | List current breakpoints |
203+
| `continue` | `thread_id?` | Resume a halted thread |
204+
| `step_over` | `thread_id?` | Step to next line |
205+
| `step_into` | `thread_id?` | Step into function call |
206+
| `step_out` | `thread_id?` | Step out of function |
207+
| `get_stack` | `thread_id?` | Get call stack frames |
208+
| `get_variables` | `thread_id?, frame_index?, scope?, object_path?` | Get variables |
209+
| `evaluate` | `expression, thread_id?, frame_index?` | Evaluate expression |
210+
| `list_threads` | | List known threads |
211+
| `select_thread` | `thread_id` | Switch current thread |
212+
| `select_frame` | `index` | Switch current frame |
213+
214+
When `thread_id` is omitted, the last thread that halted is used.
215+
216+
### Events
217+
218+
| Event | Description |
219+
|-------|-------------|
220+
| `ready` | Emitted once after connection is established |
221+
| `thread_stopped` | A thread hit a breakpoint or step completed |
222+
223+
### Example Session (Python)
224+
225+
```python
226+
import subprocess, json
227+
228+
proc = subprocess.Popen(
229+
["b2c", "debug", "cli", "--rpc"],
230+
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
231+
text=True, bufsize=1
232+
)
233+
234+
def send(cmd, args=None, id=None):
235+
msg = {"command": cmd}
236+
if args: msg["args"] = args
237+
if id is not None: msg["id"] = id
238+
proc.stdin.write(json.dumps(msg) + "\n")
239+
proc.stdin.flush()
240+
241+
def recv():
242+
return json.loads(proc.stdout.readline())
243+
244+
# Wait for ready
245+
assert recv()["event"] == "ready"
246+
247+
# Set a breakpoint
248+
send("set_breakpoints", {"breakpoints": [{"file": "Cart.js", "line": 42}]}, id=1)
249+
response = recv() # {"id": 1, "result": {...}}
250+
251+
# Wait for breakpoint hit (trigger a request on the instance)
252+
event = recv() # {"event": "thread_stopped", "data": {...}}
253+
254+
# Inspect state
255+
send("get_stack", id=2)
256+
stack = recv()
257+
258+
send("get_variables", id=3)
259+
variables = recv()
260+
261+
# Continue execution
262+
send("continue", id=4)
263+
recv()
264+
```
265+
266+
---
267+
268+
## See Also
269+
270+
- [Authentication Guide](/guide/authentication) — Setting up instance credentials
271+
- [Logs Commands](/cli/logs) — Retrieving server logs for debugging
272+
- [Code Commands](/cli/code) — Deploying code before debugging

0 commit comments

Comments
 (0)