Позволяет ИИ-агентам полностью управлять отладкой запущенных Java-приложений через JDWP/JDI, включая инспекцию состояния и изменение значений.
Также известен как FgForrest/mcp-jdwp-java
- Звёзды
- 34
- Инструменты
- 50
- Загрузки, 30 дней
- —
- Последняя версия
- —
- Последний коммит
- 14 нояб. 2026 г.
- Лицензия
- MIT
Что такое jdwp-java MCP?
jdwp-java — это MCP server. Позволяет ИИ-агентам полностью управлять отладкой запущенных Java-приложений через JDWP/JDI, включая инспекцию состояния и изменение значений. Он предоставляет 50 инструментов, например jdwp_connect, jdwp_disconnect, jdwp_reconnect.
Сводка сгенерирована из README проекта
О jdwp-java MCP server
Этот MCP server позволяет отлаживать запущенные Java-приложения прямо из ИИ-агента. Он предоставляет интерфейс для глубокой инспекции и манипуляций с JVM во время выполнения с использованием протоколов JDWP и JDI. Вместо простого чтения стектрейсов агент может исследовать весь граф объектов, читать локальные переменные, а также проверять поля или потоки. Это обеспечивает уровень глубины диагностики, сопоставимый с профессиональными IDE, такими как IntelliJ или Eclipse.
Сервер работает локально через stdio и подключается к целевой JVM через локальный сокет JDWP. Для работы требуется JDK 17 или выше, хотя для сборки сервера из исходного кода необходим JDK 21. Пользователи могут интегрировать его с Claude Code с помощью плагина или ручной регистрации. После подключения агент может использовать более 50 инструментов для установки условных точек останова, выполнения произвольных Java-выражений и даже изменения значений полей или локальных переменных, чтобы тестировать различные сценарии без перезапуска приложения.
Что вы можете с ним делать
- 1Инспекция состояния объектов во время выполненияАгент может изучить весь граф объектов и прочитать локальные переменные, чтобы понять, почему конкретное значение является неверным.
- 2Тестирование логики через изменение состояния в рантаймеРазработчик может попросить ИИ изменить значение поля или локальной переменной, пока программа приостановлена, чтобы увидеть, как приложение на это отреагирует.
- 3Мониторинг изменений полей с помощью watchpointsРазработчик может точно отследить, когда и где конкретное поле изменяется работающим приложением.
Взято из README проекта. Каждый вариант использования проверен на соответствие цитате из него.
Инструменты jdwp-java MCP 50
Определено из READMEjdwp_connectjdwp_disconnectjdwp_reconnectjdwp_wait_for_attachjdwp_get_versionjdwp_get_threadsjdwp_dump_locksjdwp_get_stackjdwp_get_localsjdwp_get_fieldsjdwp_to_stringjdwp_get_breakpoint_context
| jdwp_connect | — | |
| jdwp_disconnect | — | |
| jdwp_reconnect | — | |
| jdwp_wait_for_attach | — | |
| jdwp_get_version | — | |
| jdwp_get_threads | — | |
| jdwp_dump_locks | — | |
| jdwp_get_stack | — | |
| jdwp_get_locals | — | |
| jdwp_get_fields | — | |
| jdwp_to_string | — | |
| jdwp_get_breakpoint_context | — |
Показать все 50 инструментовПоказать меньше инструментов
jdwp_get_current_threadjdwp_resumejdwp_resume_threadjdwp_suspend_threadjdwp_resume_until_eventjdwp_step_overjdwp_step_intojdwp_step_outjdwp_set_breakpointjdwp_set_logpointjdwp_set_fast_logpointjdwp_set_fast_breakpointjdwp_restore_fast_logpointsjdwp_clear_breakpointjdwp_set_exception_breakpointjdwp_set_exception_logpointjdwp_set_field_breakpointjdwp_set_field_logpointjdwp_set_breakpoint_dependencyjdwp_clear_breakpoint_dependencyjdwp_disarm_until_triggerjdwp_evaluate_expressionjdwp_assert_expressionjdwp_set_localjdwp_set_fieldjdwp_get_eventsjdwp_clear_eventsjdwp_diagnosejdwp_attach_watcherjdwp_detach_watcherjdwp_list_watchers_for_breakpointjdwp_evaluate_watchersjdwp_mark_instancejdwp_unmark_instancejdwp_rename_markjdwp_overviewjdwp_clearjdwp_reset
| jdwp_get_current_thread | — | |
| jdwp_resume | — | |
| jdwp_resume_thread | — | |
| jdwp_suspend_thread | — | |
| jdwp_resume_until_event | — | |
| jdwp_step_over | — | |
| jdwp_step_into | — | |
| jdwp_step_out | — | |
| jdwp_set_breakpoint | — | |
| jdwp_set_logpoint | — | |
| jdwp_set_fast_logpoint | — | |
| jdwp_set_fast_breakpoint | — | |
| jdwp_restore_fast_logpoints | — | |
| jdwp_clear_breakpoint | — | |
| jdwp_set_exception_breakpoint | — | |
| jdwp_set_exception_logpoint | — | |
| jdwp_set_field_breakpoint | — | |
| jdwp_set_field_logpoint | — | |
| jdwp_set_breakpoint_dependency | — | |
| jdwp_clear_breakpoint_dependency | — | |
| jdwp_disarm_until_trigger | — | |
| jdwp_evaluate_expression | — | |
| jdwp_assert_expression | — | |
| jdwp_set_local | — | |
| jdwp_set_field | — | |
| jdwp_get_events | — | |
| jdwp_clear_events | — | |
| jdwp_diagnose | — | |
| jdwp_attach_watcher | — | |
| jdwp_detach_watcher | — | |
| jdwp_list_watchers_for_breakpoint | — | |
| jdwp_evaluate_watchers | — | |
| jdwp_mark_instance | — | |
| jdwp_unmark_instance | — | |
| jdwp_rename_mark | — | |
| jdwp_overview | — | |
| jdwp_clear | — | |
| jdwp_reset | — |
Конфигурация
MCP_TIMEOUTMCP_TOOL_TIMEOUT
| Название | Описание | Тип |
|---|---|---|
| MCP_TIMEOUT | — | |
| MCP_TOOL_TIMEOUT | — |
Автоматические проверки
Запущено 13 нояб. 2026 г.Полный отчёт →Прошло наши автоматизированные проверки. Это не аудит безопасности: изучите код и предоставьте серверу минимально необходимый доступ. Как работают проверки
- Связанный и доступный исходный репозиторийПройдено
- Open source лицензия (OSI)Пройдено
- Коммит или релиз за последний годПройдено
- Имя пакета не похоже на популярный пакетНе проверялось
- Удаленная конечная точка использует HTTPSНе проверялось
- Деструктивные инструменты помеченыПройдено
- Нет скрытых инструкций для моделиПройдено
- Нет критических предупреждений в пакете или его зависимостяхНе проверялось
README
Открыть на GitHubMCP JDWP Inspector
MCP server that gives AI agents full debugger control over running Java applications — inspect state, set breakpoints, evaluate expressions, and mutate values at runtime via JDWP/JDI.
Release notes — see
CHANGELOG.md. 2.0.0 is a breaking release: severaljdwp_clear_*tools were unified, and field watchpoints are new.
Built on the foundations of mcp-jdwp-java by Nicolas Vautrin — the original project that provided core JDI connectivity, thread/stack/variable inspection, stepping, and basic breakpoint management. Everything described below as "beyond standard JDWP" was built on top of that base.
What you get (TL;DR)
An MCP server that speaks JDWP — the same protocol IntelliJ / Eclipse / VS Code use to attach — and exposes the debugger to Claude Code as 50 tools + 2 MCP resources over STDIO. Once the agent attaches to your JVM, it can:
- See runtime state, not stack traces — read locals, fields, threads, the entire object graph
- Evaluate arbitrary Java in the suspended frame — compiled to real bytecode, full classpath, including private/package-private members
- Mutate the running program — set locals, write fields, test "what if?" without restart
- Stop on the bad case only — conditional breakpoints, exception breakpoints at the throw site, deferred BPs that arm before the class loads, chains that gate one BP on another
- Trace without stopping — line/exception/field logpoints write to an event log, the thread runs on
- Catch the writer of a mutating field — watchpoints suspend at every bytecode store to the field, wherever it is; a write through reflection or
Unsafeis the one they cannot see, and the[VM_DEATH]reply's hit count shows when a watchpoint stayed silent - Pure STDIO — no daemon, no extra port, no GUI
The agent gets the same power as IntelliJ's debugger, with extras that exist specifically because an agent isn't a human staring at a UI (filtering, blocking resume, one-shot context dumps, recursion guards, reconnect after a wedge).
Security & trust
This MCP server runs entirely on your local machine:
- No network calls — the server communicates with Claude Code over STDIO and with the target JVM over a local JDWP socket. It makes zero outbound HTTP/internet requests. No telemetry, no analytics, no phone-home.
- Built from source — the JAR is compiled locally on your machine from the source code in this repository (either via the plugin auto-build hook or manually with
./mvnw clean package). No pre-built binaries are downloaded or distributed. - Auditable — the full source is here. The server is a standard Spring Boot application with no obfuscation or native code. Try asking Claude Code itself: "audit these sources for anything that touches network or filesystem beyond the JVM target".
Quick start
Prerequisites
- JDK 17+ on PATH to run the server (must be a JDK, not a JRE — JDI lives in
jdk.jdi). - JDK 21+ to build from source — the build toolchain pins to JDK ≥ 21 because Error Prone 2.48 ships Java-21 bytecode. The bytecode target stays at Java 17, so the resulting JAR still runs on a JDK 17 runtime.
No separate Maven install is required — the repository ships with the Maven Wrapper (./mvnw), which downloads a pinned Maven 3.9.x into ~/.m2/wrapper/ on first use. Every build command in this README uses the wrapper; the SessionStart hook does too, and falls back to mvn on PATH when the wrapper cannot set itself up.
1. Install the plugin
Option A: Plugin marketplace (recommended)
Installs the MCP server, the java-debug skill (debugging workflows, recipes, gotchas), and the MCP server registration in one step:
/plugin marketplace add https://github.com/FgForrest/mcp-jdwp-java.git
/plugin install jdwp-debugging@mcp-jdwp-java
The server JAR is built automatically on first session start via the bundled ./mvnw wrapper (requires JDK 21+ — no Maven install needed). The hook is content-aware: it rebuilds when the JAR is missing or, in a git checkout, when HEAD moves, so git pull + restart picks up updates without manual intervention. When the wrapper cannot set itself up (for example, ~/.m2 is read-only in a sandbox), the hook tries mvn -o and then mvn from PATH. If every attempt fails, a warning at session start names the build log and the exact command to build by hand. A build runs while Claude Code starts the MCP server and the JAR is absent until it finishes, so after a build (first install, update, moved HEAD) jdwp-inspector may show as failed in /mcp; reconnect it there once the build is done. Restart Claude Code to pick up the plugin.
If you prefer to register the MCP server directly without the plugin (no skill, no auto-build):
1. Build the JAR:
git clone https://github.com/FgForrest/mcp-jdwp-java.git
cd mcp-jdwp-java
./mvnw clean package -DskipTests # use mvnw.cmd on Windows
2. Register with Claude Code:
claude mcp add jdwp-inspector -s user \
-e MCP_TIMEOUT=30000 \
-e MCP_TOOL_TIMEOUT=120000 \
-- java --add-modules jdk.jdi,jdk.attach -jar /path/to/mcp-jdwp-java.jar
To change the JDWP port (default 5005), add -DJVM_JDWP_PORT=12345 before -jar.
The MCP_TIMEOUT and MCP_TOOL_TIMEOUT environment variables are important — JVM startup is not instant (class loading, Spring context initialization), so the default MCP timeouts will cause Claude Code to give up before the server is ready. MCP_TIMEOUT=30000 gives the server 30 seconds to start, and MCP_TOOL_TIMEOUT=120000 allows up to 2 minutes for long-running tools like first-time expression evaluation (which discovers the target's classpath and compiles bytecode).
Re-installing requires removing first: claude mcp remove jdwp-inspector -s user
Drop -s user to scope to the current project only.
.mcp.json:
{
"mcpServers": {
"jdwp-inspector": {
"command": "java",
"args": [
"--add-modules", "jdk.jdi,jdk.attach",
"-jar", "/path/to/mcp-jdwp-java.jar"
],
"env": {
"MCP_TIMEOUT": "30000",
"MCP_TOOL_TIMEOUT": "120000"
}
}
}
}
The plugin-managed install also passes -DLOG_PATH=${CLAUDE_PLUGIN_ROOT}/logs/mcp-jdwp-inspector.log so the server's SLF4J log lands inside the plugin directory; add the same flag if you want a fixed log path here.
2. Launch your Java application with JDWP
Maven Surefire (test debugging):
mvn test -Dmaven.surefire.debug
Starts the JVM with JDWP on port 5005, suspended until a debugger connects.
Any Java application:
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
3. Attach from Claude Code
From there, just tell the agent what to do — "attach to JDWP, set a breakpoint in OrderService.createOrder line 42, run the failing test, dump the context when it hits". The bundled java-debug skill teaches the agent the right tool sequence; the next section gives you a smoke test that exercises the surface.
Find the Bug — test flights
The jdwp-sandbox module ships 10 deliberately broken Java classes. Each one compiles fine, looks reasonable at first glance, and fails its test with a confusing message. Your job: attach with the JDWP MCP server and find the root cause.
This doubles as a setup verification — if you can solve these, everything works.
Each flight is built so that one tool group is the path of least resistance, and lists a par — the minimum tool calls that cleanly reveal the root cause. Hitting par is the elegant solve; the suite as a whole exercises the full tool surface (expression eval, exception breakpoints, field watchpoints, event history, marks, logpoints, runtime mutation, multi-thread inspection, and in-target probes).
Just installed the plugin? Grab the sandbox zip
The fastest path for fresh plugin users is a self-contained zip — no clone, no reactor build:
curl -L -o jdwp-sandbox.zip \
https://github.com/FgForrest/mcp-jdwp-java/releases/latest/download/jdwp-sandbox.zip
unzip jdwp-sandbox.zip && cd jdwp-sandbox
Inside, you get a parentless Maven project with src/, a pom.xml, a flight-game README.md, and a CLAUDE.md that briefs the agent on the game's house rules. Open the folder in Claude Code and ask it to play flight #1 — the bundled CLAUDE.md keeps it honest (no peeking at source, no spoiler-fetching). Continue with the workflow below.
How to launch a test flight
Start Claude Code from the sandbox folder (or this repo's root, if you cloned) and type:
Use JDWP to debug <TestClass> in the jdwp-sandbox module — the test is failing, find the root cause.
#1 The Phantom Session
Difficulty: Moderate | Test: SessionStoreTest | Package: session | Par: 4 | Exercises: expression eval
Symptom: retrieve() returned null — a session was stored, upgraded, and then... vanished from the map.
UserSession is used as a HashMap key, with both userId and role in hashCode(). upgradeUserRole() calls session.upgradeRole("PREMIUM"), which mutates role — changing the hashCode while the key is still in the map. The entry sits in the old hash bucket; lookups compute the new hash and search the wrong bucket.
Debug path: Breakpoint before and after upgradeRole(). Use jdwp_assert_expression("session.hashCode()", "<value-before>") after the upgrade — MISMATCH confirms the hash drifted.
#2 The Swallowed Exception
Difficulty: Hard | Test: EventBusTest | Package: events | Par: 4 | Exercises: exception breakpoint + trigger gate
Symptom: expected stock < 100 but was 100 and an empty error summary — the order was supposed to reserve inventory, but nothing happened and nobody complained.
OrderEvent narrows the raw quantity through byte (200 → -56), so Inventory.reserve() throws IllegalStateException. EventBus.dispatch() runs each handler on a single-thread executor as a fire-and-forget task — the Future is never inspected, so the exception is captured inside java.util.concurrent.FutureTask.run (a JDK frame) and discarded. No sandbox frame ever holds the throwable, so a breakpoint-context dump has nothing to show, and getErrorSummary() stays empty.
Debug path: Because the exception is caught (by FutureTask), an uncaught-only breakpoint never fires. Set a line BP at the test/dispatch entry as a trigger, then jdwp_set_exception_breakpoint("java.lang.IllegalStateException", caught=true, triggerBreakpointId=<trigger>) so bootstrap exceptions don't drown the signal. The throw site suspends with qty = -56 in the frame's locals.
#3 The Time Traveler's Config
Difficulty: Hard | Test: ConfigurationProviderTest | Package: config | Par: 4 | Exercises: field watchpoint + event history
Symptom: expected timeout=5000 but was 0 — the timeout was set during construction, yet it reads back as the default.
ConfigurationProvider's constructor calls init(5000), so the timeout is correct. Then runMaintenanceSweep() starts a background config-reaper thread that wrongly treats the live instance as stale and calls resetToDefaults(), writing the timeout back to 0. By the time the test reads it, the heap shows 0 and no local holds 5000 — a single context dump looks like the value was never set.
Debug path: jdwp_set_field_breakpoint(className="…config.Configuration", fieldName="timeout", mode="modification"), then jdwp_get_events shows the two stores in order — 0→5000 on the main thread, then 5000→0 on config-reaper. jdwp_get_stack on the second event names the reaper as the clobberer.
#4 The Audit That Lies
Difficulty: Hard | Test: TransferServiceTest | Package: bank | Par: 4 | Exercises: field watchpoint + stack narration
Symptom: expected discrepancy=0 but was non-zero — money is neither created nor destroyed, yet the audit says the books don't balance.
TransferService.transfer() is not atomic: it calls source.withdraw(), then auditService.snapshotBalances(), then destination.deposit(). The snapshot captures the intermediate state where money has left the source but hasn't arrived at the destination — showing a total of 1500 instead of 2000.
Debug path: jdwp_set_field_breakpoint on AuditService.lastTotalSnapshot (modification). It fires mid-transfer with the stack at TransferService.transfer between the debit and the credit; jdwp_get_stack plus a live balance read confirm the captured dip.
#5 The Field That Lies
Difficulty: Hard | Test: UserProfileTest | Package: userprofile | Par: 3 | Exercises: field watchpoint blind spot + before/after bisect
Symptom: expected: <Alice> but was: <alice> — the welcome message rendered correctly, yet the user's stored display name has silently changed casing.
LoginNormalizer.welcomeMessage calls a private canonicalForm helper, which delegates to a static DisplayNameMirror nested class. The mirror uses Field.setAccessible(true) + Field.set(profile, canonical) to write the lower-cased form straight into UserProfile.displayName, bypassing the public setter. From the test's side the formatter looks read-only, and a field watchpoint cannot see the write either: JDI watchpoints fire only on the putfield/getfield bytecodes (and JNI field access). Field.set ends in an Unsafe store straight to memory, which trips no watchpoint, in modification mode or in access mode.
Debug path: The tell is a modification watchpoint that fires once, for the constructor's ordinary assignment, and never again, although the value flips before the assertion: the write does not go through a field store. The par path takes three calls:
jdwp_set_field_breakpoint(className="one.edee.jdwp.sandbox.userprofile.UserProfile", fieldName="displayName", mode="modification"). The class is not loaded yet, so the watchpoint waits for it.jdwp_resume_until_event: the watchpoint fires in theUserProfileconstructor, which stores"Alice".jdwp_resume_until_event: the test fails and its JVM exits. The[VM_DEATH]reply lists the watchpoint with one hit. The only store the JVM saw wrote"Alice", yet the test read"alice", so the write is reflective or goes throughUnsafe. Between the two, the test calls onlywelcomeMessage, on line 24.
Bisecting takes three calls as well and shows where the value flips, though not how: jdwp_set_breakpoint("one.edee.jdwp.sandbox.userprofile.UserProfileTest", 27), just after welcomeMessage, then jdwp_resume_until_event, then jdwp_evaluate_expression("profile.getDisplayName()"), which is already "alice". When the old and new strings could be equal, compare System.identityHashCode(profile.getDisplayName()) instead.
That is the evidence to read LoginNormalizer with: canonicalForm → DisplayNameMirror.mirror → Field.set. To name the writing call from the debugger alone, break at line 24 instead and keep bisecting inside welcomeMessage with jdwp_step_into / jdwp_step_over, comparing the identity after each line: it flips across DisplayNameMirror.mirror(profile, canonical) in canonicalForm. A line BP on UserProfile.setDisplayName never fires — the setter is genuinely unused.
#6 The Doppelgänger Cart
Difficulty: Moderate | Test: CheckoutTest | Package: cart | Par: 6 | Exercises: marked instances ($label)
Symptom: expected 45.0 but was 0.0 — the cart goes through pricing and discounting, yet its total never changes.
Checkout.process runs the cart through validate → price → discount. validate was changed to return a defensive snapshot — return new Cart(cart) — so every later stage mutates the copy. process reassigns its local to the copy as it threads the stages, so no single frame holds both the caller's cart and the copy at once. The test ignores the return value (assuming in-place mutation), so its cart stays at total 0. The copy's fields are identical to the original — only identity differs.
Debug path: Break in the test once the cart exists, jdwp_mark_instance(label="input", objectId=<id>) to pin it, then set a breakpoint in price (or applyDiscount) with condition cart != $input. It fires — the stage is working on a doppelgänger, not the caller's cart.
#7 The Heisenbug Race
Difficulty: Hard | Test: RaceCounterTest | Package: race | Par: 3 | Exercises: logpoints (non-stopping)
Symptom: expected 2 but was 1 — two threads each increment a counter once, but one increment vanishes.
RaceCounter.increment reads the count, waits at a CyclicBarrier so both threads have read before either writes, then writes back read + 1. Both threads read 0 and both write 1 — one increment is lost. A suspending breakpoint serializes the threads and makes you juggle two parked threads to reconstruct what happened.
Debug path: Set a non-suspending jdwp_set_logpoint at the read site logging the thread name and count (or a field logpoint on count). Let the test run, then jdwp_get_events — both racer-1 and racer-2 are recorded reading 0 before either writes, which is the lost update laid out in order.
#8 The Magic Patch
Difficulty: Warm-up | Test: DateParserTest | Package: parser | Par: 4 | Exercises: runtime mutation (set_local)
Symptom: NumberFormatException: For input string: "15 " — a date string that looks right fails to parse.
DateParser.parse splits YYYY-MM-DD and Integer.parseInts each part. The feed delivers "2026-05-15 " with a trailing space, so parseInt("15 ") throws. The parser never trims its input.
Debug path: Four calls. jdwp_set_breakpoint at the top of parse, jdwp_resume_until_event to stop there, jdwp_set_local("input", "2026-05-15") to drop the trailing space (or break on the test's line 19 and patch fromFeed instead), and jdwp_resume. The parse now succeeds and the test passes — confirming the trailing space is the whole story, with no rebuild. The exception message already shows the stray space, so the stop needs no inspection call; the confirming resume counts.
#9 The Polite Standoff
Difficulty: Hard | Test: TransferDeadlockTest | Package: deadlock | Par: 4 | Exercises: multi-thread inspection (no breakpoints)
Symptom: The test hangs and then fails its join — both transfers never complete.
Account.transfer locks the source account, pauses, then locks the destination. Two transfers in opposite directions (A→B and B→A) each grab their first lock and then wait forever for the other's — a classic lock-ordering deadlock.
Debug path: No breakpoint can fire — the threads execute nothing. jdwp_get_threads shows transfer-A-to-B and transfer-B-to-A in MONITOR state; jdwp_dump_locks prints the lock graph with both monitor holders and the inverse-order cycle; jdwp_get_stack on each thread shows the lock-ordering inversion. (Trying jdwp_evaluate_expression / jdwp_to_string on these threads is refused — invoking a method on a monitor-blocked thread would hang the debugger, which is itself the tell.)
#10 The Oversold House
Difficulty: Hard | Test: BoxOfficeTest | Package: boxoffice | Par: 4 | Exercises: in-target probes (fast logpoint / fast breakpoint, with the user's consent)
Symptom: [Tickets sold for 300 performances of 200000 seats] expected: 60000000L but was: 60001634L — the season ends oversold by a few tickets per performance.
Solved when: you name one specific double sale: the seat, the window that sold it first and the window that sold it again. Knowing that the house is oversold, or by how much, is not enough.
BoxOffice.trySell checks that the seat is free, prices it (reading the pricing desk's volatile surcharge), and only then records the buyer: a check-then-act race. Two windows that check the same free seat within the pricing step both sell it, and sold counts both. About six seats per performance are sold twice. Customer numbers are window numbers, and owners[] keeps only the last buyer, so counting seats against sold() after the fact shows the overselling but cannot name the window that sold a seat first. Only catching the collision can.
Debug path: On the sell line (BoxOffice.java:54, seatOwners[seat] = customer), the condition seatOwners[seat] != 0 is true when another window sold the seat after this one checked it. Both in-target tools rewrite the class's bytecode, so each needs the user's explicit consent: the first call, without userApproved, arms nothing and returns a risk briefing to relay word for word. BoxOffice is not loaded when the test JVM starts; the approved probe waits for the class to load and arms on its next hit.
- Fast logpoint, 4 calls (par):
jdwp_set_fast_logpointonone.edee.jdwp.sandbox.boxoffice.BoxOfficeline 54, with that condition and the expressionseat + " sold to " + seatOwners[seat] + ", resold to " + customer, withoutuserApproved: the risk briefing, relayed to the user.- The same call with
userApproved=true, once the user agrees. jdwp_resume_until_event: the test runs to its end. The reply is[VM_DEATH]and says how manyFAST_LOGPOINThits were recorded.jdwp_get_events: the double sales, each with its seat and the first and second window.
- Fast breakpoint, 5 calls: the briefing and the approved
jdwp_set_fast_breakpointcall with the same condition, thenjdwp_resume_until_event: the first double sale stops with every window parked.jdwp_get_breakpoint_contextshowsseatand its second buyer,customer.jdwp_evaluate_expression("seatOwners[seat]")gives the first buyer; array elements are not in the context dump.
The logpoint does not catch every double sale. Its condition reads a plain int[] without synchronization, so a window that reads the seat before the other window's write is visible logs nothing; one live run caught 1,460 of 2,472 double sales. One is enough to solve the flight. The probe also sits inside the race window, between the check and the write, so it widens the race rather than hiding it: runs with the probe sold more seats twice, not fewer.
An ordinary conditional breakpoint or logpoint on that line goes through the debugger on each of about 60 million passes, at 3–30 ms each. The box office closes first, and the serialized windows stop colliding.
Вопросы о jdwp-java MCP
Какие инструменты предоставляет jdwp-java MCP?
Он предоставляет 50 инструментов, включая jdwp_connect, jdwp_disconnect, jdwp_reconnect, jdwp_wait_for_attach, jdwp_get_version.
Что ИИ-агент может делать с помощью этого сервера?
Агент может проверять состояние во время выполнения (локальные переменные, поля), выполнять произвольный Java-код в приостановленных фреймах, изменять работающие программы, а также устанавливать условные точки останова или точки останова по исключениям.
Какие системные требования необходимы для запуска сервера?
Для запуска сервера в вашем PATH должен быть установлен JDK 17+, а для сборки из исходного кода требуется JDK 21+.
Как подключить его к моему Java-приложению?
Вы должны запустить ваше Java-приложение с включенным JDWP, например, используя аргумент -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005.