Skip to content

Latest commit

 

History

History
1061 lines (795 loc) · 52.7 KB

File metadata and controls

1061 lines (795 loc) · 52.7 KB

Tool Reference

jinn exposes 21 tools by default, or 22 when started with --shell-mode=sandboxed or --shell-mode=unsafe. The protocol accepts exactly one JSON object of at most 16 MiB and rejects duplicate keys, trailing values, unknown fields, invalid types, and unknown tool arguments.

echo '{"tool":"read_file","args":{"path":"main.go"}}' | jinn

Every text tool returns {"ok": true, "result": "..."} on success or {"ok": false, "error": "..."} on failure. Tools can also attach structured content and meta fields for images, checksums, truncation, diffs, and other machine-readable details.

Public web tools

web_fetch

echo '{"tool":"web_fetch","args":{"url":"https://example.com","reader":"defuddle"}}' | jinn
Argument Type Default Meaning
url string required Absolute HTTP(S) URL.
raw bool false Return direct response content.
reader string configured jina, defuddle, or auto.
render string never never, auto, or always.
render_wait string load load or networkidle when rendering.
format string markdown markdown, headings, or links projection. Raw content supports only markdown.
start_line integer 0 Zero-based line offset within the selected projection.
max_bytes integer 0 Maximum projected UTF-8 bytes; zero is unlimited.
max_lines integer 0 Maximum projected lines; zero is unlimited.

The text result is projected document content. Metadata uses snake case for document fields plus format, truncated, truncated_by, total_bytes, output_bytes, total_lines, output_lines, max_bytes, max_lines, start_line, and next_start_line. Private/local addresses and redirect hops are denied unless JINN_WEB_ALLOW_PRIVATE_NETWORKS=true is intentionally set at process start.

web_search

echo '{"tool":"web_search","args":{"query":"Go release notes","max_results":5}}' | jinn
Argument Type Default Meaning
query string required Search terms.
max_results integer 10 1 through 50 normalized results.
category string omitted Provider category.
include_domains string array omitted Hostname filters.
start_published_date string omitted RFC3339 or YYYY-MM-DD lower bound.
include_highlights bool true Include provider highlights where supported.
highlight_sentences integer 3 Requested highlight sentence count, 1 through 10.

Search uses Brave by default (BRAVE_API_KEY) or Exa when JINN_WEB_SEARCH_PROVIDER=exa (EXA_API_KEY). max_results sizes one provider request; it is not multi-page provider pagination. Its text is compact JSON with query and results; metadata contains provider and count.

Response Envelope

The full response type includes optional fields that carry structured metadata:

Field Type Description
ok bool true on success, false on error
result string Text tool output (present when ok: true for text responses)
content array Structured content blocks, currently used for detected images from read_file
meta object Structured metadata such as truncation info, checksums, diffs, stdout/stderr, and compression details
error string Error message (present when ok: false)
error_code string Stable error category on structured errors
suggestion string One-sentence next-step hint on structured errors
classification string Exit-code class set by run_shell: success, expected_nonzero, error, timeout, signal
risk string Pre-execution risk set by run_shell: safe, caution, dangerous
request_id string Echoes the caller-supplied top-level request_id

suggestion is present on errors from any tool when jinn can offer a specific recovery action. Always read it before retrying.


File Operations

These tools read, write, and edit files. All file paths are confined to the working directory. See Security for rooted access, mutation preconditions, and locking.

read_file

Read a file with line-numbered output.

echo '{"tool":"read_file","args":{"path":"main.go"}}' | jinn

Parameters:

Name Type Required Default Description
path string Yes -- File path relative to working directory
start_line int No 1 First line to return
end_line int No start_line + 1999 Last line to return
tail int No 0 (disabled) Return last N lines. Overrides start_line/end_line
line_numbers bool No true Prefix each output line with a right-justified line number. Set false for raw content without numbering.
truncate string No "head" Strategy when windowed output exceeds the line limit: head (keep first N lines, paginate with start_line), tail (keep last N lines, useful for logs), middle (keep both ends, elide center), none (no line-level truncation, byte cap still applies), smart (brace-depth heuristic — cuts at block boundaries for C-syntax files .go/.rs/.ts/.js/.java/.c/.cpp/.h/.hpp/.tsx/.jsx, falls back to head for others).
include_checksum bool No false Include a SHA-256 checksum in meta.sha256
if_checksum string No -- Skip the body when the current checksum matches this value (returns {"unchanged":true,...}). On write_file/edit_file the same arg is a stale-write guard instead.

Notes:

  • Files larger than 50 MB are rejected.
  • Empty text files are valid and return an empty result.
  • PDF files return ok: false with suggestion: "convert the PDF to text first (pdftotext, pdftk, or a cloud OCR service) and read the text file". Content is never returned.
  • Image files are detected by content rather than extension — a .png renamed without an extension is still identified as an image. Detected images return a base64-encoded content block with the correct MIME type (image/png, image/jpeg, etc.). SVG files return image/svg+xml. Pass the result directly to a vision model.
  • Binary files (null byte in first 512 bytes) return [binary file: N bytes — use stat_file for metadata or skip content reads] as a success result (not an error).
  • Sequentially truncated output appends: [Showing lines X-Y of Z. Use start_line=N to continue. Remainder saved to <path>.]. It also returns an exact next_call. Tail and middle truncation omit unsafe continuations and recommend a narrower window.
  • include_checksum:true returns the SHA-256 required by secure mutation calls. See Security: Mutation Preconditions.

Read lines 10 through 20:

echo '{"tool":"read_file","args":{"path":"main.go","start_line":10,"end_line":20}}' | jinn

Read the last 5 lines:

echo '{"tool":"read_file","args":{"path":"main.go","tail":5}}' | jinn

Read with a checksum, then skip the body on the next call if the file has not changed:

echo '{"tool":"read_file","args":{"path":"go.mod","include_checksum":true}}' | jinn
echo '{"tool":"read_file","args":{"path":"go.mod","if_checksum":"<sha256-from-meta>"}}' | jinn

multi_read

Read multiple files in a single call.

echo '{"tool":"multi_read","args":{"files":[{"path":"main.go"},{"path":"go.mod"}]}}' | jinn

Parameters:

Name Type Required Default Description
files array Yes -- List of file read requests, 1–20 entries

Each file entry:

Name Type Required Default Description
path string Yes -- File path relative to working directory
start_line int No 1 First line to return
end_line int No start_line + 1999 Last line to return
tail int No 0 (disabled) Return last N lines. Overrides start_line/end_line
line_numbers bool No true Prefix each output line with a right-justified line number
truncate string No "head" Truncation strategy: head, tail, middle, none, or smart (brace-depth heuristic for C-syntax files)

Notes:

  • Returns JSON with files (path→content), errors (path→error detail), truncation (path→metadata), and next_calls for any truncated file or unprocessed tail of the input batch.
  • Partial success: if some files fail, they appear in errors while successful reads still return in files.
  • Only returns ok: false if ALL files fail.
  • Binary/image files are reported in errors with error_code: "binary_file" — use read_file for single-image viewing.
  • Empty text files are returned in files with an empty string value.
  • Per-file windowing: each file entry supports independent start_line/end_line/tail/truncate.
  • Duplicate paths: last entry wins.

Read with per-file windowing:

echo '{"tool":"multi_read","args":{"files":[{"path":"main.go","start_line":1,"end_line":10},{"path":"go.mod","tail":5}]}}' | jinn

Mixed success (some files missing):

echo '{"tool":"multi_read","args":{"files":[{"path":"main.go"},{"path":"nonexistent.go"}]}}' | jinn

write_file

Write content to a file atomically.

echo '{"tool":"write_file","args":{"path":"hello.txt","content":"Hello, world.\n","if_absent":true}}' | jinn

Parameters:

Name Type Required Default Description
path string Yes -- File path relative to working directory
content string Yes -- Content to write
dry_run bool No false Preview the write without modifying the file
if_checksum string Conditional -- Required to replace an existing file. SHA-256 from a previous read (meta.sha256); mismatch rejects the write with error_code: "stale_file"
if_absent bool Conditional -- Must be true to create a new file

Notes:

  • Writes are atomic: jinn writes to a hidden temp file, syncs to disk, then renames it into place. See Security: Atomic Writes.
  • Parent directories are created automatically.
  • If the file already exists, jinn preserves its permissions.
  • Existing targets require the checksum from a prior read. See Security: Mutation Preconditions.
  • Pass if_checksum (from read_file with include_checksum: true) to reject the write when the file changed since your read — the in-process check above cannot cross jinn invocations; if_checksum can.
  • dry_run on an existing file returns a unified diff. On a new file, it returns [dry-run] would create path (N bytes).

Preview a write without applying it:

echo '{"tool":"write_file","args":{"path":"hello.txt","content":"new content\n","dry_run":true}}' | jinn

edit_file

Replace an exact text match in a file.

echo '{"tool":"edit_file","args":{"path":"main.go","old_text":"fmt.Println","new_text":"log.Println","if_checksum":"<sha256-from-read_file>"}}' | jinn

Parameters:

Name Type Required Default Description
path string Yes -- File path relative to working directory
old_text string Yes -- Text to find (must be unique in the file)
new_text string Yes -- Replacement text
dry_run bool No false Preview the edit as a unified diff
fuzzy_indent bool No false Detect indentation at match site, re-indent new_text to match
show_context int No 0 Return N surrounding lines around the edit with * markers on changed lines
if_checksum string No -- SHA-256 from a previous read (meta.sha256). Mismatch rejects the edit with error_code: "stale_file"

Notes:

  • old_text cannot be empty. An empty string produces an error with a suggestion to include the first line of the file when you need to prepend content.
  • old_text must match exactly once. Zero matches or multiple matches both produce an error. On multi-match, the error includes line numbers for up to 10 locations.
  • If exact match fails, jinn tries fuzzy matching (normalizes whitespace, smart quotes, Unicode dashes). Fuzzy match is used only when it produces exactly one candidate.
  • If old_text and new_text are equivalent (including after fuzzy normalization), jinn returns an error rather than silently writing an unchanged file.
  • jinn preserves BOM markers and CRLF line endings through the edit.
  • When both exact and fuzzy fail, the error message includes the nearest line by character overlap to help you locate the right text.
  • dry_run returns a unified diff with 3 lines of context.

Edit with context lines:

echo '{"tool":"edit_file","args":{"path":"main.go","old_text":"old","new_text":"new","show_context":2,"if_checksum":"<sha256-from-read_file>"}}' | jinn

Preview an edit:

echo '{"tool":"edit_file","args":{"path":"config.yaml","old_text":"port: 8080","new_text":"port: 9090","dry_run":true}}' | jinn

multi_edit

Apply multiple edits across files. jinn validates all edits before writing, then writes each changed file atomically.

echo '{"tool":"multi_edit","args":{"edits":[{"path":"a.go","old_text":"foo","new_text":"bar","if_checksum":"<a-sha256>"},{"path":"b.go","old_text":"baz","new_text":"qux","if_checksum":"<b-sha256>"}]}}' | jinn

Parameters:

Name Type Required Default Description
edits array Yes -- Array of edit objects (see below)

Each edit object:

Name Type Required Default Description
path string Yes -- File path relative to working directory
old_text string Yes -- Text to find (must be unique in the file)
new_text string Yes -- Replacement text
if_checksum string No -- SHA-256 for this target from read_file; a mismatch rejects the whole batch as stale
fuzzy_indent bool No false Detect indentation, re-indent new_text
show_context int No 0 Return N surrounding lines with markers

Notes:

  • Validate first. jinn validates every edit (rooted path, checksum, and match uniqueness) before applying any. If any edit fails validation, zero edits are applied.
  • Cross-call guard. Put if_checksum on each edit derived from an earlier read. jinn also rechecks preflight bytes immediately before every write.
  • Per-file atomic writes. Each changed file is written via temp+rename. If a write fails after validation, earlier successful file writes are not rolled back; the error enumerates them with undo ids (partial apply — N of M files already written: ... (undo id=...)) so you can restore or retry the remainder.
  • old_text cannot be empty in any edit entry. An empty value returns an error immediately, before any edits are applied.
  • Overlap detection. Edits targeting overlapping byte ranges in the same file are rejected in the validation phase. The error names which two edit indices conflict. Split them into separate multi_edit calls or combine them into a single edit.
  • If any edit's old_text and new_text are equivalent (including after fuzzy normalization), jinn returns an error and applies nothing.
  • Each edit uses the same matching and normalization rules as edit_file.
  • Multiple edits to the same file are applied sequentially in array order. Later edits in the array see the file as modified by earlier ones (chained edits).

apply_patch

Apply a Codex-style patch payload.

echo '{"tool":"apply_patch","args":{"patch":"*** Begin Patch\n*** Update File: main.go\n@@\n-old\n+new\n*** End Patch","if_checksums":{"main.go":"<sha256-from-read_file>"}}}' | jinn

Parameters:

Name Type Required Default Description
patch string Yes -- Patch text starting with *** Begin Patch and ending with *** End Patch
if_checksums object No -- Map of target path to SHA-256 from read_file; any mismatch rejects the patch before writes
dry_run bool No false Validate and preview changes without writing

Supported operations:

Operation Behavior
*** Add File: path Creates a new file. Fails if the target already exists.
*** Delete File: path Deletes an existing file. Fails if the target does not exist.
*** Update File: path Applies hunk-based edits using context, removed, and added lines.

Notes:

  • All operations are validated before writing starts.
  • Supply if_checksums for update/delete targets derived from earlier reads. jinn also rejects targets whose bytes or existence change between validation and write.
  • Writes are per-file atomic. If a later write fails after validation, earlier successful writes are not rolled back; the error enumerates them with undo ids so you can undo each or retry the remainder.
  • Update hunks support progressive fuzzy matching when exact context fails.

undo

Browse and restore file snapshots. jinn captures a snapshot automatically before every write_file, edit_file, multi_edit, and apply_patch mutation. Mutations of existing files whose pre-mutation content exceeds 5 MiB are rejected before writing so no successful mutation silently lacks an undo snapshot.

echo '{"tool":"undo","args":{"action":"list"}}' | jinn

Parameters:

Name Type Required Default Description
action string Yes -- list, preview, restore, or clear
id string For preview, restore -- Snapshot ID. A unique prefix of the ID also works.
if_checksum string No -- For restore, SHA-256 of the current target; mismatch rejects the restore as stale
limit int No all Maximum number of snapshots to return for list

Actions:

Action Required args Effect
list -- List snapshots newest-first with ID, file path, timestamp, and operation
preview id Unified diff between the snapshot and the current file contents
restore id Revert the file to the snapshot contents via an atomic write
clear -- Delete all snapshot history

Notes:

  • Snapshots are recorded automatically -- there is no "save snapshot" action.
  • History is bounded; the oldest snapshots are evicted once the limit is reached.
  • Existing files larger than 5 MiB are rejected for mutating operations with file_too_large; the original file remains unchanged.
  • preview and restore accept any unique prefix of a snapshot ID, so the short form shown by list works directly.

List snapshots, then restore one:

echo '{"tool":"undo","args":{"action":"list"}}' | jinn
echo '{"tool":"undo","args":{"action":"restore","id":"a1b2c3","if_checksum":"<current-target-sha256>"}}' | jinn

diff_files

Compare two files and return a unified diff.

echo '{"tool":"diff_files","args":{"path_a":"old.go","path_b":"new.go"}}' | jinn

Parameters:

Name Type Required Default Description
path_a string Yes -- First file to compare
path_b string Yes -- Second file to compare
context_lines int No 3 Lines of context around each change

Notes:

  • Uses the same diff engine as the edit_file dry-run preview.
  • meta carries is_identical (bool) and first_changed_line (int).
  • Both paths are confined to the working directory.

Search and Discovery

search_files

Search file contents with regex.

echo '{"tool":"search_files","args":{"pattern":"func main"}}' | jinn

Parameters:

Name Type Required Default Description
pattern string Yes -- Regex pattern (or fixed string when literal: true)
path string No "." Directory to search in
format string No "text" Output format: text, json, or filenames
include string No -- Glob filter on filenames (e.g., "*.go")
literal bool No false Treat pattern as a fixed string rather than a regex. Passes -F to grep / --fixed-strings to rg.
max_matches int No 500 Maximum number of matches to return. When exceeded, response includes truncated: true and total_count.
offset int No 0 Zero-based result offset. Requires format: "json".
context_lines int No 0 Surrounding lines to include per match
case_insensitive bool No false Case-insensitive matching

Notes:

  • jinn uses rg (ripgrep) if available, otherwise falls back to grep -r -n.
  • These directories are always excluded: .git, node_modules, vendor, __pycache__, .cache, dist, build.
  • Without literal: true, the pattern is validated as a regex before any search runs; invalid patterns return an error immediately.
  • Output limits: 200 characters per line truncation per match. Default cap: 500 matches.

Structured results for programmatic use:

echo '{"tool":"search_files","args":{"pattern":"func \\w+Handler","format":"json","include":"*.go"}}' | jinn

format: "json" returns an object with results, offset, count and truncation fields, and an exact next_call when another page exists. Each match contains file, line, column, text, and optional context_before/context_after fields.

List files with match counts:

echo '{"tool":"search_files","args":{"pattern":"TODO","format":"filenames"}}' | jinn

find_files

Find files by glob pattern.

echo '{"tool":"find_files","args":{"pattern":"*.go","path":"internal"}}' | jinn

Parameters:

Name Type Required Default Description
pattern string Yes -- Glob pattern such as *.go, **/*.json, or src/**/*_test.go
path string No "." Directory to search in
limit int No 1000 Maximum number of results before truncation
offset int No 0 Zero-based result offset for continuation

Notes:

  • Uses a native bounded walker; .gitignore is not interpreted.
  • Patterns without / match basenames. Slash patterns match normalized relative paths; * stays within one segment and ** spans segments.
  • Returns one JSON document with files, offset, truncated, total_count, total_count_exact, limit_used, backend, and optional hint and exact next_call fields.
  • Excludes hidden paths, .git, .ssh, .aws, .gnupg, .env variants, node_modules, vendor, __pycache__, .cache, dist, and build at every depth.

search_replace

Replace regex matches across explicit files or glob patterns.

echo '{"tool":"search_replace","args":{"pattern":"oldName","replacement":"newName","files":"*.go","dry_run":true}}' | jinn

Parameters:

Name Type Required Default Description
pattern string Yes -- Regex pattern to search for
replacement string Yes -- Replacement text. Supports $1, $2, etc. for capture groups.
files string or array Yes -- Target path, glob pattern, directory, or array of paths/globs. Max 50 resolved files.
include string No -- Optional glob filter applied after target expansion, e.g. "*.go"
if_checksums object No -- Map of target path to SHA-256 from read_file; mismatched files are rejected as stale
case_insensitive bool No false Match case-insensitively
multiline bool No true Enable ^/$ line-boundary matching
dry_run bool No false Preview diffs and match counts without writing

Notes:

  • Each file is validated before any writes are applied.
  • jinn rechecks every pending file immediately before writing; use if_checksums to extend that guard across separate jinn calls.
  • Writes are per-file atomic. If a later write fails after validation, earlier successful writes are not rolled back; the error enumerates them with undo ids.
  • Binary files are skipped with structured per-file errors.
  • Empty replacement is valid and deletes the matched text.

stat_file

Get file metadata without reading content.

echo '{"tool":"stat_file","args":{"path":"main.go"}}' | jinn

Parameters:

Name Type Required Default Description
path string Yes -- File path relative to working directory

Returns:

Field Description
path Resolved file path
type file, directory, or special
size Size in bytes
lines Line count (regular files under 50 MB only)
modified Modification time as RFC 3339

list_dir

List files in a directory tree.

echo '{"tool":"list_dir","args":{"path":".","depth":2}}' | jinn

Parameters:

Name Type Required Default Description
path string No "." Directory to list
depth int No 3 Maximum recursion depth (clamped to 1--10)
max_entries int No 500 Maximum number of entries to return (cap: 10000). When exceeded, response includes truncated: true and total_count.
offset int No 0 Zero-based result offset for continuation
changed_since number No 0 Unix epoch seconds, including fractions. Only list entries modified strictly after it.
changed_after string No -- RFC3339Nano timestamp. Only list entries modified strictly after it.

Notes:

  • Hidden files and directories (names starting with .) are excluded.
  • Output is sorted alphabetically.
  • Directory entries are suffixed with / to distinguish them from files.
  • Returns one JSON object with entries, offset, truncated, total_count, and total_count_exact; an early traversal stop makes the total inexact. A truncated result includes an exact next_call.

Shell Execution

run_shell

Run a bash command with a timeout. This tool exists only when an explicit shell mode is selected on the jinn command line.

echo '{"tool":"run_shell","args":{"command":"go test ./..."}}' | jinn

Parameters:

Name Type Required Default Description
command string Yes -- Bash command to execute
timeout int No 30 Timeout in seconds (max 300)
dry_run bool No false Print the command without executing it
force bool No false Execute even when risk classification is dangerous. See Security: Risk Classifier.

Notes:

  • --shell-mode=sandboxed uses the host OS sandbox with network disabled, synthetic HOME/TMPDIR, and fails at startup if confinement is unavailable. --shell-mode=unsafe is unconfined and restores only the allowlisted host environment. Both use a fixed workspace cwd, a sanitized snapshot of absolute existing directories from the launch-time host PATH, and process-group cleanup.
  • Exit code 124 means the command was killed by the timeout.
  • Output format: [exit: N]\n<output>\n[classification: <class> — <reason>].
  • Every response includes risk and classification fields in the envelope.
  • Dangerous commands (e.g., rm -rf, dd, sudo) are blocked and return ok: false with a suggestion unless force: true is passed.
  • The shell environment is scrubbed to a fixed allowlist. See Security: Shell Environment.
  • Output over 1 MiB spills to a temp file and is capped at 16 MiB total; excess output kills the process group with resource_limit. See Security: Output Bounds.

Run with a longer timeout:

echo '{"tool":"run_shell","args":{"command":"go build ./...","timeout":120}}' | jinn

Preview without executing:

echo '{"tool":"run_shell","args":{"command":"rm -rf /tmp/test","dry_run":true}}' | jinn

run_plan

Execute a condition-gated plan tree of tool and shell operations in one deterministic engine walk — no model call between nodes. The walk starts at root and follows first-match-wins conditional edges until it reaches a leaf, a dead end, the depth limit, or a blocked mutation.

echo '{"tool":"run_plan","args":{"plan":{"root":"check","nodes":[{"id":"check","commands":[{"shell":"test -f go.mod"}],"edges":[{"when":{"kind":"exitCode","op":"eq","value":0},"to":"build"}]},{"id":"build","commands":[{"shell":"go build ./..."}],"mutates":true}]}}}' | jinn

Parameters:

Name Type Required Default Description
plan object Yes -- The plan tree to execute (fields below)

The plan object:

Name Type Required Default Description
root string Canonical form -- ID of the starting node
nodes array Canonical form -- Non-empty plan nodes, each with non-empty commands and optional edges
steps array Compact form -- One to 16 tool operations, expanded into a single plan node; shell steps are not accepted
parallel bool No false Run compact-form steps concurrently
mutates bool No false Enable normal Phase 2 mutation gating for compact-form steps
cwd string No working dir Existing directory inside the engine workdir; all shell, tool, and cwd-relative condition paths use it
max_depth int No 8 Maximum execution depth before the walk stops, including nested run_plan calls
force bool No false Plan-level gate; with a node's own force, permits dangerous mutations

Notes:

  • Use either root plus nodes, or steps; combining both forms is rejected. Compact steps preserve the same read-only default, operation budget, and mutation classifier as canonical plans. They cannot set node-level force, so dangerous mutations remain blocked.
  • Each command sets exactly one of a non-empty shell or a known tool. Unknown tools, empty nodes, and malformed conditions are rejected before the walk starts.
  • One shared budget permits at most 256 operations across the outer plan and all nested run_plan calls. Exhausting it stops the walk with stopped_reason: "resource_limit".
  • Read-only by default: a node without mutates: true allows only safe shell commands and read-only tools. This includes memory actions recall/list and undo actions list/preview; their mutating actions remain blocked. A mutates: true node runs caution operations automatically; dangerous ones require both plan.force and the node's force, including nested tool: "run_shell" and tool: "run_plan" commands. Nested plans inherit the parent's remaining depth and dangerous-mutation authority. A nested force argument cannot bypass those gates.
  • A node cannot combine parallel: true with mutates: true; split mutating operations into serial nodes.
  • Edges are evaluated against the last op's result; a failed or blocked tool reports a nonzero exit_code. Condition kinds are exitCode, fileExists, jsonPath, numeric, match, and always; negate applies uniformly, and JSON equality preserves value types.
  • A numeric.extract regex must contain a capture group for the numeric value. An empty match.regex is valid and matches every result.
  • A condition-evaluation error (for example, a path outside the plan sandbox) stops the walk with stopped_reason: "error"; it never falls through to a later edge.
  • The result carries the run in meta.plan_run (transcript, path_taken, stopped_reason, and edge counts).
  • Full PlanNode / PlanEdge / condition reference lives in the run_plan section of AGENTS.md.

Meta Tools

list_tools

Get tool capability metadata. By default this avoids returning the full schema again.

echo '{"tool":"list_tools","args":{}}' | jinn

Parameters:

Name Type Required Default Description
include_schema boolean No false Also include the compact OpenAI tool schema for runtime discovery

Use include_schema: true only when the calling agent needs to discover schemas at runtime.

MCP jinn_route

The default jinn --mcp profile intentionally exposes only one MCP tool, jinn_route, to avoid prompt bloat from listing every jinn tool directly. It uses the official Go SDK for MCP 2026-07-28. Requests are stateless and carry the protocol version and client capabilities in _meta. jinn_route recommends existing jinn tools for a coding-agent task and never executes them.

{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}},"name":"jinn_route","arguments":{"need":"regex replace across many files","include_call":true}}}

An MCP client sends those lines over the long-lived jinn --mcp stdin pipe and reads one response per request. Do not close stdin until the responses arrive.

<!-- equivalent shell payload, without closing the client pipe -->
printf '%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}' \
  '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}},"name":"jinn_route","arguments":{"need":"regex replace across many files","include_call":true}}}'

The shell payload above is illustrative only; piping it directly to `jinn --mcp`
closes stdin too early for an in-flight response. Use the checked
long-lived driver in [mcp-smoke-test.md](mcp-smoke-test.md).

Parameters:

Name Type Required Default Description
need string Yes -- Natural-language task or capability request
max_tools integer No adaptive Explicit maximum recommendations, capped at 8; omit for adaptive cardinality
include_schema boolean No false Include lean schemas only for returned tools
include_call boolean No false Include the smallest executable jinn_call argument template and replacement paths relative to its arguments object
include_mutating boolean No true Allow recommendations for mutating tools

With max_tools omitted, a high-confidence route returns one match and a close score returns the top two. Set it explicitly to override that cardinality.

mutating is a maximum-capability classification: it is true when at least one valid invocation of the tool may mutate files or persistent state. Therefore, include_mutating:false excludes run_plan even when the particular plan a caller intends to submit would be read-only.

Returns: the tool result contains structured structuredContent route JSON and a mirrored text content block for clients that only consume text. The request above returns:

{
  "route_id": "<32 lowercase hex characters>",
  "query": "regex replace across many files",
  "confidence": "high",
  "score_margin": 23,
  "adaptive": true,
  "matches": [
    {
      "name": "search_replace",
      "description": "Regex-based search and replace across files. Supports capture groups ($1, $2) in replacement, multi-file scope (glob patterns), replace-all (every occurrence), validate-first apply, and per-file atomic writes. Use when edit_file/multi_edit cannot: regex patterns, multi-file bulk changes, or replacing all occurrences. Binary files are skipped automatically.",
      "reason": "Matched name tokens, description, parameters, features, task intent.",
      "mutating": true,
      "risk": "mutating",
      "features": ["regex", "capture_groups", "multi_file", "glob_patterns", "replace_all", "dry_run", "case_insensitive", "multiline"],
      "call": {
        "tool": "search_replace",
        "arguments": {"files":"<required>","pattern":"<required>","replacement":"<required>"},
        "replace": ["files","pattern","replacement"]
      }
    }
  ],
  "notes": [
    "Recommendation only: jinn_route does not execute tools.",
    "Mutating recommendations can change files or persistent state; use dry_run where supported."
  ]
}

Notes:

  • Routing is deterministic and fully local -- no LLM, no network, no persistent state. The same inputs return the same recommendations, confidence, and score margin. The MCP broker adds a fresh opaque route_id for call linkage.

  • Tools are scored by lexical overlap between the need and each tool's name, description, parameter names, enum values, and feature tags, plus curated task-intent rules -- "revert my last change" routes to undo without naming the tool.

  • Matches below a relevance floor are dropped rather than padded. A vague need returns empty matches and a corrective note:

    {"query": "do the thing", "matches": [], "notes": ["No confident route found. Try a more concrete task, object, or operation name."]}
  • Phrase need as operation + object: "replace one exact string in a single file" routes to edit_file; "get the size and encoding of a file without reading it" routes to stat_file.

  • include_mutating: false restricts recommendations to read-only tools, useful while an agent is in a plan or review phase.

  • include_schema: true attaches a lean schema (parameter descriptions stripped) to each returned match only.

MCP read-only profile and jinn_call

Start the opt-in read-only profile with:

jinn --mcp-profile=read-only --mcp

The profile keeps jinn_route and adds jinn_call, a generic executor whose tool enum is generated from the canonical read-only tool registry. It forces shell execution off and rejects mutation-capable tools, memory, and undo before dispatch. The default jinn --mcp profile remains route-only, so adding this profile does not change existing MCP clients. Its jinn_route schema defaults include_mutating to false and runtime always excludes mutating recommendations.

jinn_call parameters:

Name Type Required Default Description
tool string Yes -- One of the advertised read-only jinn tools
arguments object No {} Arguments for the selected tool
compress boolean No true Apply deterministic context compression to text output
route_id string No -- Opaque 32-character lowercase hex identifier returned by jinn_route; echo it unchanged to link route and call effectiveness records

Example call:

{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}},"name":"jinn_call","arguments":{"tool":"read_file","arguments":{"path":"README.md"},"compress":false,"route_id":"<32 lowercase hex characters>"}}}

The result has structured structuredContent with the selected tool, optional route_id, text result, optional jinn content blocks, and merged meta. Errors use isError: true plus structured error, error_code, suggestion, retryable, and an exact next_call when a safe deterministic recovery exists. A mutation attempt never reaches the engine dispatcher.

Set JINN_MCP_LOG_LEVEL=error|info|debug to enable the private, capped JSONL effectiveness ledger at $JINN_CONFIG_DIR/jinn/logs/mcp.jsonl or the platform user-config equivalent. Version 2 records may include route/call linkage, recommendations, confidence, score margin, result size, truncation, and retry classification. They never include prompts, tool arguments, paths, or content. Logging is best effort and does not affect request results. The read-only profile requires current stateless request metadata. Its legacy initialize-based traffic is handled by the current SDK and rejected before route or tool dispatch; only the default profile retains the route-only compatibility path.

MCP Streamable HTTP

Start the opt-in HTTP transport with the route-only profile:

jinn --mcp-http

It listens on 127.0.0.1:8788 and serves only POST /mcp. The read-only profile uses the same profile and allowlist as stdio:

jinn --mcp-profile=read-only --mcp-http 127.0.0.1:8788

Each request is stateless and must include these headers. The body is one MCP 2026-07-28 JSON-RPC request or notification:

Header Required Value
Accept Yes application/json, text/event-stream
Content-Type Yes application/json
MCP-Protocol-Version Yes 2026-07-28, matching the body _meta
Mcp-Method Yes The JSON-RPC method, such as server/discover
Mcp-Name tools/call only The body params.name value
Authorization When a token is configured Bearer $TOKEN

Example discovery request:

curl -sS http://127.0.0.1:8788/mcp \
  -H 'Accept: application/json, text/event-stream' \
  -H 'Content-Type: application/json' \
  -H 'MCP-Protocol-Version: 2026-07-28' \
  -H 'Mcp-Method: server/discover' \
  --data '{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}'

Loopback binds do not need a token unless JINN_MCP_HTTP_TOKEN is set. Any non-loopback address requires both JINN_MCP_HTTP_TOKEN and JINN_MCP_HTTP_ORIGINS at startup. The origin variable is a comma-separated list of exact HTTP(S) origins. When origins are configured, every supplied Origin header, including localhost, must exactly match that list; a request without Origin remains available to non-browser MCP clients. Missing or invalid bearer auth returns HTTP 401 with WWW-Authenticate: Bearer; an unlisted browser origin returns HTTP 403. The SDK's insecure allow-any-origin option is never enabled, and tokens are not accepted through CLI arguments.

Use a non-loopback bind only on a trusted network or behind a TLS-terminating proxy or tunnel: bearer tokens authenticate requests but do not encrypt them.

HTTP has an explicit 64 KiB header limit and 8 MiB body limit, a five-second header timeout, no read/write stream deadline, and a two-minute idle timeout. SIGINT and SIGTERM perform bounded graceful shutdown. It intentionally does not implement the stdio legacy initialize compatibility path. See mcp-smoke-test.md for a real endpoint check.

MCP network profile and explorer

jinn --mcp-profile=network --mcp keeps the compact jinn_route and jinn_call surface. jinn_call is read-only, non-destructive, and open-world: its web_fetch and web_search requests leave the machine and may consume provider quota. Web output defaults to uncompressed; local read-only output keeps compression by default.

jinn mcp list ENDPOINT [--timeout 30s]
jinn mcp inspect ENDPOINT TOOL [connection flags]
jinn mcp call ENDPOINT TOOL [--args JSON] [-a NAME VALUE]... [connection flags]
jinn mcp list --command PATH [--arg ARG]...

The explorer accepts only HTTP(S) endpoints or explicit subprocess argv, follows all tools/list pages, disables HTTP retries, and reaps subprocesses. HTTP bearer authentication comes only from JINN_MCP_HTTP_TOKEN; it is never printed or accepted through a command-line option. --args and repeated assignments merge in command-line order; valid JSON assignment values remain typed. Its successful call JSON always contains resultType, content, structuredContent, and isError; a tool-level error still exits zero.

detect_project

Detect language, framework, and build commands from project config files.

echo '{"tool":"detect_project","args":{"path":"."}}' | jinn

Parameters:

Name Type Required Default Description
path string No "." Project root to probe

Returns:

Field Description
languages Detected languages (e.g., ["Go", "TypeScript"])
build_tool Build command (e.g., "go build ./...")
test_command Test command (e.g., "go test ./...")
linter Lint command (e.g., "golangci-lint run")
config_files Config files found (e.g., ["go.mod", ".golangci.yml"])
frameworks Detected frameworks (e.g., ["Next.js"])

Notes:

  • Probes for: go.mod, package.json, bun.lockb, Cargo.toml, pyproject.toml, setup.py, requirements.txt, composer.json, Makefile, Taskfile.yml.
  • Secondary detection: tsconfig.json upgrades JS to TypeScript. package.json scripts override build/test/lint commands. next.config.js or next.config.mjs triggers Next.js detection.

memory

Persist key/value pairs across jinn invocations, scoped per project by default.

echo '{"tool":"memory","args":{"action":"save","key":"project.notes","value":"auth service uses JWT RS256"}}' | jinn

Parameters:

Name Type Required Default Description
action string Yes -- save, recall, list, forget, or gc
key string Depends -- Key name. Required for save, recall, and forget. Charset: [a-zA-Z0-9_.-], max 128 chars.
value string For save -- Value to store. Max 16 KiB.
scope string No "project" Omit for the auto-detected current project. Use "global", "project", "task", or "agent".
scope_id string Depends auto project root Explicit project path, task ID, or agent name. Required for task and agent; invalid with global.
kind string No "fact" Memory type: "fact", "directive", or "lesson"
pin bool No false Keep this entry during memory garbage collection
expires_in string No -- Relative expiry duration such as "12h", "7d", or "2w"
include_values bool For list false Include values and metadata in list output instead of keys only
request_id string No -- Idempotency key for mutating actions. The top-level request ID is copied here automatically when present.

Returns by action:

Action Success result
save "saved: <key>"
recall The stored value string
list {"keys": [...], "count": N} or {"entries": [...], "count": N} with include_values: true
forget "forgotten: <key>" (idempotent; not found is success)
gc {"deleted": N, "idempotency_deleted": N, "scope": "..."}

Notes:

  • Stored in a SQLite database at ~/Library/Application Support/jinn/memory.db on macOS (~/.config/jinn/memory.db on Linux). Override the base dir with JINN_CONFIG_DIR (the DB lives at $JINN_CONFIG_DIR/jinn/memory.db).
  • Keys are scoped by (scope, scope_id). With no scope, jinn uses project and auto-detects the nearest .git ancestor of its working directory, falling back to the working directory itself.
  • scope: "project" accepts an optional scope_id path. scope: "task" and scope: "agent" require a caller-supplied scope_id. scope: "global" cannot have a scope_id.
  • The DB directory is created with mode 0700. Writes use WAL journaling with a 5s busy timeout for cross-process safety.
  • Legacy memory.json files are not imported automatically.
  • Read-only recall and list do not create or migrate a database. Expired non-pinned rows are hidden immediately, and new saves reject pin:true combined with expiry.
  • recall on a missing key returns ok: false with suggestion: "use action=\"list\" to see available keys".
  • gc removes expired, unpinned memories and old idempotency rows. Pass scope to restrict memory cleanup to one scope bucket.

Save a value:

echo '{"tool":"memory","args":{"action":"save","key":"db.host","value":"localhost"}}' | jinn

List all keys:

echo '{"tool":"memory","args":{"action":"list"}}' | jinn

List values and metadata:

echo '{"tool":"memory","args":{"action":"list","include_values":true}}' | jinn

Save an expiring task-scoped lesson:

echo '{"tool":"memory","args":{"action":"save","key":"migration.lesson","value":"retry failed rows after backfill","kind":"lesson","scope":"task","scope_id":"backfill-2026-06","expires_in":"14d"}}' | jinn

Forget a key:

echo '{"tool":"memory","args":{"action":"forget","key":"db.host"}}' | jinn

Garbage-collect expired memories and old idempotency rows:

echo '{"tool":"memory","args":{"action":"gc"}}' | jinn

Language Server

lsp_query

Query a language server for semantic information at a source location.

echo '{"tool":"lsp_query","args":{"action":"hover","path":"main.go","line":12,"character":5}}' | jinn

Parameters:

Name Type Required Default Description
action string Yes -- definition, references, hover, symbols, diagnostics, or rename
path string Yes -- File path relative to working directory
line int Unless symbol -- 1-based line number of the symbol
character int No -- 1-based character offset within the line
symbol string No -- Identifier name. Supply instead of line/character and jinn resolves the declaration position from the file's document symbols (errors if the name is missing or not unique); with an explicit line it resolves just the column.
new_name string For rename -- New identifier name. Required when action is rename.

Supported extensions and servers:

Extension Server binary Install hint
.go gopls go install golang.org/x/tools/gopls@latest
.rs rust-analyzer rustup component add rust-analyzer
.py pylsp pip install python-lsp-server
.ts, .tsx, .js, .jsx typescript-language-server npm install -g typescript-language-server typescript
.c, .h, .cpp, .cc, .cxx, .hpp clangd bundled with LLVM or via system package manager
.java jdtls install the Eclipse JDT Language Server
.lua lua-language-server https://github.com/LuaLS/lua-language-server
.zig zls https://github.com/zigtools/zls

Returns by action:

Action Result format
definition file:line:col of the definition site
references One file:line:col per reference, up to 100. Truncation noted with [truncated: showing N of M].
hover Documentation / type signature string from the server
symbols Kind Name (line:col) table for every symbol in the file
diagnostics One file:line:col severity source/code: message line per diagnostic

Notes:

  • The language server is started, queried, and torn down within a single call. There is no persistent daemon.
  • diagnostics uses the LSP pull diagnostics request and may depend on server support.
  • rename returns a preview of changes; it does not modify files.
  • Hard timeout: 10 seconds per query. Slow server startups may cause timeouts on cold runs.
  • If the server binary is not on PATH, ok: false is returned with a suggestion containing the install command.
  • Path must be inside the working directory (normal path security applies).

lsp_batch

Run up to 20 semantic queries while starting each required language server only once. Results preserve input order and report per-item success or structured failure, so one bad query does not discard the others.

echo '{"tool":"lsp_batch","args":{"queries":[{"action":"symbols","path":"internal/jinn/engine.go"},{"action":"diagnostics","path":"main.go"}]}}' | jinn

Each query accepts the same fields as lsp_query. The JSON result contains results, succeeded, failed, and server_starts; every result item contains its zero-based index, ok, and either result or error with error_code/suggestion. Queries sharing a server binary reuse one initialized session. The total batch budget is the per-query timeout multiplied by its group size, capped at two minutes.

Get definition:

echo '{"tool":"lsp_query","args":{"action":"definition","path":"cmd/jinn/main.go","line":15,"character":12}}' | jinn

List symbols:

echo '{"tool":"lsp_query","args":{"action":"symbols","path":"internal/jinn/engine.go"}}' | jinn