Use Chisel MCP filesystem tools efficiently — patch-based edits, minimal token reads, shell exec patterns, and anti-patterns to avoid.
You have access to a Chisel MCP server. Chisel exposes precision filesystem tools designed to minimise token cost and maximise safety. The cardinal rule: never read a full file when you only need part of it, and never write a full file when you only need to change part of it.
⚠️
shell_exechas no shell interpreter. Pipe (|),&&,||,$(), and all shell metacharacters are passed as literals to the process — they are never interpreted. Eachshell_execcall runs exactly one command. Compose multi-step logic across separate calls.
Need to find something in a file or directory?
└─ shell_exec (grep / find / sed / awk / stat / ls)
Need to edit an existing file?
├─ Small targeted change (< whole file) → patch_apply
│ └─ patch_apply fails twice on a small file? → write_file (see below)
└─ Overwrite entirely / file is tiny → write_file
Need to add content to the end of a file?
└─ append
Need to create a directory?
└─ create_directory
Need to rename or relocate a file?
└─ move_file
Always start with the minimum slice required. Only escalate if the first call does not give enough context.
| Goal | Call |
|------|------|
| Find which lines contain a symbol | shell_exec grep ["-n", "symbol", "/root/file"] |
| Multi-file symbol search | shell_exec grep ["-rn", "symbol", "/root"] |
| List all headers in a Markdown file | shell_exec grep ["-n", "^#", "/root/file.md"] |
| Extract one section by header | shell_exec sed ["-n", "/^## Section/,/^## /p", "/root/file.md"] |
| Read lines 40–60 of a large file | shell_exec sed ["-n", "40,60p", "/root/file"] |
| Show every line with its number (small files) | shell_exec cat ["-n", "/root/file"] |
| Count occurrences | shell_exec grep ["-c", "pattern", "/root/file"] |
| Get file size / line count | shell_exec wc ["-l", "/root/file"] |
| List directory contents | shell_exec ls ["-la", "/root/dir"] |
| Find files by name | shell_exec find ["/root", "-name", "*.rs"] |
| Inspect file type / metadata | shell_exec stat ["/root/file"] |
| Tail a log | shell_exec tail ["-n", "50", "/root/app.log"] |
Avoid calling cat on a file longer than ~50 lines. Use sed -n 'M,Np' to window in, or cat -n on short files to get line numbers before writing a patch.
patch_apply — primary edit toolSend only the changed lines. The file is written atomically; a hunk mismatch returns PatchFailed and leaves the original untouched.
--- a/src/main.rs
+++ b/src/main.rs
@@ -12,7 +12,7 @@
fn main() {
- let port = 3000;
+ let port = 8080;
start_server(port);
}
Rules for a valid patch:
@@ -L,N +L,N @@ line numbers must be exact for the current file state — both the start line and the count. A wrong line number in the header produces PatchFailed even if the content lines are correct. Always run grep -n or cat -n first to confirm the real line numbers before writing the hunk header.--- /dev/null as the source header; the file must not exist.@@ hunks in a single call — all are applied atomically.When PatchFailed is returned:
grep -n or sed -n 'L-3,L+3p'.PatchFailed happens twice on a file under ~100 lines: stop debugging the hunk. Use cat -n to read the whole file, correct it in full, and use write_file instead.write_file — full overwriteUse when:
patch_apply has failed twice on a short file — write_file is the safe recovery path.appendUse for log entries, appending lines to a config, or adding to an incrementally-built file. The file must already exist.
shell_exec sed -iFor bulk replacements (rename a symbol, fix a repeated pattern), sed mutates the file directly — no diff needed.
⚠️
sed -ihas different signatures on macOS (BSD sed) vs Linux (GNU sed). Use exactly the forms below — do not improvise alternatives.| Platform | Argument array | |---|---| | Linux (GNU sed) |
["-i", "s/Old/New/g", "/abs/path/file"]| | macOS (BSD sed) |["-i", "", "s/Old/New/g", "/abs/path/file"]|On macOS the empty string
""is the backup-suffix argument (meaning: no backup file). Chisel passes it through to BSD sed correctly as a separate argument — do not omit it, do not replace it with-i.bak, and do not combine it with-iinto a single arg like"-i''". Any deviation produces a cryptic BSD sed parse error (command a expects \…) and wastes turns.If you are unsure which platform you're on, use
shell_exec stat ["/proc/version"](Linux) orshell_exec uname ["-s"]to check. Alternatively, preferpatch_applyorwrite_filefor single-file edits — they are platform-independent.
# Linux
shell_exec sed ["-i", "s/OldName/NewName/g", "/root/src/lib.rs"]
# macOS — the "" is mandatory and is passed correctly; do not second-guess this
shell_exec sed ["-i", "", "s/OldName/NewName/g", "/root/src/lib.rs"]
A successful in-place edit returns exit_code: 0 with empty stdout. Non-zero exit or any stderr output means the expression or path is wrong — fix those, do not change the -i form.
The standard low-token edit loop:
grep -n "target_symbol" /root/file — find the line number.sed -n "L-5,L+10p" /root/file — read a tight window around it.@@ -L,N +L,N @@ header matches exactly.patch_apply — send the diff against that exact content.Short files only: use cat -n /root/file in step 1 to see every line numbered at once — faster than a grep/sed pair when the file is < 50 lines.
ls -la /root — top-level layout.find /root -name "*.rs" -not -path "*/target/*" — enumerate source files.grep -rn "struct MyStruct" /root/src — locate the definition.sed -n "L,L+30p" /root/src/file.rs — read the struct body.Relative paths are auto-anchored to root.
.means the root itself,subdir/filemeansroot/subdir/file. Absolute paths also work. Traversal (../escape) is always blocked.
Use write_file. Once it exists, all subsequent edits go through patch_apply.
Preferred — one call via find -exec … {} +:
find -exec cmd {} + batches all matched paths into a single command invocation. No shell, no loop, one shell_exec call regardless of how many files match.
# macOS (BSD sed)
shell_exec find ["/root/src", "-name", "*.rs", "-exec", "sed", "-i", "", "s/OldName/NewName/g", "{}", "+"]
# Linux (GNU sed)
shell_exec find ["/root/src", "-name", "*.rs", "-exec", "sed", "-i", "s/OldName/NewName/g", "{}", "+"]
The {} placeholder and + terminator are passed literally to find — they are not shell metacharacters and require no escaping.
Fallback — one call per file (only if find -exec is unavailable or the pattern needs per-file logic):
grep -rl "OldName" /root/src — get the file list.sed ["-i", "s/OldName/NewName/g", "/root/src/file.rs"]sed ["-i", "", "s/OldName/NewName/g", "/root/src/file.rs"]create_directory /root/src/handlers
write_file /root/src/handlers/mod.rs "..."
write_file /root/src/handlers/auth.rs "..."
| Error | Meaning | Fix |
|-------|---------|-----|
| PatchFailed | Context lines or hunk header don't match current file | Re-read with grep -n / cat -n, fix both header line numbers and context, regenerate patch. If it fails twice on a short file, use write_file. |
| OutsideRoot | Path escapes configured root | Use only paths under the root the server was started with |
| NotFound | File doesn't exist | Use write_file to create, or check path with find |
| ReadOnly | Server in read-only mode | Writes are disabled; only reads and shell_exec work |
| CommandNotAllowed | Command not in whitelist | Use only: grep sed awk find cat head tail wc sort uniq cut tr diff file stat ls du |
| Anti-pattern | Why | Do instead |
|---|---|---|
| cat /root/large_file.rs | Returns thousands of tokens you mostly won't use | grep -n to locate, sed -n to slice; cat -n only on files < 50 lines |
| write_file for a 3-line change in a 500-line file | Uploads entire file, risks hallucination corrupting unrelated lines | patch_apply with a targeted hunk |
| Shell pipelines in a single shell_exec (cmd1 \| cmd2) | No shell interpreter — \|, &&, $() are literals, not operators | Make separate shell_exec calls; compose in your logic |
| find / -name "*.env" | Path validated against root; will be blocked or return nothing useful | Always scope find to /root/... |
| Sending a patch with approximate context lines or wrong line numbers | PatchFailed — both context and @@ -L,N @@ must be exact | Run grep -n or cat -n first, then diff against the real content |
| Running one sed call per file for a bulk replacement | N tool calls when 1 will do | find ["/root", "-name", "*.ext", "-exec", "sed", "-i", "", "s/x/y/g", "{}", "+"] — batches all files into one sed invocation |
| sed -i "s/x/y/" file on macOS without "" | BSD sed requires an explicit suffix arg — omitting it silently misparses arguments | macOS: ["-i", "", "s/x/y/g", "/abs/file"]; Linux: ["-i", "s/x/y/g", "/abs/file"] |
| Using -i.bak or -i'' instead of ["-i", ""] | Chisel passes the empty string through correctly — no workaround needed | Stick to ["-i", ""] two-arg form; do not invent alternatives |
| Retrying with a different -i form after a BSD sed parse error | The error is the expression or path, not the -i "" form | Fix the sed expression or path; keep ["-i", ""] as-is |
| Continuing to retry patch_apply after two failures on a small file | Wastes turns; context drift compounds the problem | cat -n the file, use write_file to rewrite it correctly |
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer