go install github.com/dotcommander/jinn/cmd/jinn@latest
echo '{"tool":"read_file","args":{"path":"go.mod"}}' | jinn
echo '{"tool":"run_shell","args":{"command":"go test ./..."}}' | jinn --shell-mode=sandboxedjinn is a single-binary tool executor for AI coding agents. It reads one JSON
request on stdin, runs one tool inside the current workspace, and writes one JSON
response on stdout.
Use it when you need a small, deterministic tool layer for an agent loop, subagent, hook, CI job, or harness integration.
- No daemon: spawn
jinnonce per tool call. - Single-binary distribution: no external runtime services; embedded state uses pure-Go SQLite.
- Workspace confinement: paths stay inside the working directory.
- Safer mutation: file writes use atomic replacement and undo snapshots.
- Shell is opt-in: the default
disabledmode omitsrun_shell. Choosesandboxedfor OS confinement orunsafeonly for explicit compatibility.
go install github.com/dotcommander/jinn/cmd/jinn@latest
jinn --versionBuild from source:
git clone https://github.com/dotcommander/jinn.git
cd jinn
go build -o jinn ./cmd/jinn/Read a file:
echo '{"tool":"read_file","args":{"path":"go.mod"}}' | jinnRun a command:
echo '{"tool":"run_shell","args":{"command":"go test ./..."}}' | jinn --shell-mode=sandboxedInspect a command without running it:
echo '{"tool":"run_shell","args":{"command":"rm -rf build","dry_run":true}}' | jinn --shell-mode=unsafeThe response is always a JSON envelope:
{"ok": true, "result": "..."}run_shell also includes risk and exit-code classifications:
{"ok": true, "result": "[dry-run] would execute: rm -rf build", "risk": "dangerous", "classification": "success"}Errors use the same envelope and often include a next-step hint:
{"ok": false, "error": "file not found: missing.go", "suggestion": "verify the path exists with list_dir on the parent, or check for typos"}Print the OpenAI-compatible function schema:
jinn --schemaAsk from inside the protocol:
echo '{"tool":"list_tools","args":{"include_schema":false}}' | jinnStart the browser inspector:
jinn --inspect 127.0.0.1:8787Use MCP discovery mode:
{
"mcpServers": {
"jinn": {
"command": "jinn",
"args": ["--mcp"]
}
}
}jinn --mcp speaks MCP 2026-07-28 through the official Go SDK and exposes one
MCP tool, jinn_route. It recommends matching jinn tools for a task and can
return lean schemas for only those tools. It does not execute filesystem or
shell operations itself. The one-tool surface is intentional: it keeps model
context bounded. Discovery exposes 18 tools by default and 19 with an explicit
sandboxed or unsafe shell mode.
Current MCP requests use server/discover, tools/list, and tools/call with
_meta.io.modelcontextprotocol/protocolVersion set to 2026-07-28 plus
_meta.io.modelcontextprotocol/clientCapabilities. Existing clients that send
the older initialize handshake are routed through a compatibility path.
The deterministic black-box checks are documented in
docs/mcp-smoke-test.md.
Claude Code, Codex, pi, and similar tools already have native read/edit/shell surfaces. Add jinn for gaps that are useful as one-shot subprocesses:
lsp_queryfor definition, references, hover, diagnostics, symbols, and rename previews without running an MCP server.run_shellwithdry_run: truefor permission hooks that need semantic risk classification before a shell command runs.memoryfor scoped SQLite-backed facts, directives, and lessons with optional expiry and garbage collection.apply_patchto validate and atomically apply Codex-style patches outside the Codex harness.list_toolsor--schemawhen a custom loop needs a compact tool surface.
Recipes for Claude Code, Codex CLI, pi, and custom loops: docs/harness-integrations.md.
jinn exposes 18 tools by default, or 19 with an enabled shell mode:
| Tool | Description |
|---|---|
read_file |
Read windowed chunks of a file with line numbers (max 50MB). Supports tail, line_numbers, and a truncate strategy (head/tail/middle/none). Images detected by content; PDFs return a structured error. |
multi_read |
Read up to 20 files in one call with per-file windows, partial success, and structured per-file errors. |
write_file |
Atomic full-file write. Creates parent directories automatically. |
edit_file |
Targeted text replacement. Handles fuzzy whitespace/quotes, CRLF/BOM preservation, dry_run diff preview. Rejects empty old_text and no-op edits. |
multi_edit |
Apply batch edits with validate-first semantics and per-file atomic writes. Detects overlapping regions, rejects empty or no-op entries. |
apply_patch |
Apply a Codex-style patch (*** Begin Patch … *** End Patch) to create, delete, or update files. Validates all operations first; writes are per-file atomic. |
search_files |
Fast grep/regex search with glob filtering, context lines, and a literal flag for fixed-string matching. |
search_replace |
Regex search-and-replace across explicit files or glob patterns. Supports capture groups, dry runs, and per-file atomic writes. |
run_shell |
Controlled bash execution with risk classification. Process-group kill ensures background children are also terminated on timeout. Dangerous commands blocked unless force: true. |
run_plan |
Execute a condition-gated plan tree of tool/shell operations in one deterministic engine walk. Read-only nodes by default; mutating nodes are risk-gated behind plan- and node-level force. |
stat_file |
Get metadata (size, lines, mtime) without reading contents. |
list_dir |
Recursive directory tree exploration (skips hidden files). Directories suffixed with / in output. |
find_files |
Native bounded glob traversal. Basename patterns match anywhere; slash patterns match normalized relative paths; ** spans segments. It does not interpret .gitignore. |
diff_files |
Unified diff between two files with is_identical and first_changed_line metadata. |
detect_project |
Auto-detect language, frameworks, and build/test/lint commands. |
list_tools |
Programmatic tool capability metadata; can include the compact schema on request. |
memory |
Persistent, project-scoped key/value store across sessions. Actions: save, recall, list, forget. |
undo |
Browse, preview, and restore file snapshots captured automatically before every mutation. Existing files over 5 MiB are rejected before mutation so the undo guarantee is preserved. |
lsp_query |
Query a language server for definition, references, hover, symbols, diagnostics, or rename. |
Security is not an opt-in feature; it is the core of the engine.
- Rooted File Access: File operations use a workspace
os.Root; traversal attempts and special-file reads are blocked. Recursive tools never follow directory symlinks. - Sensitive Blocklist: Direct access to
.git,.ssh,.aws,.env, and.gnupgis always denied. - Mutation Preconditions: Existing targets require
if_checksum; creation requiresif_absent:true. Cross-process locks cover validation, durable snapshot, and atomic replacement. - Shell Modes: Shell is disabled by default. Sandboxed mode denies network and user configuration; unsafe mode is explicitly unconfined.
- Risk Classifier:
forceoverrides only the advisory dangerous-command block; it never enables shell execution or removes confinement. - Output Caps: Shell capture uses 1 MiB memory and at most 16 MiB spill before the process group is killed with
resource_limit.
import subprocess, json
def call_jinn(tool: str, args: dict):
req = json.dumps({"tool": tool, "args": args})
# Run as a subprocess — no daemon or server needed
proc = subprocess.run(["jinn"], input=req, capture_output=True, text=True)
return json.loads(proc.stdout)
# Automate a refactor
project = call_jinn("detect_project", {})
if "Go" in project["languages"]:
call_jinn("run_shell", {"command": "go mod tidy"})For TypeScript, Go, PHP, and shell-script integrations, see docs/getting-started.md.
jinn prioritizes single-binary distribution and maximum reliability. Run
just test before submitting PRs; it is the race-enabled gate for both the
root module and the nested example module.
MIT