From karellen-jdb-mcp
Debug a Java application using JDB. Attaches to a running JVM, sets breakpoints, inspects state, and navigates execution to find the root cause of bugs.
npx claudepluginhub karellen/claude-plugins --plugin karellen-jdb-mcpThis skill uses the workspace's default tool permissions.
Use this skill when the user wants to debug a Java application, investigate an exception,
Guides Payload CMS config (payload.config.ts), collections, fields, hooks, access control, APIs. Debugs validation errors, security, relationships, queries, transactions, hook behavior.
Builds scalable data pipelines, modern data warehouses, and real-time streaming architectures using Spark, dbt, Airflow, Kafka, and cloud platforms like Snowflake, BigQuery.
Builds production Apache Airflow DAGs with best practices for operators, sensors, testing, and deployment. For data pipelines, workflow orchestration, and batch job scheduling.
Use this skill when the user wants to debug a Java application, investigate an exception, inspect runtime state, or understand execution flow.
jdb must be installed and on PATH (comes with the JDK)suspend=y pauses the JVM until the debugger attaches (recommended for startup debugging)suspend=n lets the JVM run immediately (for attaching to a live process)You (Claude) will typically start the JVM yourself before connecting. The JDWP agent stanza
varies by launcher. Pick a free port (e.g. 5005) and use suspend=y so the JVM waits for
you to attach.
javajava -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005 \
-cp target/classes com.example.Main
mvn test -Dmaven.surefire.debug="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005"
Or the built-in shorthand (uses port 5005 by default):
mvn -Dmaven.surefire.debug test
For Failsafe integration tests replace surefire with failsafe.
MAVEN_OPTS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005" \
mvn exec:java -Dexec.mainClass=com.example.Main
./gradlew test --debug-jvm # uses port 5005 by default, suspend=y
Add to the task or pass via env:
JAVA_OPTS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005" \
./gradlew run
Tycho Surefire uses the same property as Maven Surefire:
mvn verify -Dmaven.surefire.debug="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005"
Or with the tycho.testArgLine property:
mvn verify -Dtycho.testArgLine="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005"
mvn spring-boot:run -Dspring-boot.run.jvmArguments="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005"
Or with Gradle:
./gradlew bootRun --args='--debug' # or pass JAVA_OPTS
As a fallback, this environment variable is picked up by every JVM:
JAVA_TOOL_OPTIONS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005" \
<any-command-that-starts-a-jvm>
Use jdb_launch to start the JVM with ${JDB_PORT} substitution (allocates a random port):
jdb_launch(["java", "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:${JDB_PORT}",
"-cp", "target/classes", "com.example.Main"])
Or for Maven tests:
jdb_launch(["mvn", "test",
"-Dmaven.surefire.debug=-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:${JDB_PORT}"])
Then connect with wait_timeout to wait for the JVM to start. When there is exactly
one launched process, jdb_connect auto-resolves the port — no need to pass it:
jdb_connect(wait_timeout=30)
If you launched multiple JVMs or are connecting to a manually started JVM, specify the port:
jdb_connect(port=5005, wait_timeout=30)
Similarly, all debugging tools auto-resolve the port when only one session is active.
When multiple sessions are active, pass port= to select which session to use.
Pass jdb_path if jdb is not on PATH. Pass sourcepath to enable source listing.
Pass trackallthreads=True on JDK 20+ to track virtual threads.
jdb_breakpoint_set("com.example.MyClass:42") # line breakpoint
jdb_breakpoint_set("com.example.MyClass.myMethod") # method breakpoint
jdb_breakpoint_set("com.example.MyClass.myMethod(int)") # overloaded method
For exception debugging:
jdb_catch("java.lang.NullPointerException") # break on NullPointerException
jdb_catch("*", filter_type="uncaught") # break on any uncaught exception
For field watchpoints:
jdb_watch("com.example.MyClass.myField") # break on field write
jdb_watch("com.example.MyClass.myField", access_type="access") # break on field read
Execution commands are fire-and-forget: they return immediately once the JVM resumes.
If a breakpoint or exception is hit within a few seconds, the stop event is returned
directly. Otherwise, the tool returns reason="resumed" and you should use
jdb_suspend() or wait for the next breakpoint to examine state.
If the JVM was started with suspend=y:
jdb_run() # start execution (returns immediately)
Or if already running:
jdb_cont() # continue execution (returns immediately)
If jdb_run or jdb_cont returns reason="resumed", call jdb_wait_for_event to
block until the next breakpoint, exception, or program exit:
jdb_wait_for_event(timeout=120) # blocks until stop event or timeout
jdb_where() — see the call stackjdb_locals() — see all local variablesjdb_print("expression") — evaluate any Java expressionjdb_dump("objectRef") — show all fields of an objectjdb_list() — see source code at current positionjdb_threads() — list all threadsjdb_step() — step into (enter method calls)jdb_next() — step over (skip over method calls)jdb_step_up() — step out (run until current method returns)jdb_up() / jdb_down() — navigate the call stack without steppingjdb_classes() to find loaded classesjdb_methods("com.example.MyClass") to discover available methodsjdb_fields("com.example.MyClass") to discover fieldsjdb_class_info("com.example.MyClass") to see class hierarchyjdb_set("variable", "newValue") to modify state and test hypothesesFor deadlock analysis:
jdb_threads() — find stuck threadsjdb_threadlocks() — see what locks a thread holds and waits forjdb_lock("objectRef") — see who owns a lock and who's waitingjdb_thread("threadId") then jdb_where() — inspect each thread's stackjdb_session_list() — list all active debug sessions with port and JDK versionjdb_launch_list() — list all launched JVM processes with statusjdb_launch_status(port=<port>) — check if a specific launched process is still runningjdb_disconnect()
jdb_launch_stop(port=<port>)
Or when debugging multiple JVMs:
jdb_disconnect(port=<port>)
jdb_launch_stop(port=<port>)
jdb_launch to start JVMs rather than Bash. It handles port allocation,
${JDB_PORT} substitution, and process detachment.jdb_connect,
jdb_launch*, and jdb_session_list require an active session.jdb_catch for exception debugging — it's more effective than guessing where to
set breakpoints.jdb_version for feature availability. Thread-filtered breakpoints require
JDK 13+, virtual thread tracking requires JDK 20+.jdb_exclude to skip library code when stepping. By default, JDB skips
java., javax., sun., com.sun., jdk.*.jdb_monitor("locals") to auto-print locals at every stop for hands-free
debugging.