Skip to content

Commit a6583de

Browse files
ozzafarCopilot
andcommitted
Fix #18: add_breakpoint takes a line number instead of lineContent
Replace the substring-based `lineContent` parameter with a 1-based `line` number, making add_breakpoint consistent with remove_breakpoint, list_breakpoints, and the documented tool schema. The handler now validates the line (integer >= 1 and within the file's line count) and sets a single breakpoint at that line, failing clearly instead of substring-matching (which could set multiple/unexpected breakpoints). Updated the MCP tool schema, routing handler/test, README, SKILL.md, and the C# troubleshooting doc. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6ea24b92-40ad-4539-aa13-61aa6ff8751f
1 parent e531260 commit a6583de

7 files changed

Lines changed: 29 additions & 44 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ DebugMCP is an MCP server that gives AI coding agents full control over the VS C
6060
| **continue_execution** | Continue until next breakpoint | None |
6161
| **pause_execution** | Interrupt a freely-running program and stop at its current location (no breakpoint needed) | None |
6262
| **restart_debugging** | Restart the current debug session | None |
63-
| **add_breakpoint** | Add a breakpoint at a specific line (optionally conditional) | `fileFullPath` (required)<br>`lineContent` (required)<br>`condition` (optional) |
63+
| **add_breakpoint** | Add a breakpoint at a specific line (optionally conditional) | `fileFullPath` (required)<br>`line` (required, 1-based)<br>`condition` (optional) |
6464
| **remove_breakpoint** | Remove a breakpoint from a specific line | `fileFullPath` (required)<br>`line` (required) |
6565
| **clear_all_breakpoints** | Remove all breakpoints at once | None |
6666
| **list_breakpoints** | List all active breakpoints | None |
@@ -371,7 +371,7 @@ Yes. DebugMCP supports `.cs` files and `.csproj` project files for C#/.NET debug
371371
- **Symptom**: Breakpoints are set but execution doesn't pause
372372
- **Solution**:
373373
- Ensure the correct file is being debugged
374-
- Check that the breakpoint line content matches exactly
374+
- Check that the breakpoint line number is correct
375375
- Verify the relevant language debugger extension is installed
376376

377377
#### Configuration Not Auto-Detected

skills/debug-live/SKILL.md

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,8 @@ If you can step through the code in a few tool calls, do that instead of specula
4646

4747
## Core workflow
4848

49-
1. **Set a starting breakpoint.** Use `add_breakpoint` with the file path and the exact
50-
line content you want to pause on (line content matching is more robust than line
51-
numbers — it survives small edits). Place it at the earliest point that's still
49+
1. **Set a starting breakpoint.** Use `add_breakpoint` with the file path and the 1-based
50+
line number you want to pause on. Place it at the earliest point that's still
5251
relevant to the suspected issue.
5352
2. **Optionally add strategic breakpoints.** Decision points, error-handling branches,
5453
data boundaries (where input enters, where output is produced).
@@ -168,8 +167,8 @@ Before ending the debug session, confirm you can answer:
168167

169168
- **Start broad, then narrow.** Begin at the entry point of the suspect function. As
170169
you isolate the issue, add tighter breakpoints around the problematic region.
171-
- **Match line content, not numbers.** `add_breakpoint` takes the exact line text so
172-
breakpoints survive small edits and refactors.
170+
- **Use line numbers.** `add_breakpoint` takes a 1-based `line`; re-check the line after
171+
edits since numbers shift when code changes.
173172
- **Don't overuse breakpoints.** A handful of well-placed pauses beats dozens of noisy
174173
ones. After each session, `clear_all_breakpoints` to start fresh.
175174
- **For test debugging,** pass `testName` to `start_debugging`. The server routes through
@@ -182,7 +181,7 @@ Before ending the debug session, confirm you can answer:
182181

183182
### Investigating a bug in `calculate.py`
184183
```text
185-
add_breakpoint fileFullPath=/repo/src/calculate.py lineContent="result = parse(raw)"
184+
add_breakpoint fileFullPath=/repo/src/calculate.py line=42
186185
start_debugging fileFullPath=/repo/src/calculate.py workingDirectory=/repo
187186
# session pauses on the breakpoint
188187
get_variables_values scope=local
@@ -194,7 +193,7 @@ clear_all_breakpoints
194193

195194
### Debugging a single xUnit test in C#
196195
```text
197-
add_breakpoint fileFullPath=C:\Repo\Calculator.Tests\CalculatorTests.cs lineContent="Assert.Equal(5, _calc.Add(2, 3));"
196+
add_breakpoint fileFullPath=C:\Repo\Calculator.Tests\CalculatorTests.cs line=18
198197
start_debugging fileFullPath=C:\Repo\Calculator.Tests\CalculatorTests.cs workingDirectory=C:\Repo testName=Add_ReturnsSum
199198
# pauses inside the test
200199
step_into

skills/debug-live/references/troubleshooting/csharp.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ DebugMCP provides enhanced support for debugging C# applications using the C# De
3333
3. Set appropriate working directories
3434

3535
### Breakpoint Management
36-
1. Use line content matching for reliable breakpoint placement
36+
1. Use the 1-based line number for reliable breakpoint placement
3737
2. Clear breakpoints when stopping debug sessions
3838
3. Verify breakpoints are hit in the expected files
3939

src/debugMCPServer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,10 +271,10 @@ export class DebugMCPServer {
271271
description: 'Set a breakpoint to pause execution at a critical line of code. Essential for debugging: pause before potential errors, examine state at decision points, or verify code paths. Breakpoints let you inspect variables and control flow at exact moments. Provide an optional condition to create a conditional breakpoint that only pauses when the expression evaluates to true (e.g. "i == 5" or "user.id === null").',
272272
inputSchema: {
273273
fileFullPath: z.string().describe('Full path to the file'),
274-
lineContent: z.string().describe('Line content'),
274+
line: z.number().int().describe('Line number (1-based) where the breakpoint should be set'),
275275
condition: z.string().optional().describe('Optional condition expression. When provided, execution only pauses if this expression evaluates to true at the breakpoint location.'),
276276
},
277-
}, async (args: { fileFullPath: string; lineContent: string; condition?: string }) =>
277+
}, async (args: { fileFullPath: string; line: number; condition?: string }) =>
278278
this.runTool('add_breakpoint', () => debuggingHandler.handleAddBreakpoint(args)));
279279

280280
// Remove breakpoint tool

src/debuggingHandler.ts

Lines changed: 16 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export interface IDebuggingHandler {
1818
handleContinue(): Promise<string>;
1919
handlePause(): Promise<string>;
2020
handleRestart(): Promise<string>;
21-
handleAddBreakpoint(args: { fileFullPath: string; lineContent: string; condition?: string }): Promise<string>;
21+
handleAddBreakpoint(args: { fileFullPath: string; line: number; condition?: string }): Promise<string>;
2222
handleRemoveBreakpoint(args: { fileFullPath: string; line: number }): Promise<string>;
2323
handleClearAllBreakpoints(): Promise<string>;
2424
handleListBreakpoints(): Promise<string>;
@@ -300,40 +300,26 @@ export class DebuggingHandler implements IDebuggingHandler {
300300
* Add a breakpoint at specified location. An optional condition makes it a
301301
* conditional breakpoint that only pauses when the expression is true.
302302
*/
303-
public async handleAddBreakpoint(args: { fileFullPath: string; lineContent: string; condition?: string }): Promise<string> {
304-
const { fileFullPath, lineContent, condition } = args;
305-
303+
public async handleAddBreakpoint(args: { fileFullPath: string; line: number; condition?: string }): Promise<string> {
304+
const { fileFullPath, line, condition } = args;
305+
306306
try {
307-
// Find the line number containing the line content
308-
const document = await vscode.workspace.openTextDocument(vscode.Uri.file(fileFullPath));
309-
const text = document.getText();
310-
const lines = text.split(/\r?\n/);
311-
const matchingLineNumbers: number[] = [];
312-
313-
for (let i = 0; i < lines.length; i++) {
314-
if (lines[i].includes(lineContent)) {
315-
matchingLineNumbers.push(i + 1); // Convert to 1-based line numbers
316-
}
307+
if (!Number.isInteger(line) || line < 1) {
308+
throw new Error(`Invalid line number: ${line}. Provide a 1-based line number.`);
317309
}
318-
319-
if (matchingLineNumbers.length === 0) {
320-
throw new Error(`Could not find any lines containing: ${lineContent}`);
310+
311+
// Validate the line exists so we fail clearly instead of setting an
312+
// unbound breakpoint past the end of the file.
313+
const document = await vscode.workspace.openTextDocument(vscode.Uri.file(fileFullPath));
314+
if (line > document.lineCount) {
315+
throw new Error(`Line ${line} is out of range: ${fileFullPath} has ${document.lineCount} lines.`);
321316
}
322-
317+
323318
const uri = vscode.Uri.file(fileFullPath);
324-
325-
// Add breakpoints to all matching lines
326-
for (const lineNumber of matchingLineNumbers) {
327-
await this.executor.addBreakpoint(uri, lineNumber, condition);
328-
}
329-
319+
await this.executor.addBreakpoint(uri, line, condition);
320+
330321
const conditionInfo = condition ? ` (condition: ${condition})` : '';
331-
if (matchingLineNumbers.length === 1) {
332-
return `Breakpoint added at ${fileFullPath}:${matchingLineNumbers[0]}${conditionInfo}`;
333-
} else {
334-
const linesList = matchingLineNumbers.join(', ');
335-
return `Breakpoints added at ${matchingLineNumbers.length} locations in ${fileFullPath}: lines ${linesList}${conditionInfo}`;
336-
}
322+
return `Breakpoint added at ${fileFullPath}:${line}${conditionInfo}`;
337323
} catch (error) {
338324
throw new Error(`Error adding breakpoint: ${error}`);
339325
}

src/routingDebuggingHandler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ export class RoutingDebuggingHandler implements IDebuggingHandler {
198198
return this.forward('handleRestart', {});
199199
}
200200

201-
public handleAddBreakpoint(args: { fileFullPath: string; lineContent: string; condition?: string }): Promise<string> {
201+
public handleAddBreakpoint(args: { fileFullPath: string; line: number; condition?: string }): Promise<string> {
202202
return this.forward('handleAddBreakpoint', args, args.fileFullPath);
203203
}
204204

src/test/routing.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ suite('Multi-window routing', () => {
115115
const routing = new RoutingDebuggingHandler(new WorkspaceRegistry(process.pid, dir));
116116
const result = await routing.handleAddBreakpoint({
117117
fileFullPath: path.join(repoA, 'src', 'y.py'),
118-
lineContent: 'return 1'
118+
line: 1
119119
});
120120

121121
assert.strictEqual(result, 'A:addBp');

0 commit comments

Comments
 (0)