Shoddy · Fettler
Install it, register it, turn it on in a project, check it. Four steps, one command each. The pages after this one are the detail behind them.
One file. No .NET runtime, no installer, nothing else from this
repository. X.Y.Z below is whatever version you
downloaded.
$dir = "$env:LOCALAPPDATA\Programs\fettle"
New-Item -ItemType Directory -Force $dir | Out-Null
Expand-Archive fettle-X.Y.Z-win-x64.zip -DestinationPath $dir -Force
Get-ChildItem $dir -Recurse | Unblock-File
$user = [Environment]::GetEnvironmentVariable('Path', 'User')
if ($user -notlike "*$dir*") {
[Environment]::SetEnvironmentVariable('Path', "$user;$dir", 'User')
}mkdir -p ~/.local/bin
tar -xzf fettle-X.Y.Z-osx-arm64.tar.gz -C ~/.local/bin
xattr -d com.apple.quarantine ~/.local/bin/fettle # macOS onlyThose commands also put the folder on PATH — the
list of folders your terminal searches when you type a command.
Now open a new terminal, because the one you just used still has
the old PATH. Then check it:
fettle --version
fettle 1.0.0PATH.Registering tells your assistant that Fettler exists, so it can
launch it. Do this once per account, not once per project.
--all means every assistant client this machine actually
has; ones that are not installed are skipped rather than created.
--dry-run prints exactly what the real run would do and
writes nothing. Run it first, every time:
fettle setup --all --global --dry-run
fettle setup --all --globalchanged:
C:\Users\you\.claude.json register fettler under mcpServers
note: fettle is on PATH at C:\Users\you\AppData\Local\Programs\fettle\fettle.exe, so the
registration names it plainly
note: this is the machine level and declares no tree; each project still needs its own
.fettler.json - run `fettle setup claude-code --local` in the tree, which creates one
declaring that folder as 'work'
note: not written: Read, Write, Edit, NotebookEdit, Grep, Glob, Bash, PowerShell, Monitor,
BashOutput, TaskOutput would be denied in C:\Users\you\.claude\settings.json. Changing
how the assistant works in every project needs --deny said out loud.fettler.json. And it
only offers the deny list — the settings entry that
blocks the assistant's built-in file tools, so Fettler becomes the only
route. Adding --deny writes that too, for every project
this account ever opens. Leave it off for now; step 3 writes it where
you can see it.Change to your project folder and run this. Same
--dry-run habit:
fettle setup claude-code --local --dry-run
fettle setup claude-code --localFour files, and that is the whole of it:
changed:
C:\work\myproject\.fettler.json created, declaring this folder as 'work'
C:\work\myproject\.mcp.json register fettler under mcpServers
C:\work\myproject\.claude\settings.json deny the built-in Read, Write, Edit, NotebookEdit, Grep, Glob, Bash, PowerShell, Monitor, BashOutput, TaskOutput
C:\work\myproject\CLAUDE.md add a Fettler instruction block
note: the shell is denied too, so every command through it now stops. Allow back only what
this project needs - "allow": ["Bash(git status:*)"] and the like - or declare them as
tasks in .fettler.json, which run inside the boundary. Never allow a command that writes files| File | What it does |
|---|---|
.fettler.json | The boundary. Declares this folder as a tree called work. Fettler will touch nothing outside it. |
.mcp.json | Tells the assistant to launch Fettler. |
.claude/settings.json | Denies the built-in file tools and the shell, so Fettler is the route rather than one option among several. |
CLAUDE.md | Tells the assistant, in words, to use it. |
setup and doctor are
the only two verbs that run before .fettler.json
exists. That is why this step is one command and not two:
--local writes the very file every other verb requires.
Every other verb in an undeclared folder refuses, and says so..fettler.json as a
task, or name it in allow —
"allow": ["Bash(git status:*)"]. Never allow one that
writes files. The reasoning is
the shell section.To use another client instead of claude-code:
claude-desktop, vscode-copilot,
github-copilot, or --all for every one this
machine has.
fettle doctorfettle 1.0.0 C:\Users\you\AppData\Local\Programs\fettle\fettle.exe
on PATH: C:\Users\you\AppData\Local\Programs\fettle\fettle.exe
tool inventory drawn 2026-08-20; check 2.8 says nothing about a tool added to the client since
claude-code global healthy registered and launchable as ...fettle.exe
C:\Users\you\.claude.json
claude-code repo healthy registered and launchable as ...fettle.exe
C:\work\myproject\.mcp.json
claude-desktop repo n/a this client has no project scope, so there is nothing to configure here
what could let the assistant go round the boundary:
- 2.8 nothing denies the built-in Read, Write, Edit, NotebookEdit, Grep, Glob, Bash,
PowerShell, Monitor, BashOutput, TaskOutput, so Fettler is an option rather than
the route; `fettle setup claude-code --local` writes the denies
C:\Users\you\.claude\settings.jsonThe block after the rows is the second half of the report, and it is the half worth reading. The finding shown here is the expected one. Step 2 registered the machine and deliberately did not touch its deny list, so the global settings file still has nothing in it. Your project is covered; every other project on this account is not.
| Word | Meaning | Exit |
|---|---|---|
healthy | registered, and the command it names actually launches | 0 |
n/a | nothing to configure at that level for that client — not a fault | 0 |
absent | the client is here; Fettler is not registered with it | 1 |
broken | registered and wrong — usually the command does not resolve | 2 |
Then restart your assistant. A client reads its server list at launch. A session already running does not have Fettler and will not notice it appear.
The report is two halves, and they are fixed in different places. The wiring is one row per client per level, ending in a verdict. The findings come after it, under what could let the assistant go round the boundary. Each finding is numbered, and each is a route past Fettler rather than a broken registration.
| What you see | What to do |
|---|---|
on PATH: no | Step 1 did not take. Putting it on PATH — and open a new terminal. Nothing else works until this does. |
absent | Run step 2 or step 3 for that client and level. The wiring table gives the exact command per row. |
broken | The registration is there, and its command does not resolve. Re-run setup with --command naming the binary, and --force to overwrite an entry that says something else. Details. |
A numbered finding — 2.6, 2.8, B.17… | Everything doctor can report, and how to clear it has a row per check: what it found, and the edit that clears it. |
| Exit 1 with no finding you recognise | Warnings only, nothing broken. The four verdicts says which rows count as what. |
2.15 — your assistant's scratch directory (the
staging folder it writes drafts into) is outside every tree, which
matters now that Write is denied. Declare it, per
step 2 of setting up a
project. 2.8 at the global level — expected,
since step 2 deliberately left the machine-wide deny list alone. And
2.17 if you run any other MCP server, which most people
do: reported as a fact rather than a fault, because the deny list
closes names and another server's tools are not called
Read.Worth doing once, because the guardrail is the reason to install this at all. Run these in the project you just set up.
fettle rootswork C:\work\myproject (default)
can: list read create update rename delete
declared by C:\work\myproject\.fettler.jsonThat is the whole boundary. Now step outside it:
fettle read C:\Windows\win.inirefused: the path is outside every declared tree; ask for the roots to see the boundaryExit 4. And the file that grants the permissions cannot be edited by the thing they are granted to:
fettle write .fettler.json --text "x"refused: .fettler.json is one of the files that say what this tool and the
assistant driving it may do, and it does not edit those. Change it with an
editor.Exit 11. Both refusals are the same whether a person or an assistant asks. The boundary is read at launch, from a file neither of them can write.
| You want | Page |
|---|---|
| To widen the tree, add a second one, or hide a folder from the assistant entirely | Step 1, declaring your trees |
The scratch directory an assistant stages work in, which needs declaring before Write is denied | Step 2, declaring the scratch |
Every flag setup takes | In a project |
| Every exit code a script can branch on | The reference |
| To stop a tree of health or customer records leaving in a transcript | Screening |
| What the permissions mean and why the defaults are what they are | Fettler |
| To run it from a script instead of an assistant, or to register it by hand | The MCP front end |