v1 shipped. The shipped surface is
run_tests(Maven, Node/jest, Go),build,install, the five git read verbs, andget_log. The npm/npx native launcher is available (PRD-5 #44 S3, v0.0.1-alpha.2 published). Forge verbs are roadmap items, not yet available.
A Micronaut MCP server that replaces an agent's Bash tool with safe, structured, token-efficient operations so the Bash permission can be removed entirely. The agent never composes a command; the server detects the environment and maps a typed verb to a controlled invocation.
The following verbs are available over a STDIO JSON-RPC channel:
Runs a project's tests via the detected manager (Maven, Node/jest, Go). Returns a structured
Envelope with one of three shapes: success (counts only), test-failure (normalized
failures[] with file:line, assertion message, and a handle for drill-down), or
operational-error (enumerated code + actionable hint).
| Parameter | Type | Required | Description |
|---|---|---|---|
path |
string | no | Path to the project directory; absent/blank fails closed to INVALID_PATH |
flags |
string[] | no | Manager flags, vetted against the per-operation allowlist |
timeout |
integer | no | Timeout in seconds; clamped to the policy cap; exceeded runs get a hard tree-kill |
targetKind |
CLASS/METHOD |
no | Narrows the run to a class or method; absent means full-suite |
target |
string | no | Test identity matching the kind (FooTest or FooTest#bar) |
The structured target selector is translated into a controlled -Dtest=<value> flag. The agent
cannot pass -Dtest= directly via flags — the allowlist drops it. An invalid targetKind/target
pair returns INVALID_TARGET before any process is launched.
A concurrency guard prevents two overlapping run_tests calls on the same project path from
racing; the second caller receives RESOURCE_BUSY immediately.
Compiles the project via the detected manager and returns a structured Envelope. On a compile
failure, compiler diagnostics are returned as diagnostics[] with file, line, col,
severity, and message. A successful build returns a minimal counts payload with no
diagnostics[] noise. Full compiler output is retained behind the handle for get_log.
Installs Node.js dependencies via npm install and returns a structured Envelope. A successful
install returns a minimal counts payload (manager:"npm", installSummary:{added, removed, changed}). A failed install surfaces INSTALL_FAILED with npm output retained behind the handle.
Five read-only verbs expose structured git inspection, each returning a normalized envelope:
| Verb | Description |
|---|---|
git_status |
Working-tree status — branch, upstream, ahead/behind, staged/unstaged/untracked changes |
git_log |
Capped commit list (sha, short, author, dateIso, subject), newest first |
git_show |
Commit metadata and body; the diff is retrievable via get_log(handle) |
git_diff |
Structured diff summary (gitDiff[]) for working-tree vs HEAD; full patch via get_log(handle) |
git_branch |
Normalized branch list (name, current, upstream, ahead, behind) |
All git verbs are read-only and exempt from the concurrency lock.
Expands a retained run result without re-running. Returns the requested slice from the session run
cache indexed by the handle returned with the previous run_tests result.
| Parameter | Type | Required | Description |
|---|---|---|---|
handle |
string | yes | Opaque handle id from a previous run_tests result |
filter |
string | no | Test identity (suite.name or name); omit to get full raw output |
With filter: returns the full detail/stack trace for the matching failing test.
Without filter: returns the whole retained raw output (stdout + stderr).
Returns null when the handle is unknown or evicted from the run cache.
Agents with an open Bash permission can compose arbitrary commands (rm -rf, curl | sh) with no
structural limit. This server replaces Bash-mediated build/test operations with typed verbs:
- The agent expresses intent (
run_tests), not a command string. - The server validates, guards, and invokes the system manager via a trusted launcher.
- Untrusted repo-derived content in the response (test names, messages, paths) is neutralized before it reaches the agent (P9 outbound neutralization).
- The token-efficient envelope separates signal (actionable
file:line, assertion diffs, failing test identity) from noise (framework frames, progress bars), and defers raw detail toget_logvia aHandle.
The native binary is built and acceptance-tested in CI on four target tuples (GraalVM JDK-25; native-image does not cross-compile, so each tuple is built on its own runner).
| Tuple | Native binary | run_tests: Maven / Go / Node |
|---|---|---|
linux-x64 |
✅ static (--static-nolibc, portable across glibc hosts) |
✅ all three |
linux-arm64 |
✅ static (--static-nolibc) |
✅ all three |
darwin-arm64 |
✅ system-dynamic, ad-hoc codesigned | ✅ all three |
win32-x64 |
✅ system-dynamic (no-bash-mcp.exe) |
Windows caveat (win32-x64). Maven and Node tests do not run from the native Windows
binary. Their launchers (mvn.cmd, npx.cmd) are .cmd shims, and the server spawns launchers
directly with no shell (the trusted-launcher security posture, ADR-0008) — but Windows
CreateProcess only ever executes .exe, never a .cmd, without a shell. go (a real go.exe)
works. Maven/Node run_tests (and build/install) is therefore unsupported on the native
Windows binary: the resolver finds mvn.cmd/npx.cmd on PATH, but the binary cannot spawn a
.cmd without a shell, so the launch fails closed with a structured MANAGER_NOT_SPAWNABLE
operational error — naming the launcher and pointing at the JVM-jar / WSL2 remedy, never an
unstructured exception (#71). On Windows, use the JVM jar (java -jar) for Maven/Node projects, or
run the native binary under WSL2 (a linux-x64 / linux-arm64 environment).
win32-arm64 and darwin-x64 (Intel) do not have a GraalVM JDK-25 toolchain (win32-arm64 has
none; darwin-x64 is deprecated upstream — 25.0.1 was the last release). No platform package is
produced for these tuples. When the launcher runs on one of them it emits a structured JSON error on
stderr (exit code 78 / EX_CONFIG) and exits immediately — it never starts a half-open channel:
{
"error": "no-bash-mcp-launcher",
"reason": "unsupported-platform",
"platform": "<tuple>",
"supported": ["linux-x64", "linux-arm64", "darwin-arm64", "win32-x64"],
"hint": "No native binary is produced for this OS/arch. Run the server from the JVM jar instead (java -jar no-bash-mcp.jar), or, on win32-arm64, the win32-x64 binary under emulation."
}win32-arm64 users can run the win32-x64 build under Windows x64 emulation as a workaround
(US11). A JVM-jar published distribution channel is deferred to the roadmap (it would
reintroduce the JRE dependency native exists to kill — YAGNI until evidence of a real user
on an uncovered platform).
The npm/npx channel keeps a thin Node shim process in front of the native MCP binary for the
whole session (it pipes stdio), adding roughly 30–50 MB RSS plus the Node dependency. The
server's operations run in the native binary; Node is the delivery and session-level bridge.
This is an accepted, eyes-open trade — esbuild does the same, and Node is present anyway because
Claude Code itself ships via npm. See ADR-0010 and
docs/design/build-and-distribution.md.
Pushing a version tag (v*) runs the full 4-tuple matrix as a release gate
(a red acceptance IT on any tuple blocks the release) and publishes both the GitHub Release assets
and the npm packages:
| Asset / package | Tuple |
|---|---|
no-bash-mcp-linux-x64 / @no-bash-mcp/linux-x64 |
linux-x64 (static) |
no-bash-mcp-linux-arm64 / @no-bash-mcp/linux-arm64 |
linux-arm64 (static) |
no-bash-mcp-darwin-arm64 / @no-bash-mcp/darwin-arm64 |
darwin-arm64 (signed) |
no-bash-mcp-win32-x64.exe / @no-bash-mcp/win32-x64 |
win32-x64 |
SHA256SUMS |
integrity manifest |
Registration is a manual step — the Bootstrap skill (#78, not yet landed) that will auto-write
.mcp.json is not yet available. Add the server block by hand.
Add the server under mcpServers in your harness configuration. The server communicates over
STDIO. Use the npx channel (primary, available now — PRD-5 S3, [ADR-0010]):
{
"mcpServers": {
"no-bash-mcp": {
"command": "npx",
"args": ["-y", "no-bash-mcp@0.0.1-alpha.2"]
}
}
}The pin (0.0.1-alpha.2) is exact, never @latest (D42/D37) — a security-critical binary
must not auto-update silently; bump the pin explicitly when upgrading. npm's os/cpu fields
select the correct platform package automatically on install.
Uncovered platforms. On win32-arm64 or darwin-x64 (Intel), no native binary is available
(see Unsupported tuples — fail-clear above). The launcher emits a structured JSON error on stderr
and exits without starting a channel. Those platforms may use the JVM jar as a manual fallback
(build from source with mvn package -DskipTests and run java -jar target/no-bash-mcp-<version>-SNAPSHOT.jar);
a published JVM-jar distribution channel is deferred to the roadmap.
After confirming the MCP verbs work, manually remove the agent's Bash permission from your
harness configuration. The exact mechanism is harness-specific (e.g. a permissions.deny entry
in Claude Code's settings.json). Keeping Bash enabled alongside the MCP defeats the purpose.
Shipped in v1:
run_tests— Maven, Node (jest), Gobuild— Maven (compile diagnostics viadiagnostics[])install— Node/npm dependency installgit_status,git_log,git_show,git_diff,git_branch— read-only git inspectionget_log— drill-down into any retained run result via ahandle- P9 outbound neutralization of untrusted repo-derived strings
- Concurrency guard (
RESOURCE_BUSY) on overlapping runs
Also shipped (PRD-5 S3):
- npm/npx native binary launcher (
no-bash-mcp+@no-bash-mcp/<os>-<arch>platform packages, v0.0.1-alpha.2; ADR-0010)
Not yet available (roadmap, not shipped):
lint,run_taskverbs- Forge inspection (
pr_checks,pr_view,pr_diff) describe_project,dependencies- Bootstrap auto-write of
.mcp.jsonfor the npx channel (#78, halted/needs-triage) - Published JVM-jar distribution channel (for uncovered platforms; deferred — YAGNI)
The corpus documents every design decision; the README only summarizes the shipped surface. Many of these documents carry Mermaid diagrams (color-keyed structural, flow, and state views), so the corpus is as much a visual study book as a written one — start with the worked-example lesson below for the architecture read at a glance.
| Document | What it covers |
|---|---|
docs/lessons/ |
Worked-example lessons that read the ADRs and decision-log back as teachable principles — resilience, security, scalability of an agentic backend — with Mermaid diagrams throughout |
DESIGN.md |
Architecture (hexagonal, package structure, schema, output contract, testing posture, version baseline), with structural and flow diagrams |
CONTEXT.md |
Ubiquitous language — what every term means (Verb, Manager, Reporter, Envelope, Handle, Guardrail, …) |
docs/design/ |
Pillars, security model, forge security model, tool catalog, operational model, schema divergence map, roadmap, gotchas, decision log — several with diagrams |
docs/adr/ |
Architecture Decision Records (0001–0011) |
docs/research/ |
Empirical grounding: architecture survey, technology baseline, testing stack, GraalVM/WSL setup |
prototype/NOTES.md |
Schema/port validation from three real reports (Maven, jest, Go) |
spikes/ |
De-risking spike outcomes (universal schema, MCP STDIO, forge read-only) |
