Skip to content
Fermium

Search and edit

Grep across every document you can see, read files by line range, change them with find-and-replace or a unified diff, and roll back through versions.

An agent can treat your documents like a small repository: search their contents, read a window of a file, make an exact edit or apply a diff, and roll back if it went wrong. Every edit keeps the URL and writes a new version.

Search across documents

search_docs(q) greps the contents of every text file in every document you can see: your own documents plus documents that members of your workspace have set to listing: team. It never widens what list_docs would show you.

Matching is line-oriented, like grep. By default q is a literal, case-insensitive substring. Set regex: true for a regular expression and case_sensitive: true to respect case. An invalid regex is rejected with the parser's message.

Narrow the sweep with any of:

Parameter Effect
folder a folder path; "" is the root. recursive (default true) includes subfolders
tag only documents carrying this tag
kinds a list from html, markdown, snippet, file
path_glob a pattern on the file path inside a document, such as *.md or src/*.ts
include_team false searches only your own documents
limit matching files to return, default 50, at most 200
context lines either side of each hit, default 1, at most 10

The result is one entry per matching file: doc_id, title, url, folder, kind, mine, owner_email, path, and matches, each with the 1-based line, its text, and before and after context. A document contributes at most 20 matching lines, so one noisy file does not crowd out the rest.

Prompts that work:

  • "Where did we write down the staging database name?"
  • "Which runbooks mention pgbouncer?" The agent adds tag: "runbook" or path_glob: "*.md".
  • "Find every TODO in the Acme folder." The agent uses folder: "Customers/Acme".

REST: GET /api/search?q=... with the same parameters (kinds comma-separated).

What is searchable

  • Text files up to 1 MiB, decoded as UTF-8: markdown sources, snippet sources, HTML, CSS, JavaScript, JSON, CSV, YAML, SVG and the usual source-code extensions. Binary files, files that are not valid UTF-8, and larger files are not indexed.
  • The generated index.html of a markdown, snippet or file-share document is skipped, because it is rendered from another file in the same document and would double every hit. The index.html of an HTML document is searched.
  • Patterns are capped at 256 characters, and a regular expression looks at the first 20,000 characters of a line.
  • A search has a wall-clock budget of a few seconds. If it runs out, you get the matches found so far rather than a hung request. Narrow the scope and search again.

Search covers a slice of up to 500 documents, newest-updated first. Documents shared with you only through an access grant, without team listing, are not in the slice.

Grep inside one document

grep_doc(doc_id, pattern) searches every text file of a document you can see, or just one file with path. It takes the same regex and case_sensitive flags; context defaults to 2 so the surrounding lines can be copied straight into an edit. The response lists files, each with path and matches, plus total_matches and truncated (true past 500 matches or when the time budget ran out). Asking for a path the document does not have is a not-found error; asking for a binary or oversize file says so.

REST: GET /api/docs/{id}/grep?pattern=....

Read a file

get_file(doc_id, path, start_line, end_line) returns exact text. Lines are 1-based and inclusive; omit both to get the whole file. The response carries content, total_lines, the start_line and end_line actually returned, the document's version and the file's size. It works on any text file in the document, and on team-listed documents owned by colleagues. The same 1 MiB and UTF-8 rules as search apply.

Agents are told to read before they write: an exact edit needs the current text, whitespace included, and reading a window around a grep hit keeps a large file out of the conversation.

REST: GET /api/docs/{id}/text?path=...&start=...&end=....

Edit a file

edit_file(doc_id, path, old_string, new_string) is a careful find-and-replace inside one text file:

  • old_string must appear exactly once. If it matches several times the call fails and tells you the count; add surrounding lines until the snippet is unique, or pass replace_all: true to change every occurrence (renaming a variable, say).
  • Zero matches also fails, with a reminder to re-read the file and copy the text verbatim. Leading spaces and tabs count.
  • old_string must not be empty and must differ from new_string.

One successful edit is one new version. The response is the document object plus path, replacements, changed and version.

REST: POST /api/docs/{id}/edit with {path, old_string, new_string, replace_all}.

Apply a patch

apply_patch(doc_id, diff) takes a unified diff exactly as git diff prints it:

  • --- a/source.md and +++ b/source.md headers (the a/ and b/ prefixes are optional), @@ -12,7 +12,9 @@ hunk headers, and diff --git or index lines, which are ignored;
  • several files in one diff;
  • /dev/null on one side to create or delete a file.

Context must match the current content exactly. There is no fuzz, and hunks must be in order and not overlap. Every hunk is checked against the current text before anything is written, so a bad patch changes nothing. The error names the file, the hunk number and the line that did not match, so the agent can re-read that region and regenerate the patch.

Two rules: a diff may touch each file only once per call, and it cannot delete index.html or a document's primary content (source.md, source.<ext>, or a file share's file). Files that are not rendered pages land together in a single new version; a rendered source is written through its renderer as described next. The response lists files, each with path, action (create, modify or delete) and changed, plus the resulting version.

REST: POST /api/docs/{id}/patch with {diff}.

Edits keep the page in sync

The rendered page of a document is always regenerated from its source, so editing the source is enough:

Document kind Edit this What happens
markdown source.md the page is re-rendered, wiki links resolved again
snippet source.<ext> the page is re-highlighted in the stored language
HTML index.html the page is replaced directly
any another file (styles.css, data.json) the file is replaced in place

The generated index.html of a markdown or snippet document cannot be edited; the error tells you which source file to edit instead. The viewer page of a file share is generated too; change its description with set_description.

Who may edit

The owner of a document, and on Business and above anyone holding edit access to it through a grant (see Members and roles). Editors can change content, the title and tags; moving, listing, visibility, retention, alias and delete stay with the owner. Reading and searching follow the usual rule: your own documents and team-listed ones.

Edits made through an agent count against the account's publish quota of 120 per hour, like any other write.

Versions

Every publish, update, file change, edit, patch and restore adds one version; nothing is ever overwritten. list_versions(doc_id) returns the history newest first: version, created_at, created_via (mcp, ui, upload or system), total_bytes, current, and files with each path's size and SHA-256.

restore_version(doc_id, version) rolls back by creating a new version whose file set equals the old one. History is not rewritten, the URL does not change, and a rollback is itself undoable. Restoring content identical to the current version is a no-op and the response says changed: false. A snippet or markdown document restored to a version with a different source file name picks that source up again.

REST: GET /api/docs/{id}/versions and POST /api/docs/{id}/versions/{version}/restore.

Prompts that work:

  • "Show me the history of the runbook." then "Go back to version 3."
  • "Undo that last change." The agent lists versions and restores the one before the current.

Writes that check the version first

edit_file and apply_patch write whatever the document holds now. When an agent shares a document with a person in the browser editor or with another agent, it should use write_file(doc_id, path, content, base_version) instead: the write lands only if the document is still at base_version, and otherwise fails with the current version, who changed it and a diff of the file, so the agent can re-read and merge. lock_doc takes a short edit lease for a multi-step change. Both are described in Editing in the browser and the MCP reference.

The grep above is exact and line-oriented, which is what an agent wants. The wiki's search box and GET /api/search/ranked use a ranked full-text index over the same files instead: results are ordered by relevance with the title weighted above the body, one result per document, each with a highlighted excerpt. It takes the same folder, recursive, tag, kinds and include_team scoping. See the REST reference.

In the app

The inspector's Edit button opens the browser editor for the document's files. The Versions tab lists every version with Compare and Restore.