Reclazz MCP server
An MCP server that turns the running Reclazz agent into tools a coding agent can call: check whether Reclazz is attached, nudge a scan after a build, ask what still needs a restart, or diagnose why a class did not reload. It is a thin, loopback-only client of the agent's status socket and asks the agent nothing beyond its own state.
It lives in :mcp-server, not the agent jar, because the agent's own tests
forbid the shipped agent from opening a client socket.
The client acceptance matrix records independent SDK, real build-tool and OS coverage, with runnable tests and explicit untested lanes.
Running it
The server requires Java 17 or newer and speaks JSON-RPC over stdio. It is a standalone jar containing its runtime dependencies; it does not need IntelliJ, Gradle or a separately installed Gson at runtime.
Build and install locally
From a source checkout, build matching agent/MCP jars and test the MCP package:
./gradlew :agent:shadowJar :mcp-server:test :mcp-server:mcpRelease
version=$(sed -n 's/^pluginVersion=//p' gradle.properties)
install_dir="$HOME/.local/share/reclazz/$version"
mkdir -p "$install_dir"
cp "mcp-server/build/distributions/mcp/reclazz-mcp-$version.jar" \
"mcp-server/build/distributions/mcp/reclazz-mcp-$version.jar.sha256" "$install_dir/"
(cd "$install_dir" && shasum -a 256 -c "reclazz-mcp-$version.jar.sha256")
These are macOS/Linux shell commands; Linux can use sha256sum -c in place of
shasum -a 256 -c. On Windows copy the same two files to a permanent directory
and compare the SHA-256 from Get-FileHash
with the first field of the sidecar (ignoring hex letter case). The sidecar names only the jar, so relocation is supported.
Stop on a checksum mismatch. This stages/installs local files; it does not publish.
Release assets
The updated tag workflow will attach reclazz-mcp-X.Y.Z.jar and
reclazz-mcp-X.Y.Z.jar.sha256 to future GitHub releases, next to the matching
agent jar. This change is unreleased; it does not add MCP assets to existing
releases. When a release contains these assets, download both from that same
tag, put them together in a permanent directory and verify the checksum as above.
Use matching agent and MCP builds for BUILD/VERIFY support. A checksum detects
file corruption; it is not a signature or proof of publisher identity.
Configure a stdio client
For clients using an mcpServers JSON configuration, merge this entry into their
existing configuration. Replace the example paths and X.Y.Z; use the absolute
path to your Java 17+ executable if the client cannot find java on its PATH:
{
"mcpServers": {
"reclazz": {
"command": "/absolute/path/to/java",
"args": ["-jar", "/absolute/path/to/reclazz-mcp-X.Y.Z.jar",
"--project-dir", "/absolute/path/to/project"]
}
}
}
Keep each argument separate, even for paths with spaces. Do not include shell
quotes inside the JSON strings. On Windows use escaped backslashes or forward
slashes (for example C:/Tools/Reclazz/reclazz-mcp-X.Y.Z.jar). Client-specific
configuration file locations vary; this entry does not modify them automatically.
Clients with another configuration format need the same executable and arguments.
Restart/reconnect the MCP client after changing the entry.
The application must separately run with the Reclazz agent attached. The
--project-dir option makes startup independent of the client's working
directory. Discovery checks only that directory's .reclazz/agent.port and
.idea/reclazz/agent.port, in that order. For a custom port file, also add
"--port-file", "relative/or/absolute/agent.port" to the argument array.
The port file may be absent at startup; the application can create it later.
An absent, unreadable, malformed or unreachable explicit target never causes
fallback to a different file or project.
Target selection and overrides
Startup accepts only --project-dir PATH, --port-file PATH, or --help.
Unknown/duplicate options and missing/blank/invalid values fail before stdio
starts (exit2, stderr only). The selected project must be an existing directory.
Help writes usage to stderr and exits0. There is no shared configuration file,
process search, parent-directory search or machine-global default.
Relative startup project paths resolve from the MCP process's startup directory. A relative startup port file resolves from the selected project, or that startup directory when no project is selected. Each server keeps its own defaults. Per-tool selection works as follows:
projectDiroverrides the project for that call; relative values resolve from the startup project/directory. It resets the startup port-file default, so project-only overrides discover the new project's own files.- Per-call
port, thenportFile, thenhybrisHometake precedence over the startup port file. RelativeportFile/hybrisHomepaths use the effective project directory. An explicithybrisHomeuses only its.reclazz/agent.port; absence does not fall back to the project/client directory. - Overrides affect only that call. Invalid input does not retry against defaults.
A call selecting another project's port must also select its
projectDirwhen a startup project is configured.
An explicit project opts into a launch-directory check: on the same agent
connection, before the requested command, MCP requires a valid correlated
DOCTOR result whose canonical absolute workingDirectory equals or is below
that project. Missing/unsupported/malformed evidence, a relative working
directory or another project's directory fails closed. This also applies to
status and batch verification. It prevents a stale port pointing to an agent
launched outside the selected tree from receiving BUILD/SCAN/reload commands.
Working directory is not proof of repository root, watched-output coverage or
which of two JVMs launched in the same project is intended. Inspect DOCTOR PID,
session and watch evidence for those distinctions. If the application launches
outside its project tree, use explicit --port-file alone (or per-call portFile)
without project selection; this selects an endpoint but does not validate its
project. No-argument startup retains discovery within the startup working
directory and has no project check. Old agents without DOCTOR cannot satisfy an
explicit project check; use matching agent/MCP builds.
Smoke check without an attached application
Set MCP_JAR to the absolute installed jar path and run in a macOS/Linux shell:
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05"}}' \
'{"jsonrpc":"2.0","method":"notifications/initialized"}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
| java -jar "$MCP_JAR"
Expect two JSON response lines: initialization reports reclazz-mcp and the
jar's version; the tools list contains the nine tools below. The notification
has no response. EOF closes the server. If Java cannot open the jar, check the
absolute path; if it cannot load its classes, use the packaged jar from
build/distributions/mcp, not the plain development jar. A missing attached
application is a separate condition: reclazz_status reports attached:false.
Tools
| Tool | What it does |
|---|---|
reclazz_doctor |
Read correlated target JVM, session, live watch registration and capability evidence, with next checks |
reclazz_status |
Whether the agent is attached and how it is doing (reloads, failures, latency, watched directories), as JSON |
reclazz_build |
Signal started, ok or failed; waits for the agent to acknowledge the signal, and returns a tool error if unconfirmed |
reclazz_verify_batch |
Read 1–32 unique class/hash pairs on one connection within a total timeout; cancellable through stdio |
reclazz_verify |
Structured exact-byte reload receipt; requires className and lower-case sha256; only status:applied is success |
reclazz_scan |
Ask the agent to look at the watched directories now and reload changed classes, instead of waiting for its next poll |
reclazz_pending |
Restart-required changes plus manual-pause and pending counts |
reclazz_reload_control |
Pause/resume automatic reload admission or inspect its state; never releases BUILD holds |
reclazz_diagnose |
Why a given class did or did not reload last time (requires className) |
Each tool accepts optional projectDir, portFile, port and hybrisHome arguments to
locate the agent; reclazz_diagnose also requires className.
Tool execution errors
reclazz_scan, reclazz_pending and reclazz_diagnose return isError:true
with a text explanation when the agent cannot be reached or its handshake is
invalid. Before sending a command, the client requires a JSON CONNECTED event
with numeric status-socket version:1 and a nonblank string agent release.
Malformed or unsupported handshakes do not terminate the MCP stdio session.
Pending/diagnose also return a tool error if the connection fails, a diagnostic
response is malformed, or no INFO answer arrives before disconnect/timeout.
Silence does not mean nothing needs a restart or no diagnosis exists. An explicit
empty-ledger answer from the agent remains a successful result. Invalid tool
arguments still return a JSON-RPC error rather than a tool execution result.
reclazz_status remains an attachment diagnostic: attached:false is a
successful observation. If the handshake succeeds but health cannot be read,
it reports attached:true with a reason describing the missing health data.
SCAN has no acknowledgement in the status-socket protocol. Its isError:false
means the command was written to a validated connection; acceptance and reload
completion are unconfirmed. Use reclazz_verify for exact-byte reload evidence.
The legacy pending/diagnose INFO stream is broadcast and has no request ID or
end marker. Collected lines are diagnostic observations, not proof that the
entire response was received or that every line belongs to this request.
Protocol version negotiation
The server supports 2025-06-18 and 2024-11-05. Send a nonblank string
params.protocolVersion with initialize; a missing, blank or non-string version
returns JSON-RPC -32602. Either supported requested version is returned unchanged.
Any other nonblank version receives 2025-06-18 as the supported alternative;
the server does not echo arbitrary versions. The selected version belongs to that
stdio session. An invalid initialization request does not change it.
Following the MCP version negotiation rule,
a client that supports the returned version sends notifications/initialized
and continues. A client that cannot support it should disconnect. This server
advertises tools only. It does not implement HTTP transport, resources, prompts,
sampling or elicitation. Full initialization-order enforcement is not added here;
for compatibility, direct calls before initialization retain the legacy format.
Structured tool contracts
Sessions negotiating 2025-06-18 receive outputSchema and annotations from
tools/list. Every tool execution result, including isError:true, has a JSON
object in structuredContent and the identical serialized JSON in its text
content block. Invalid requests/arguments remain JSON-RPC errors without a tool
result. Explicit 2024-11-05 sessions retain the existing text results and omit
these newer fields.
| Tool | Structured result |
|---|---|
reclazz_doctor |
status:observed with JVM/watch/capability evidence, or unavailable; separate clientWorkingDirectory, nextActions, reloadConfirmed:false |
reclazz_reload_control |
status:acknowledged with action, paused, pending counts and buildHold, or unavailable; reloadConfirmed:false |
reclazz_status |
Existing attached, optional agent, protocol, port, health, reason fields |
reclazz_scan |
status:sent or unavailable, detail, reloadConfirmed:false |
reclazz_build |
status:acknowledged or unavailable, requested state and owner, detail, reloadConfirmed:false |
reclazz_pending |
status:observed or unavailable, lines, detail, complete:false |
reclazz_diagnose |
Same diagnostic fields plus requested className |
reclazz_verify_batch |
status:applied, incomplete or unavailable; ordered results, requestedCount, sessionId, allApplied, atomic:false, reloadConfirmed:false |
reclazz_verify |
Existing correlated receipt fields; status:applied alone is success, or the existing unavailable failure object |
sent confirms a socket write, acknowledged confirms the BUILD signal, and
observed describes collected diagnostic lines. None proves reload completion.
The diagnostic complete:false reflects the lack of a response end marker in
the broadcast stream. VERIFY preserves its exact-byte checks, and application
behavior still needs its own test.
Status, doctor, pending, diagnose, verify and verify-batch advertise readOnlyHint:true and
openWorldHint:false. Scan, build and reload-control advertise readOnlyHint:false,
destructiveHint:true, idempotentHint:false and openWorldHint:true: live
reload can replace behavior and invoke application callbacks with external
effects. These conservative hints are metadata, not permission or safety proof.
The schemas use JSON Schema 2020-12. The contract tests export real responses to
mcp-server/build/tool-contract-fixtures. An independent check can be run with
an existing Python jsonschema 4.x installation after the tests:
./gradlew :mcp-server:test --rerun
python3 mcp-server/src/test/python/check_tool_contracts.py
The Python checker validates the schemas, successful and failed results, and rejects deliberate missing fields, wrong types, invalid statuses and invented completion flags. It is an optional development check, not a runtime dependency.
Input validation
Each stdio line carries one JSON-RPC 2.0 object. Requests have a string method,
a string or integer ID (not null), and optional object params. Notifications
have no ID and receive no response. Batch arrays are not supported by this server.
Malformed JSON returns -32700, invalid request envelopes return -32600, and
invalid method/tool parameters return -32602. When an ID cannot be determined,
the error has id:null. The process continues reading subsequent lines.
Frames are limited to 65,536 Java characters, excluding the newline; an oversized
line is drained and rejected before the next line is read. Known tool arguments
must be strings, at most 4,096 Java characters, without control characters.
className for diagnose/verify must be a binary Java name of at most 256
characters (Unicode identifiers and $ inner classes are supported).
timeoutMs is an integer string in 1–60,000, with defaults of 5,000 for
build/verify and 2,000 for other tools. Paths with spaces are supported.
Invalid numeric ports produce tool errors for scan/pending/diagnose; status
reports them as attached:false.
Diagnose rejects invalid names before connecting. The socket writer additionally rejects commands containing control characters or exceeding 512 Java characters, so a supplied name cannot insert a second line command.
What it does not do
It opens one connection, to 127.0.0.1, to the agent already running on this
machine. It sends no telemetry and reaches nothing off the machine, the same
promise the agent itself keeps.
Protecting terminal builds
Build the current agent and MCP jars together from this source tree:
./gradlew :agent:shadowJar :mcp-server:shadowJar. This receipt protocol is new;
it is not a promise that an older published agent supports it. Attach the built
agent to your development app and use the resulting MCP jar as MCP_JAR below.
From the consuming project's directory, wrap the whole compile command:
java -cp "$MCP_JAR" com.onurkat.reclazz.mcp.BuildMain \
--port-file .reclazz/agent.port -- ./gradlew classes
java -cp "$MCP_JAR" com.onurkat.reclazz.mcp.BuildMain \
--port-file .reclazz/agent.port -- mvn compile
--port N selects an explicit loopback port instead. With neither option, the
usual port files are located in the current directory. --timeout-ms N changes
the receipt timeout (default 5000; range 1–60000). Command arguments after --
are passed directly, without a shell. Quote paths containing spaces. On Windows,
use your command interpreter explicitly for .bat/.cmd build launchers.
The wrapper generates a unique owner token and prints it to stderr before
connecting. Retain that token: --owner TOKEN reuses it for recovery after a
failed or disconnected build. Owner tokens are 1–64 ASCII letters, digits,
underscores or hyphens. A fresh token cannot take another outstanding hold.
The wrapper waits for an owner-correlated started receipt before launching the command.
It sends ok only after exit zero, and failed after a nonzero exit, launch
failure or interruption. A lost connection is never replaced mid-build. A
missing agent or missing start receipt prevents compilation; an unconfirmed
finish returns nonzero. A confirmed finish preserves the command's exit code.
The app keeps running. Class output from a failed build stays held until a
subsequent successful build by the same owner. If the wrapper is killed, the
installed hold stays in place. Run another complete wrapped build with the
printed --owner TOKEN to recover; do not send ok merely
to clear the warning. A lost ok receipt leaves an uncertain result, not proof
that no reload occurred.
Different named owners and unowned IDE commands cannot change an owned hold.
A named start cannot take an active legacy hold. Ownership releases after
successful output capture, asynchronously after the ok receipt. Lost owner
tokens require an application/agent restart; no timeout or force unlock exists.
Tokens identify cooperating builds; they are not authentication and cannot
protect output from a compiler that bypasses the hold. Use a unique token for
each independent build and retain it until recovery is complete.
Use compile-only commands, not long-running bootRun/spring-boot:run. Normal
unwrapped builds retain legacy behavior. The Gradle/Maven attachment plugins
do not install this wrapper automatically. Receipt confirms the build signal,
not completion of live reload; exercise the endpoint/test to verify behavior.
Protecting a build through MCP
For a client that launches its own compiler, call reclazz_build with
{"state":"started","owner":"build-unique-id","portFile":"/absolute/path/.reclazz/agent.port"} and
require isError:false before launching compilation. Afterwards call the
same tool against the same running agent with the same owner and state:"ok" only for exit zero,
or state:"failed" for failure/cancellation. Do not restart the app or run
another builder between those calls. If the target changes, stop the sequence;
the terminal wrapper is preferable when one persistent connection is required.
reclazz_scan alone provides no failed-build protection and cannot release a
hold. The MCP server signals states; it does not launch arbitrary build commands
or place compiler output into its JSON-RPC stream. The caller must choose and
retain a unique owner; it is required in both supported MCP protocol versions.
Missing or malformed owner is an invalid argument. Ownership rejection or an
old/wrong-owner receipt is a tool error. Use the retained owner for started,
failed and successful recovery; a disconnected MCP call does not release it.
Verifying the compiled change
For a concrete first installation, use the Maven verification recipe: it combines DOCTOR identity, class-file hashing, this receipt and an observable response, with next steps for wrong targets, stale sessions and unwatched output.
Build and attach matching current agent/MCP jars; older published agents may not
support this tool's socket command. After a successful protected compilation,
compute SHA-256 of each changed .class file and call reclazz_verify with its
binary name (com.acme.OrderService, or com.acme.OrderService$Nested), sha256,
and the same portFile/port used for the build. It returns JSON text with
requestId, sessionId, className, expectedSha256, observedSha256, status, source,
detail and completedAt. Request IDs are generated and checked by the MCP client.
Require isError:false and status:"applied", matching both hashes. All other
states have isError:true: running, not_observed, mismatch, failed or
unverified. Read the detail and poll with a bounded deadline for asynchronous
completion; do not infer success from an increasing global counter. Connection,
timeout and malformed-receipt failures return status:"unavailable" and
isError:true. Bad input returns JSON-RPC invalid params. Optional string
timeoutMs bounds each socket request (default 5000, range 1–60000).
A fresh session ID invalidates assumptions from the earlier app. The receipt is bounded, latest-only evidence that the exact captured bytes finished the reload batch, including deferred refresh; it is not a build job ID or an atomic result for several classes. Warnings, ambiguous classloaders and never-loaded classes cannot produce a positive receipt. See the receipt contract. After verifying the relevant classes, exercise the live endpoint or test to prove the intended application behavior.
Check the target before an agentic build
Call reclazz_doctor using the same endpoint arguments as the other tools.
Its default timeout is 5000 ms. The tool reads one correlated DOCTOR response and
validates its types and bounds. Missing, old, malformed, oversized or uncorrelated
responses produce isError:true, status:unavailable; capability support remains
unknown. Both supported MCP versions receive JSON text; 2025-06-18 also receives
the identical object as structured content.
Compare the target pid, workingDirectory and sessionId against the application
intended for the build. clientWorkingDirectory belongs to the MCP process;
neither directory is an inferred repository root. sessionId matches VERIFY for
this agent instance. Capability booleans reflect installed handlers; observation
can legitimately show incomplete startup. watcherState, live directory count,
refused registration count and a bounded path sample expose watcher evidence.
watchSampleTruncated:true means absence from the sample proves nothing about
coverage. The sample can include source and resource watches too.
Follow nextActions to inspect startup, output paths or a held build. buildHold
reports only the kind of hold, never its owner token, and doctor cannot release it.
Even a successful status:observed reports reloadConfirmed:false; follow the
owned-build, exact-byte VERIFY and application behavior checks for actual proof.
Bounded batch verification and cancellation
reclazz_verify_batch takes items, an array of 1–32 objects containing only
className and lower-case sha256. Class names must be unique and follow the
single-VERIFY rules. The complete array is validated before opening a connection.
Endpoint arguments remain the same. timeoutMs is a string from 1 to 60000,
default "5000", covering connect, handshake and every receipt together.
The tool composes existing VERIFY commands sequentially on one socket; it neither
builds nor polls automatically. results preserves input order. Validated earlier
receipts are retained when a timeout, disconnect, malformed receipt or changed
session makes the remaining entries unavailable. It does not reconnect or combine
proof from different agent sessions. Each socket reply is limited to 16384
characters, with at most 32 replies. sessionId is empty if none was validated.
status:applied and allApplied:true require every individual receipt to be
applied; incomplete means all receipts arrived but at least one was not applied.
unavailable means one or more receipts could not be validated. Both latter cases
return isError:true. Even all-applied is a sequence of point-in-time observations,
not a transactional reload or guarantee that earlier receipts remain current.
atomic and reloadConfirmed remain false. Test application behavior separately.
Both supported MCP versions return JSON text; 2025-06-18 also returns the identical
object as structured content with its output schema.
The stdio server accepts one batch at a time with no queued work. While it is
active, ping and tools/list remain responsive; other requests receive JSON-RPC
error -32000 and can be retried after completion/cancellation. Duplicate active IDs
are invalid. Initialization therefore cannot change a batch's negotiated output
version while it runs. Existing non-batch tools retain synchronous behavior.
For a pending batch, send a notification with the original request ID and no id:
{"jsonrpc":"2.0","method":"notifications/cancelled","params":{"requestId":42,"reason":"No longer needed"}}
Cancellation closes the batch socket, including during handshake or reply waits, and suppresses its response if cancellation wins the completion race. Numeric and string IDs are distinct. Unknown, completed and malformed cancellations are ignored. Initialize and non-batch tools are not cancellable. End of stdin also cancels the active batch and frees its socket; keep stdin open while awaiting a batch result. Cancelling observation does not cancel application reload, undo changes, release a build hold or reclaim its owner. A result already sent may race with cancellation; clients should ignore such a late response, as described in the 2025-06-18 cancellation specification and its 2024-11-05 counterpart.
Build-plugin entrypoints
The Gradle plugin's explicit reclazzSafeBuild task and Maven plugin's standalone
safe-build goal reuse the local MCP jar's BuildMain for one owned connection
around a complete child build. See Gradle safety
and Maven safety. They do not change plain
compile/build invocations, add automatic ok finalizers, or imply a published
plugin release. Retain the owner for recovery and collect exact-byte VERIFY
receipts separately after a successful build.
A reproducible acceptance harness lives at scripts/test-build-plugin-safety.py.
Build local agent/MCP/Gradle-plugin jars and package the Maven plugin, then run:
python3 scripts/test-build-plugin-safety.py --work-dir /tmp/reclazz-safety-proof --gradle /absolute/path/to/gradle
It requires a fresh work directory, Java/Javac 17+, Gradle 8.10.2, Maven 3.9.x and
an already populated offline Maven cache. --help lists explicit artifact and
tool paths. It does not publish or install to the user's Maven repository; it
uses a temporary cache overlay. Each tool exercises two real modules and a live
agent: opt-out, partial failure, competing owner rejection, same-owner recovery,
exact-byte receipts, up-to-date barriers and offline refusal before output.
Logs and evidence.json stay in the work directory. A missing tool/dependency is
a failed prerequisite, never a silently skipped success. Mac/Linux local results
do not prove Windows compatibility.
Pause and resume automatic reload
reclazz_reload_control accepts action: "pause", "resume" or "status"
and the same port/portFile endpoint options as other tools. For example:
{"name":"reclazz_reload_control","arguments":{"action":"pause","portFile":"/project/.reclazz/agent.port"}}
Wait for status: "acknowledged". The result reports paused,
pendingClasses, pendingActions, and buildHold (none, named, legacy)
as an admission-state snapshot. reloadConfirmed is always false. On
unavailable, the action may have reached the agent; inspect status before
retrying. Unsupported older agents time out instead of appearing successful.
Pause retains observed class/source/resource changes; SCAN does not apply them. Resume schedules current output, but never clears an active or failed BUILD hold. A successful BUILD does not clear manual pause. Already admitted work may finish. The switch is shared by all clients of this agent, survives client disconnect, and is not a per-client ownership lock. Repeated pause/resume does not discard pending changes. See the socket contract for admission and count boundaries. No IDE control or automatic rollback is added.
Canonical source
This full page is generated from docs/mcp-server.md. Edit the repository source and rebuild; do not edit generated HTML.