fix(mcp): make BTrace MCP deployment work and test it end to end - #6
Merged
Merged
Conversation
The Codex manifest only declared skills, so Codex installs never got the BTrace MCP server. Codex's plugin MCP format does not expand a plugin-root placeholder in args but resolves a relative cwd against the installed plugin root, so the server is launched with cwd "." and a relative script path. The file lives under .codex-plugin/ so Claude Code does not also auto-load it as a root .mcp.json. Verified: `codex mcp get btrace --json` in an isolated CODEX_HOME resolves cwd to the installed plugin root; running that command from there answers initialize and lists all 7 tools; scripts/validate-marketplace.sh passes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Three defects made every deployment fail against a real JVM: - Attach failed with "No masked btrace.jar found": the BTrace client finds its agent through btrace.jar.path or a classpath entry named btrace.jar, but JBang supplies btrace-<version>.jar. The server now exports the jar it already located as btrace.jar.path. - A successful deploy never returned: the client's submit runs the probe command loop until the probe exits. Deploys now submit on a daemon thread, stop buffering output after the status reply, and close the client when deployment times out or fails. - deploy_script named the source BTraceScript_<timestamp>.java, so public classes failed to compile and other scripts fell back to short syntax (no fields, no @export). The file is now named after the first declared class. The fake test Client's submit now blocks until close, as the real one does, so unit tests catch a blocking deploy. Verified: ./gradlew clean check (17 tests, coverage rule) and ./gradlew e2eTest (9 passed, list_probes skipped) pass; with the jar-path fix disabled, e2eTest fails 5 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Unit tests replace the BTrace client with fakes, so attach, deployment and lifecycle defects went unnoticed. The new e2eTest task launches the server through JBang, as the plugin manifests do, against a sample app and exercises every tool and prompt. No tool returns probe output after deployment, so probe effects are checked through @export counters read with jcmd: instrumentation fires, send_event runs the handler, a detached probe keeps running, and exit_probe stops it. The sample app lives outside io.btrace, which BTrace does not instrument, and the agent uses a free port so other BTrace sessions on the host do not interfere. list_probes is disabled because the BTrace client calls System.exit after listing probes. The task needs jbang and a resolvable io.btrace:btrace artifact, so it is not part of check. Verified: ./gradlew clean check e2eTest → BUILD SUCCESSFUL, e2eTest 9 passed, 1 skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
list_probes terminates the MCP server (the BTrace client calls System.exit after listing probes), dropping every open probe session. The btrace-mcp-operations skill now tells agents not to call it and to track deployments themselves. It also no longer promises output inspection: tools return only a probe's initial output, so it points at @export counters read with jcmd for ongoing results. Verified: scripts/validate-marketplace.sh passes; the list_probes behavior is reproduced by the disabled e2eTest case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The e2e suite could only launch the server through JBang, which resolves io.btrace:btrace:3.0.0-SNAPSHOT; that artifact exists only in a local Maven repository, so CI could not run the suite against its own BTrace build. Setting BTRACE_JAR (or -PbtraceJar) to a masked btrace.jar now runs the compiled server on a plain classpath with that JAR. Without it, the JBang launch used by the plugin manifests is still exercised. A blank value counts as unset, and a path that is not a file fails fast. Verified: BTRACE_JAR=<btrace-dist btrace.jar> ./gradlew e2eTest → 9 passed, 1 skipped, no JBang in the server log; blank BTRACE_JAR → JBang launch (fails without jbang on PATH, passes with it); directory path → "BTrace JAR not found"; ./gradlew clean check --configuration-cache → BUILD SUCCESSFUL. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The MCP e2e suite needs a BTrace distribution, and the io.btrace:btrace:3.0.0-SNAPSHOT artifact JBang resolves is not published. The job builds btraceio/btrace (default branch, or a ref given on manual dispatch) with :btrace-dist:btraceJar, which skips BTrace's own tests, and runs ./gradlew e2eTest with BTRACE_JAR pointing at the resulting masked jar, so it works with whatever version that build produces. No LLM is involved. Logs and reports are uploaded on failure. Verified: actionlint reports no findings; the job's steps reproduced locally against a fresh clone of btrace develop (ee8434e): :btrace-dist:btraceJar → BUILD SUCCESSFUL, jar lookup finds exactly one jar, e2eTest → 9 passed, 1 skipped. Not yet run on a GitHub runner. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jbachorik
force-pushed
the
agent/move-btrace-mcp
branch
from
October 2, 2026 20:52
fc07b1a to
cb5b89b
Compare
jbachorik
marked this pull request as ready for review
October 2, 2026 20:56
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Running the BTrace MCP server against a real JVM showed that most operations were broken. The unit tests had missed this because they replace the BTrace client with fakes. This PR fixes the MCP-side defects, adds end-to-end tests that run the server against a live sample JVM (no LLM involved), runs those tests in CI against a BTrace build, and corrects the skill guidance.
Server fixes (
5cba3ee)No masked btrace.jar found. The BTrace client finds its agent throughbtrace.jar.pathor a classpath entry namedbtrace.jar, but JBang suppliesbtrace-<version>.jar. The server now setsbtrace.jar.pathfrom the jar it has already located.submitruns the probe command loop until the probe exits. Deploys now submit on a daemon thread, stop buffering output after the status reply, and close the client when deployment times out or fails.deploy_scriptrejected public classes. It named the sourceBTraceScript_<timestamp>.java, so public classes failed to compile and every other script fell back to BTrace short syntax, which allows no fields and no@Export. The file is now named after the first declared class.Codex (
212707d): the Codex manifest declared only skills, so Codex installs had no MCP server. Codex's plugin MCP format does not expand a plugin-root placeholder inargs(checked in codex-rsplugin_config.rs), but it resolves a relativecwdagainst the plugin root, so the server is launched withcwd: ".". The file sits under.codex-plugin/so Claude Code does not also load it as a root.mcp.json.End-to-end tests (
0037366,d0802cb): the new./gradlew e2eTestdrives every tool and prompt against a sample app.@Exportcounters read withjcmd: instrumentation fires,send_eventruns its handler, a detached probe keeps running, andexit_probestops it.BTRACE_JARset, it runs on a plain classpath against that jar.io.btrace, which BTrace does not instrument, and the agent uses a free port.CI (
cb5b89b): the newMCP e2eworkflow buildsbtraceio/btrace(default branch, or a ref given on manual dispatch) with:btrace-dist:btraceJarand runse2eTestagainst the resulting jar. It never depends on the unpublishedio.btrace:btrace:3.0.0-SNAPSHOTartifact.Skills (
9cff5dc): agents are told not to calllist_probes(see limitations), and the skill no longer promises output inspection.Rejected alternatives:
$schemaAgent Plugins MCP format: it does support${PLUGIN_ROOT}, but it isn't needed, and thecwdform is verified.System.exit: deferred. The fix belongs in the BTrace client.How verified
./gradlew clean check(mcp)BUILD SUCCESSFUL, 17 unit tests passed, 80% line-coverage rule passed./gradlew e2eTest(JBang launch)list_probes)BTRACE_JAR=<fresh btrace develop ee8434e build> ./gradlew e2eTestBTRACE_JAR=<directory>/ blankBTRACE_JARBTrace JAR not found/ falls back to JBangcodex mcp get btrace --json(isolatedCODEX_HOME, plugin installed from this repo)cwdresolves to the installed plugin root; that command answersinitializeand lists 7 toolsMCP e2eworkflow on GitHub (first run, this PR)e2eTeststeps all succeededactionlint .github/workflows/mcp-e2e.yml:btrace-dist:btraceJar, jar lookup,e2eTest)scripts/validate-marketplace.shon this branch merged intoorigin/mainRepository consistency valid: 4 plugins, 22 skills,Marketplace validation passed.Coverage notes
The e2e suite covers all 7 tools and 3 prompts on one sample JVM, including the session lifecycle: deploy, event, detach and exit. It does not cover a failed probe start, because the BTrace client still calls
System.exit(1)there. It does not cover agents choosing these tools; LLM evals stay manual and are not part of CI.Not verified / known limitations
list_probesstill terminates the server.Client.connectAndListProbesin BTrace callsSystem.exit(0), andsubmitcallsSystem.exit(1)on failure. The fix belongs in btraceio/btrace; the e2e case is@Disabledand the skill warns against the tool until then.validate.yml. The new workflow pins actions to commit SHAs (the same ones BTrace's CI uses), whilevalidate.ymluses major tags.Breaking changes / migration
None.
deploy_scriptnow uses the script's class name as the source file name; scripts that relied on short syntax still work.Links
Depends on #5 (base of the stack). Retarget to
mainafter #5 merges.🤖 Generated with Claude Code