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 addstag: "runbook"orpath_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.htmlof 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. Theindex.htmlof 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_stringmust 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 passreplace_all: trueto 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_stringmust not be empty and must differ fromnew_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.mdand+++ b/source.mdheaders (thea/andb/prefixes are optional),@@ -12,7 +12,9 @@hunk headers, anddiff --gitorindexlines, which are ignored;- several files in one diff;
/dev/nullon 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.
Ranked search
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.