File and Shell Tools

Four tools cover file content over the API: read, write, edit and bash. Paths are workspace-absolute (/Projects/auth.py), Monofile versions every write, and the writes of one call land in one changeset. The request envelope is the same for all of them, see Calling Tools.

Terminal
curl -X POST "https://www.monofile.ai/api/tools/read" \
  -H "Authorization: Bearer $MONOFILE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"path": "/Projects/auth.py", "includeMetadata": true}'

Files

read

Returns a file's raw content, optionally with its metadata.

InputTypeRequiredDescription
pathstringyesFull path to the file
includeMetadatabooleannoAdd id, name, path, mimeType, language, size, lines and modified. Default false

Scope: files:read. Ask for metadata when you plan to edit: modified is the timestamp you pass back as expectedUpdatedAt.

POST/api/tools/read
{ "path": "/Projects/auth.py", "includeMetadata": true }

write

Creates a file, or overwrites one that already exists. Monofile keeps the previous content as a version, so you can restore it after an overwrite.

InputTypeRequiredDescription
pathstringyesFull path to create or replace
contentstringyesThe new content
createParentsbooleannoCreate missing parent folders. Default true

Scope: files:write. Two file types have their own tools because their content is machine-generated JSON. Use create_diagram for .draw and canvas_edit for .canvas, both on Content Tools.

POST/api/tools/write
{ "path": "/Projects/notes/plan.md", "content": "# Plan\n\n- [ ] draft\n" }

edit

Replaces an exact string inside a file. Prefer it to write on an existing file, because it changes one region and leaves the rest of the document alone.

InputTypeRequiredDescription
pathstringyesFull path to the file
old_stringstringyesThe exact text to find, case-sensitive
new_stringstringyesThe replacement. Empty deletes the matched text
replace_allbooleannoReplace every occurrence. Default false
dryRunbooleannoPreview without writing. Default false
showDiffbooleannoInclude a unified diff in the response. Default true
expectedUpdatedAtstringnoISO timestamp from a prior read. Refuses the edit if the file changed since

Scope: files:write.

POST/api/tools/edit
{
  "path": "/Projects/auth.py",
  "old_string": "def login(user):",
  "new_string": "def login(user, remember=False):"
}

The edit contract

The tool matches old_string literally. No regular expressions, no whitespace normalization, no case folding. Most failed calls trip on that, and each failure has its own code:

ErrorWhat happenedWhat to do
NO_MATCHold_string is not in the fileRead the file again. Your copy is stale, or the indentation or line endings differ from what you assumed
MULTIPLE_MATCHESMore than one occurrence and replace_all is false. The message carries the countExtend old_string with surrounding lines until it is unique, or set replace_all: true if you really mean all of them
EDIT_CONFLICTYou passed expectedUpdatedAt and the file changed after that timestampRe-read, rebuild old_string from the new content, retry
INVALID_INPUTold_string is empty, or equal to new_stringSend a non-empty search string that differs from the replacement

Shell

bash runs a command against your workspace. Read-only commands answer instantly. Everything else needs a real machine, so it runs in a sandbox, and the sandbox is part of paid plans.

The two tiers

The built-in shell is on every plan. A read-only chain of the commands below, joined by ;, && or ||, runs in-process straight against storage. It is instant and always sees the latest content.

CommandFlags it reads
pwdnone
ls-l, -a, -h
tree-L N, --depth N
cat-n
head / tail-n N
statnone
wc-l, -w, -c
find-name GLOB, -type f or -type d, -maxdepth N, -mtime N, -size N. Predicates are AND-ed, there is no -not or -o
grep-i, -n, -l, -c, -v, -w, -A N, -B N, -C N, --include=GLOB, --exclude=GLOB. Always recursive, JavaScript regex, line numbers on by default
mkdir-p
rm-r, -R, -f
mv / cpcp takes -r, -R
touchnone
echo / printfredirection with > and >>. printf handles %s, \n, \t

One path per command, no glob expansion, and flag values are space-separated (-n 5, not -n5). This tier has no pipes, <, 2>, command substitution, background &, sed, awk, sort or interpreters.

The full shell is part of paid plans (Pro on a personal workspace, Team on an org workspace). Anything that is not a read-only built-in chain runs in a real sh with real coreutils, inside a persistent per-workspace sandbox whose disk mirrors your files. That is where pipes, redirects, sed, awk, sort, jq, git, python3, node and multi-line scripts work. Monofile writes the file changes the command leaves behind into the workspace as versioned edits, one changeset per call.

bash

InputTypeRequiredDescription
commandstringyesThe command to run
timeoutnumbernoFull shell only. Seconds before the command is killed. Default 60, larger values are clamped to 240
cwdstringnoFull shell only. Workspace-absolute working directory without .. segments. Default /
debugbooleannoFull shell only. Adds sandbox and usage detail to the result

The scope a command needs follows from what it does. Reading needs files:read. mkdir, mv, cp, touch and echo/printf with > need files:write. rm needs files:delete. The full shell checks the write-back diff, so a missing scope rejects the whole diff and writes nothing.

POST/api/tools/bash
{"command": "grep -rn \"TODO\" /Projects --include=*.ts"}
Terminal
curl -X POST "https://www.monofile.ai/api/tools/bash" \
  -H "Authorization: Bearer $MONOFILE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"command": "grep -rn \"TODO\" /Projects --include=*.ts"}'

grep and find exit 1 when they match nothing. Treat that as an empty result.

What the result tells you

A built-in run returns output, exitCode and, on failure, stderr. A full-shell run adds these fields:

FieldMeaning
changescreated, updated, deleted and renamed paths the run wrote back
conflictsFiles that changed elsewhere while your command ran. Monofile kept your version, and the one it replaced is in that file's versions
skippedPaths the command touched but Monofile did not write back, each with a reason such as symlink, binary, too-large or media
warningsReadable notes for the timeout, conflicts and skips above
truncated, timedOutOutput hit the cap of 400 lines or 30 KB, or the command was killed. Changes made before a kill are still written back
timingsWhere the milliseconds went. Always present

Errors you can plan for: SANDBOX_BUSY (one command per workspace at a time, retry), RATE_LIMITED, DIFF_TOO_LARGE (more than 200 changed files), INSUFFICIENT_CREDITS, SANDBOX_UNAVAILABLE and TIMEOUT.

Without the paid tier

When a workspace is not entitled to the sandbox, any command the built-in shell knows still runs there, so listing, reading, searching and basic file management keep working. A pipe, a script or an interpreter needs the sandbox, so it comes back as SANDBOX_NOT_ENTITLED, and the message names the built-in alternatives.

GET /api/tools describes the bash tool as your own key sees it, including which tier it has.

Next steps

  • Calling ToolsThe one-endpoint pattern, the MCP-shaped envelope, and the index of every tool.

  • Content ToolsCanvas, diagram, version and checkpoint tools.

  • Errors and LimitsEvery error code, what causes it, and how to recover.

  • Filesystem APIThe manifest and batch endpoints for bulk sync clients.

1220 words