JetBrains Debugger MCP Plugin
This plugin exposes JetBrains IDE debugger capabilities through MCP (Model Context Protocol), enabling AI agents to programmatically debug applications.
Quick Start
IMPORTANT: When debugging, prefer using jetbrains-debugger MCP tools to interact with the IDE debugger.
Typical Debugging Workflow
- Discover configurations:
list_run_configurationsto see available run/debug configs - Start debugging:
start_debug_sessionwith a configuration that hascan_debug: true - Set breakpoints:
set_breakpointat locations of interest - Resume and wait:
resume_executionthenwait_for_pausewith a timeout to block until a breakpoint is hit - Inspect state:
wait_for_pausereturns variables, stack trace, and source context automatically. Useget_debug_session_statusif you need to re-inspect without waiting. - Evaluate expressions:
evaluate_expressionto test hypotheses - Step through code:
step_over,step_into,step_outas needed - Repeat steps 5-7 until the issue is found
Tool Reference
Session Management
| Tool | Description |
|---|---|
list_run_configurations |
List available run configurations |
execute_run_configuration |
Run a configuration (debug or run mode) |
list_debug_sessions |
List active debug sessions |
start_debug_session |
Start a new debug session |
stop_debug_session |
Stop a debug session |
get_debug_session_status |
Get comprehensive session state (variables, stack, source) |
Breakpoints
| Tool | Description |
|---|---|
list_breakpoints |
List all breakpoints |
set_breakpoint |
Set a line breakpoint (supports conditions, log messages) |
remove_breakpoint |
Remove a breakpoint by ID |
Execution Control
| Tool | Description |
|---|---|
resume_execution |
Resume from paused state |
pause_execution |
Pause running execution |
step_over |
Step to next line (over function calls) |
step_into |
Step into function call |
step_out |
Step out of current function |
run_to_line |
Run until specific line is reached |
wait_for_pause |
Wait for session to pause (breakpoint, exception, manual). Returns full status. |
Inspection
| Tool | Description |
|---|---|
get_stack_trace |
Get call stack for a thread |
select_stack_frame |
Change stack frame context |
list_threads |
List all threads |
get_variables |
Get variables in current frame |
set_variable |
Modify variable value at runtime |
evaluate_expression |
Evaluate expression or code fragment |
get_source_context |
Get source code around a location |
Best Practices
Use get_debug_session_status First
When paused at a breakpoint, call get_debug_session_status before other inspection tools. It returns:
- Current location (file, line, method)
- Variables in scope
- Stack trace summary
- Source code context
- Breakpoint that was hit
This reduces round-trips compared to calling get_variables, get_stack_trace, and get_source_context separately.
Conditional Breakpoints for Loops
When debugging loops or frequently-called functions, use conditional breakpoints:
{
"file_path": "/src/Calculator.java",
"line": 42,
"condition": "i == 100"
}
Log Breakpoints for Tracing
Use log breakpoints (tracepoints) to trace execution without stopping:
{
"file_path": "/src/Calculator.java",
"line": 42,
"log_message": "Value of x: {x}, items.size(): {items.size()}",
"suspend_policy": "none"
}
Log Message Syntax: Use {expression} placeholders in your log message. They are automatically transformed to language-specific expressions:
| Language | Input | Transformed To |
|---|---|---|
| Java | "x={x}, y={y}" |
"x=" + (x) + ", y=" + (y) |
| Kotlin | "x={x}, y={y}" |
"x=$x, y=$y" |
| Python | "x={x}, y={y}" |
f"x={x}, y={y}" |
| JavaScript/TS | "x={x}, y={y}" |
`x=${x}, y=${y}` |
You can also pass raw language-specific expressions directly (e.g., "\"x=\" + x" for Java) - they pass through unchanged if no {...} placeholders are detected.
Evaluate Before Modifying
Before using set_variable, use evaluate_expression to preview the change:
{
"expression": "calculateNewValue(currentValue)",
"allow_side_effects": false
}
Handle Multiple Projects
When multiple projects are open in the IDE, always specify projectPath:
{
"projectPath": "/Users/dev/my-project",
"file_path": "/src/Main.java",
"line": 10
}
If omitted with multiple projects, tools return an error listing available projects.
Common Patterns
Find Why a Value is Wrong
1. set_breakpoint at the line where the wrong value is used
2. start_debug_session
3. resume_execution (wait for breakpoint)
4. get_debug_session_status (see all variables)
5. evaluate_expression to test the calculation
6. step_over/step_into to trace the logic
Debug a Specific Iteration
1. set_breakpoint with condition "i == 50"
2. start_debug_session
3. resume_execution
4. Debugger stops only when i equals 50
Trace Execution Path
1. set_breakpoint with log_message at key locations
2. set suspend_policy to "none" for all breakpoints
3. start_debug_session
4. resume_execution
5. Check IDE console for trace output
Error Handling
Tools return structured errors:
session_not_found- Invalid session_idsession_not_paused- Operation requires paused sessionbreakpoint_not_found- Invalid breakpoint_idinvalid_location- Cannot set breakpoint at locationmultiple_projects_open- Must specify projectPathevaluation_error- Expression evaluation failed
Language-Specific Limitations
Some debugging features depend on the underlying debugger's capabilities:
Full Support (Java, Kotlin, Python, JavaScript, TypeScript, PHP, Ruby)
- β All tools work as expected
- β Expression evaluation with method calls
- β Variable modification for all types
- β Rich type inspection
Limited Support (Rust, C++, C, Go, Swift)
These languages use native debuggers (LLDB/GDB) with some limitations:
| Tool | Limitation |
|---|---|
evaluate_expression |
Variable inspection works. Method calls (e.g., s.len(), vec.size()) may fail with "no field named X" errors. |
set_variable |
Works for primitive types (int, float, bool). Complex types (String, Vec, structs) may fail with "could not find item" errors. |
Workarounds for native languages:
- Use
get_variablesto inspect values instead ofevaluate_expressionwith method calls - For Rust strings, access the underlying data: evaluate
sinstead ofs.len() - Focus on stepping and breakpoints for debugging flow
Requirements
- JetBrains IDE (IntelliJ IDEA, PyCharm, WebStorm, RustRover, CLion, GoLand, etc.)
- IDE must have an open project with a debuggable run configuration
- This plugin must be installed and enabled
Developer Guide: MCP Structured Outputs
When developing tools for this plugin, be aware of the MCP protocol's structured output requirements.
When outputSchema is Defined
If a tool defines an outputSchema, the MCP protocol requires the response to include a structuredContent field containing the actual JSON object (not just text content).
Error if missing:
MCP error -32600: Tool [name] has an output schema but did not return structured content
Solution: Use createJsonResult() from AbstractMcpTool - it automatically populates both content (text) and structuredContent (JSON object).
Nullable Fields in Output Schema
When a field can be null, the JSON Schema must explicitly allow it using an array of types:
// Wrong - will fail validation if value is null:
putJsonObject("className") { put("type", "string") }
// Correct - allows null values:
putJsonObject("className") {
putJsonArray("type") {
add(JsonPrimitive("string"))
add(JsonPrimitive("null"))
}
}
Common Nullable Fields
These fields are commonly null and should use ["type", "null"]:
className- may be null for synthetic or library framesmethodName- may be null for lambda expressionsfile- may be null for generated codeline- may be null when source mapping unavailablepausedReason- null when session is runningcurrentLocation- null when session is not paused
Tool Result Pattern
// For tools WITH outputSchema - uses structuredContent:
override val outputSchema: JsonObject = buildJsonObject { /* schema */ }
override suspend fun doExecute(...): ToolCallResult {
return createJsonResult(MyResultData(...)) // Auto-populates structuredContent
}
// For tools WITHOUT outputSchema - simpler text response:
override suspend fun doExecute(...): ToolCallResult {
return createSuccessResult("Operation completed")
}
Required Imports for Nullable Schemas
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.putJsonArray
