Skip to content
Fermium

MCP reference

Every tool on the Fermium MCP server, with parameters, the response shape, and example prompts.

Server: https://fermium.app/mcp, streamable HTTP, stateless. Every request carries Authorization: Bearer <token>; the token comes from the OAuth flow your client runs, or from a static dsk_ token. Without one the server answers 401 with a WWW-Authenticate header that points at the OAuth discovery document. See Authentication.

Setup for the common clients is in Connect Codex, Connect Claude Code, and Other clients.

Agent tool names

Agents were previously called Brains. Tool discovery now advertises Agent names:

Current tool Previous callable name
list_agents list_brains
load_agent load_brain
create_agent create_brain
agent_read, agent_write, agent_search brain_read, brain_write, brain_search
list_agent_connections, request_agent_tool_call list_brain_connections, request_brain_tool_call
run_agent run_brain

The same substitution applies to the other non-legacy tools with brain or brains in their names. On these renamed tools, pass agent instead of brain. For example, load_agent({"agent": "support"}). Authorization, proposals and tool-call approvals use the same checks as before.

Previous names and their brain argument remain callable for saved clients. Response field names such as brains, brain and from_brain, stored filenames such as BRAIN.md, and REST paths retain their existing spelling. Tools whose names did not change, including propose_change, list_proposals and publishing tools, also retain their existing parameters. The reference sections below keep the previous signatures for compatibility; use the advertised schema for a current Agent alias. Older legacy_brain navigation tools are callable but are no longer advertised.

File activity tools

get_file_activity, get_folder_activity, list_high_traffic_files, list_file_activity_events and activity_status provide permission-filtered totals, charts, retained events and lightweight change polling. See Activity API and MCP for parameters and examples.

Server instructions

The server provides usage guidance when the client connects. Agents can call usage_guide for supported publishing, sharing, document-editing and Agent workflows. Use list_docs before publishing something that may already exist; use list_agents and load_agent before working with an Agent's context.

Publishing defaults to restricted access. Sharing requires the appropriate grants; moving a document does not change its link or grant access. Connected Agent sources never widen the caller's document permissions. Follow the responses' version, proposal and approval states rather than assuming a requested change is already live.

The document object

Every tool that touches a document returns the same object:

{
  "doc_id": "01J7ZK3N9QW0X4B2C6D8E0F1G2",
  "slug": "xBVNwKdi7ukQhXrWhQuz4g",
  "title": "Q3 roadmap",
  "url": "https://fermiumusercontent.app/d/xBVNwKdi7ukQhXrWhQuz4g/",
  "visibility": "unlisted",
  "listing": "private",
  "retention_days": 0,
  "expires_at": null,
  "version": 3,
  "total_bytes": 48211,
  "created_at": "2026-09-15T14:02:11.482910+00:00",
  "updated_at": "2026-09-15T16:40:03.118204+00:00",
  "kind": "html",
  "folder": "Planning/2026",
  "tags": ["q3-review"],
  "alias": "q3-roadmap",
  "icon": "lucide:rocket/amber",
  "legal_hold": false,
  "root_id": 4,
  "root": { "id": 4, "slug": "engineering", "name": "Engineering", "kind": "shared" },
  "doc_type": null,
  "attrs": { "language": "python", "source_path": "source.py" },
  "files": ["index.html", "style.css"]
}

folder is the path the document lives in; "" means the top of its root. root says which root that is: null (with root_id: null) for a document in someone's My documents, otherwise {id, slug, name, kind}. doc_type is null for ordinary documents and instructions, index, skill, workflow or memory for an agent file. alias is the document's human link name, or null. icon is the document's icon in the grammar below, or null for the kind glyph. legal_hold is true while an Enterprise legal hold freezes the document. attrs appears only when the kind has attributes (snippets: language, source_path; markdown: source_path; file shares: filename, display, optional description, poster, width, height, duration). files appears on tools that return the file list. url uses your custom domain when one is set as the link host.

Shared parameters

The publish_* tools and create_upload accept the same optional settings:

Parameter Type Default Notes
title string kind-specific HTML defaults to "Untitled"; markdown uses the first heading; snippets "Snippet"; files the file name
visibility restricted, unlisted, password, workspace restricted Restricted requires sign-in plus explicit access, on every plan. workspace is a legacy paid mode with the same grant checks. Unlisted and password links are deliberate external publication choices.
password string required when visibility is password
listing private, team private Placement only; never grants permission
retention_days integer >= 0 30 0 keeps forever; a workspace policy may bound it
folder string personal root or Members/<member name> in a shared root where the document lives, a path like Customers/Acme; created on the fly
root string My documents "general" or a shared root's slug from list_roots; needs edit there. The document then belongs to the root. "~" or omitted is the user's My documents
tags string list [] topics that cut across folders; lowercase slugs, max 10
idempotency_key string a retry with the same key returns the first document instead of creating another
nav object publish_* only, for agents of the earlier form. {group, navigation?, label?, icon?, position?, visibility?} places the new document under a top-level group of an agent's member tree in the same call; the group is created when missing, and navigation defaults to the agent the wiki publishes, or the workspace's default agent. Only when the user asked for the document to appear in the agent or the wiki. The response carries the entry as nav, or nav_error when placement was refused (the document is published either way)
brain string publish_* only, for agents of the earlier form. An agent's slug or id; places the new document in that agent's first group, or under nav.group when both are given. Same rule: only when the user asked for the document to be part of the agent

Folder paths: segments of 1 to 64 letters, digits, spaces, ., _ or -, starting with a letter or digit and not ending in .; / between segments; at most 8 levels and 512 characters; case preserved. A bad segment is rejected with a message naming it.

Publishing

publish_html

Publish a self-contained HTML page up to 20 MiB of UTF-8, including embedded assets. The page becomes index.html; add assets with put_file. All files together must fit within the 20 MiB document total.

Parameter Type Required
html string yes
shared parameters no

Prompt: "Publish this dashboard as a page in Ops/Dashboards, keep it forever, tag it runbook."

publish_markdown

Publish markdown as a rendered page with source.md kept next to it. Tables, highlighted fenced code, task lists, strikethrough, mermaid fences and [[wiki links]] render.

Parameter Type Required
content string yes
shared parameters no

Prompt: "Write up the incident as a markdown postmortem and publish it for the team." The agent adds listing: "team".

publish_snippet

Publish code as a highlighted page with line numbers and a raw link.

Parameter Type Required Notes
content string yes must not be empty
language string no a Pygments lexer name; omit or auto to detect
shared parameters no

Prompt: "Share this SQL query as a snippet." The response includes attrs.language.

publish_file

Publish one file up to 20 MiB, base64-encoded. Images get a viewer page, media a player page, PDFs are served directly, other allowed types get a download page. .md files render as markdown documents unless render is false.

Parameter Type Required Notes
filename string yes plain name with an allowed extension: png jpg jpeg gif webp svg ico mp4 webm m4v mov mkv mp3 m4a aac wav ogg opus flac pdf txt md markdown csv json zip js css
content_base64 string yes the file's bytes
render boolean no default true; only affects .md
description string no markdown, up to 16 KiB, shown under the viewer; not allowed on PDFs
shared parameters no

Returns the document with files. Prompt: "Publish this screenshot so I can send it to the client."

create_upload

Mint a signed upload URL, then send a local file directly from disk with a raw HTTPS PUT. File bytes never pass through the MCP tool call. New .html/.htm uploads become rendered HTML pages with the same sandbox as publish_html, up to 20 MiB. New JS/CSS files get download pages, also capped at 20 MiB. Other file shares, including PDF, video and audio, keep the 2 GiB limit.

Parameter Type Required Notes
filename string yes new files: html htm png jpg jpeg gif webp svg ico pdf txt md markdown csv json zip js css mp4 webm m4v mov mkv mp3 m4a aac wav ogg opus flac
doc_id string no replace a file in this existing document; omit new-document metadata
path string with doc_id target path; index.html replaces an HTML page, or use an asset/source path
base_version integer with doc_id document version from the latest content you read
lock_token string no token from lock_doc; must still be valid when the PUT commits
title, description string no new documents only; description is for file shares, capped at 2,000 characters; HTML has no separate description
visibility restricted, unlisted or workspace no new documents only; defaults to restricted; password is not allowed at upload time
listing, retention_days, tags, folder, root no new documents only, as above

Response:

{
  "upload_url": "https://fermium.app/u/<ticket>",
  "method": "PUT",
  "expires_in_minutes": 60,
  "max_upload_bytes": 2147483648,
  "filename": "fight.mp4",
  "curl": "curl --fail-with-body -sS -T \"<local-file-path>\" \"https://fermium.app/u/<ticket>\"",
  "note": "The PUT response body is the published doc (JSON), including its shareable url."
}

The agent runs the curl line (or you do). The PUT response is the finished document. Re-PUTting the same URL returns the same document; the ticket embeds an idempotency key. Treat the URL as a secret until it expires. Prompt: "Upload the recording at ~/Videos/demo.mp4 with a description linking to the ticket."

For a large page already on disk, call create_upload with filename: "blueprint.html". To replace an existing page, also pass doc_id, path: "index.html" and base_version from your latest read. The URL, sharing and placement stay intact, and the upload adds a version. Editable files are limited to 20 MiB each and together within an HTML document. Generated viewer pages and primary file-share payloads cannot be replaced.

For coordinated edits, acquire a lease with lock_doc, pass its token to create_upload, upload, then unlock_doc. Renew the lease if needed. Access, lock and version are checked again after transfer. A 423 response tells the agent to wait 5 seconds, read the latest content/version and reapply edits before requesting a fresh URL; a 409 also requires refreshing and merging. Download large files through authenticated GET /api/docs/{doc_id}/files/{path} and use its X-Doc-Version. Successful ticket retries return uploaded_version and cannot overwrite later edits.

Updating

All update tools keep the URL. Identical content is a no-op and the response includes changed: false. The owner and, on Business and above, anyone with edit access may update.

update_html

Replace the page with up to 20 MiB of UTF-8. Supporting files count toward the same 20 MiB document total.

Parameter Type Required
html string yes
doc_id string yes
title string no
message string no

HTML documents only. message is a one-line note stored with the version, like a commit message; it shows in file_history and file_blame. Prompt: "Swap the second chart for a table and update the same link."

update_markdown

Parameter Type Required
doc_id string yes
content string yes
title string no
message string no

Markdown documents only. message as on update_html.

update_snippet

Parameter Type Required Notes
doc_id string yes
content string yes
language string no omit to keep the stored language; auto to re-detect
title string no
message string no as on update_html

Snippets only.

set_description

Set, replace or clear ("") the markdown description on a file share. Re-renders the viewer page in place without re-uploading the file. Up to 16 KiB. Not available on PDFs.

Parameter Type Required
doc_id string yes
description string yes

generate_poster

Grab a frame from a video file share, attach it as poster.jpg, and re-render the page so the player shows it and pasted links unfurl with a thumbnail. Runs automatically after video uploads; call it to backfill or refresh.

Parameter Type Required
doc_id string yes

Files inside a document

put_file

Add or replace a supporting file. Exactly one of content or content_base64. Replacing a path writes a new version; identical content is a no-op. Returns the document with files and changed.

Parameter Type Required Notes
doc_id string yes
path string yes relative, no .., at most 5 levels, e.g. img/chart.png
content string one of text
content_base64 string one of binary, up to 20 MiB decoded

Prompt: "Add the chart image to the page as img/chart.png and reference it."

list_files

Parameter Type Required
doc_id string yes

Returns the document plus files: [{path, size, sha256, content_type}].

delete_file

Parameter Type Required
doc_id string yes
path string yes

index.html and a document's primary source or shared file cannot be deleted.

Search and read

Background and limits: Search and edit.

search_docs

Grep the contents of every text file in every document the user can see: their own, the shared roots they can open, and team-listed documents in the workspace.

Parameter Type Default Notes
q string required literal substring by default; at most 256 characters
regex boolean false treat q as a Python regular expression
case_sensitive boolean false
folder string "" for the root; omit for everywhere
recursive boolean true with folder, include subfolders
root string narrow to one root: "~" for My documents, "general" or a shared root's slug
tag string
kinds string list from html, markdown, snippet, file
path_glob string pattern on the file path inside a document, e.g. *.md, src/*.ts
include_team boolean true false for the user's own documents only
limit integer 50 matching files to return, at most 200
context integer 1 lines either side of each hit, at most 10

Response:

{
  "query": "pgbouncer",
  "count": 1,
  "results": [
    {
      "doc_id": "01J7…", "title": "Payments on-call", "url": "https://fermiumusercontent.app/d/…/",
      "folder": "Runbooks", "kind": "markdown", "mine": true, "owner_email": "ada@acme.com",
      "path": "source.md",
      "matches": [{ "line": 42, "text": "Restart pgbouncer before failing over.", "before": ["## Failover"], "after": [""] }]
    }
  ]
}

At most 20 matching lines per document. The generated index.html of markdown, snippet and file documents is skipped. Prompt: "Which runbook mentions pgbouncer?"

grep_doc

Search inside one document the user can see: all its text files, or one with path.

Parameter Type Default
doc_id string required
pattern string required
path string
regex boolean false
case_sensitive boolean false
context integer 2

Returns {doc_id, title, url, version, files: [{path, matches}], total_matches, truncated}. Prompt: "Find every TODO in this document before I edit it."

get_file

Read a text file out of a document, whole or by 1-based inclusive line range. Works on the user's documents and on team-listed ones. Text files up to 1 MiB.

Parameter Type Required
doc_id string yes
path string yes
start_line integer no
end_line integer no

Returns {doc_id, path, content, total_lines, start_line, end_line, version, size}. Prompt: "Show me lines 30 to 60 of the runbook's source."

Editing

edit_file

Exact find-and-replace inside one text file. old_string must occur exactly once unless replace_all is true; zero or several matches fail with the count. One edit is one new version; the rendered page is regenerated (source.md re-renders, source.<ext> re-highlights, an HTML document's index.html is replaced). The generated index.html of a markdown, snippet or file document is refused with a message naming the file to edit instead.

Parameter Type Required Default
doc_id string yes
path string yes
old_string string yes must not be empty
new_string string yes must differ
replace_all boolean no false

Returns the document object plus path, replacements, changed, version. Prompt: "Change the on-call phone number in the runbook to +1 555 0100."

apply_patch

Apply a unified diff, exactly what git diff prints: ---/+++ headers with optional a/ b/ prefixes, @@ hunks, several files, /dev/null for creations and deletions. Context must match exactly; nothing is written unless every hunk applies, and the error names the file, hunk and line. One patch per file per call. Cannot delete index.html or a document's primary content.

Parameter Type Required
doc_id string yes
diff string yes

Returns the document object plus files: [{path, action, changed}] and version, where action is create, modify or delete. Prompt: "Apply this diff to the dashboard."

list_versions

Parameter Type Required
doc_id string yes

Returns {doc_id, versions: [{version, created_at, created_via, total_bytes, current, files: [{path, size, sha256}]}]}, newest first.

restore_version

Roll back by writing a new version equal to an earlier one. History is not rewritten; identical content is a no-op.

Parameter Type Required
doc_id string yes
version integer yes

Returns the document object with files, plus restored_from and changed. Prompt: "Undo the last change to the roadmap."

edit_file, apply_patch and restore_version count against the publish quota of 120 per hour.

Concurrency-aware editing

The tools above write whatever the document holds now, which is right when the agent is the only writer. When a person may be typing in the browser editor at the same time, or another agent shares the document, use these. Background: edit leases.

write_file

Replace one whole file, refusing to clobber a change the agent has not seen. The path picks the updater: source.md re-renders the markdown page, source.<ext> re-highlights a snippet, index.html of an HTML document updates directly, anything else is stored as a supporting file. The generated index.html of a markdown or snippet document is refused.

Parameter Type Required Default Notes
doc_id string yes
path string yes
content string yes text, or base64 with encoding
base_version integer no the version the agent read with get_file or list_docs; the write lands only if the document is still at that version
lock_token string no from lock_doc; without it a write into a document another user has leased is refused
encoding utf8, base64 no utf8 base64 for binary supporting files

Returns the document object plus path, changed, version, sha256 and size. When base_version is stale the tool fails with a message that carries the current version, who changed it, and a unified diff of that file from the agent's version to the current one, ending with Re-read with get_file and retry with base_version=<n>. When another user holds the lease it fails with their email, the lease expiry, and recovery steps: wait 5 seconds, fetch the latest content and version, reapply the intended edits to that content, and retry with its base_version. If still locked, back off and refresh again before the next write. Use get_file, or an authenticated raw download for large files.

Prompt: "Rewrite the runbook's failover section; Ada may be editing it right now." The agent reads with get_file, writes with base_version, and merges if refused.

lock_doc

Take the edit lease on a document before a multi-step change. Locks stop other writers only; reading, viewing and syncing are never blocked. The caller's own lease never blocks them.

Parameter Type Required Default Notes
doc_id string yes owner or editor
ttl_seconds integer no 600 1 to 600; leases are short so a crashed client never blocks a document for long
path string no a hint shown to other editors about which file is being worked on

Returns {doc_id, token, expires_at, acquired_at, holder: {email, user_id}, path, mine: true}. If another user holds the lease the call fails with their email, the expiry, and instructions to wait 5 seconds and base the next write on freshly fetched content and its version. Taking a lease the caller already holds replaces it with a fresh token and expiry.

renew_lock

Parameter Type Required Default
doc_id string yes
token string yes
ttl_seconds integer no 600

Extends the caller's lease; the token stays the same. A lease that expired but was not taken by anyone else can still be renewed.

unlock_doc

Parameter Type Required
doc_id string yes
token string yes

Returns {doc_id, released, forced, lock}. Release as soon as the last write lands so a person can pick the document back up. Breaking someone else's lease is not available to agents; the document's owner can do it through the REST API with force.

list_changes

What changed since the agent last looked: the user's documents, plus team-listed documents and the shared roots they can see with include_team, oldest first, each with its manifest so a client can fetch only files whose hash differs. The contract for building a mirror on this feed is in Building a sync client.

Parameter Type Required Default Notes
since string no the next_cursor from the previous call; omit for everything
folder string no narrow to a folder
recursive boolean no true with folder, include subfolders
include_team boolean no false adds team-listed documents and, when root is omitted, every shared root the user can see
root string no "~": My documents only. A shared root's slug: that root only, whatever include_team says. Rows carry root
limit integer no 200 at most 200
{
  "changes": [
    { "doc_id": "01J7…", "title": "Payments on-call", "folder": "Runbooks", "tags": ["runbook"], "kind": "markdown", "version": 7,
      "updated_at": "2026-09-16T09:12:40+00:00", "changed_at": "2026-09-16T09:12:40+00:00", "deleted": false, "deleted_at": null,
      "owner_email": "ada@acme.com", "listing": "team",
      "manifest": { "index.html": { "sha256": "…", "size": 18211, "content_type": "text/html; charset=utf-8" }, "source.md": { "sha256": "…", "size": 4210, "content_type": "text/markdown; charset=utf-8" } } }
  ],
  "next_cursor": "MjAyNi0wOS0xNlQwOToxMjo0MHwwMUo3…",
  "has_more": false
}

changed_at moves on content writes and on metadata changes (rename, move, tags, visibility, listing, description, alias, legal hold, delete), so a mirror sees a rename even though updated_at did not move. Deleted documents come through with deleted: true and an empty manifest for 30 days after deletion. Prompt: "What changed in Runbooks since yesterday?"

Orientation: tree and bundles

Two tools for getting the lay of the land without paging through list_docs. Both include only documents the user may open, and count the rest in skipped. Background: Folders, tags, and stars.

list_tree

The file tree under a folder, depth levels deep, in one call.

Parameter Type Default Notes
folder string personal root or Members/<member name> in a shared root
depth integer 1 immediate children; 0 walks everything, at most 8 levels
include_team boolean false add other members' documents the user may open; rows then carry own and, for someone else's document, owner
root string My documents "general" or a shared root's slug for that root's tree
{
  "folder": "Runbooks", "depth": 1, "include_team": false,
  "folders": [
    { "path": "Runbooks/Payments", "name": "Payments", "icon": "lucide:wrench/amber", "description": null, "doc_count": 4, "truncated": true }
  ],
  "docs": [
    { "doc_id": "01J7…", "title": "Payments on-call", "kind": "markdown", "icon": null, "alias": "payments-oncall", "size": 4210, "updated_at": "2026-09-16T09:12:40+00:00", "url": "https://fermiumusercontent.app/d/…/", "tags": ["runbook"], "visibility": "workspace" }
  ],
  "doc_count": 5,
  "skipped": 0
}

doc_count on a folder counts visible documents under it recursively. A folder at the depth boundary carries truncated: true instead of docs and children. Up to 5,000 documents are considered per call. Prompt: "What is under Runbooks?"

download_bundle

Mirror a folder locally in one call: a signed, short-lived URL to a zip of the current version of every document under the folder that the user may open.

Parameter Type Default
folder string personal root or Members/<member name> in a shared root
recursive boolean true
include_team boolean false
root string My documents; a shared root's slug bundles that root
{
  "url": "https://fermium.app/api/bundles/<token>",
  "expires_at": "2026-09-16T09:22:40+00:00",
  "bytes": 1048576, "doc_count": 12, "file_count": 31, "skipped": 1,
  "folder": "Runbooks", "recursive": true, "filename": "Runbooks.zip",
  "curl": "curl -sS -L -o Runbooks.zip \"https://fermium.app/api/bundles/<token>\""
}

The URL is good for 10 minutes and one download, and is bound to the user who requested it. Inside the zip: <folder>/<title>/<files> for each document plus a meta.json (id, title, kind, alias, folder, tags, version, visibility, url, updated_at, files) and an index.json at the root listing every document with its version and the count of documents skipped by access rules. A bundle may hold at most 500 MiB uncompressed (the call fails with a 413 before anything is read; bundle a subfolder or pass recursive: false), and an account may mint at most 6 bundles per hour (429). Nothing to bundle is an error rather than an empty zip. Each bundle is recorded in the audit log as doc.bundle.

Agents are told to mirror, grep at disk speed, and then write back through edit_file or apply_patch so versions and links stay intact. For a mirror that stays in step, use the change feed instead; see Building a sync client.

File history, diff and blame

Background: Versions and history. All three need the same access as viewing the document. Text files up to 1 MiB get line counts, diffs and blame; binaries get sizes and hashes.

Every version records actor (who wrote it), via (mcp, ui, upload, system) and an optional message, which agents set with the message parameter on update_html, update_markdown and update_snippet.

file_history

Which versions changed one file, newest first.

Parameter Type Required
doc_id string yes
path string yes
{
  "doc_id": "01J7…", "path": "source.md", "current_version": 7, "text": true,
  "versions": [
    { "version": 7, "created_at": "2026-09-16T09:12:40+00:00", "actor": "ada@acme.com", "created_by_user_id": 3, "via": "ui", "message": null, "change": "modified", "size": 4210, "sha256": "…", "added": 3, "removed": 1 },
    { "version": 2, "created_at": "2026-09-15T16:40:03+00:00", "actor": "bo@acme.com", "created_by_user_id": 7, "via": "mcp", "message": "Add failover section", "change": "added", "size": 3980, "sha256": "…", "added": 120, "removed": 0 }
  ]
}

change is added, modified or removed. text turns false, and added and removed are null, once the file is not text in some version. Prompt: "Who last touched the runbook's source?"

file_diff

Parameter Type Required Notes
doc_id string yes
path string yes
from_version integer yes
to_version integer no defaults to the current version

Returns {doc_id, path, from_version, to_version, from, to, identical, text, unified, hunks, added, removed}. from and to each carry {version, created_at, actor, created_by_user_id, via, message, size, sha256}. unified is the diff as git diff prints it, with a/<path>@v<n> and b/<path>@v<n> headers; hunks is the same change as side-by-side rows, [{old_start, old_count, new_start, new_count, rows: [{type, old_no, old, new_no, new}]}] where type is equal, add, remove or change. For a binary file text is false and unified is null. Prompt: "What changed in the dashboard between version 4 and now?"

file_blame

Parameter Type Required Notes
doc_id string yes
path string yes
version integer no defaults to the current version
{
  "doc_id": "01J7…", "path": "source.md", "version": 7, "current_version": 7,
  "lines": [
    { "line_no": 1, "text": "# Payments on-call", "version": 2, "actor": "bo@acme.com", "created_at": "2026-09-15T16:40:03+00:00", "message": "Add failover section" },
    { "line_no": 2, "text": "Restart the pooler before failing over.", "version": 7, "actor": "ada@acme.com", "created_at": "2026-09-16T09:12:40+00:00", "message": null }
  ],
  "versions": [ { "version": 2, "created_at": "…", "actor": "bo@acme.com", "created_by_user_id": 7, "via": "mcp", "message": "Add failover section" }, { "version": 7, "…": "…" } ]
}

Text files only; a file that is not text in some version fails with a message naming the version. Prompt: "Which lines of the pricing page did a person change by hand?" The agent checks blame before overwriting them.

Icons

Background and the grammar: Icons.

set_icon

Parameter Type Required Notes
target string yes doc:<id>, folder:<path>, tag:<slug>, nav:<entry_id> or navigation:<slug>
icon string yes lucide:<name>[/<colour>], emoji:U+1F680 or a literal emoji, or svg:<id>

Returns {target, icon} with the stored form (an emoji comes back as code points; a bare curated name comes back as lucide:<name>). Unknown names and colours are rejected with a message. Who may set what: documents by the owner or an editor; folders by a member with documents in the folder or a workspace owner or admin; tags by any member; agent members and agents by owners and admins. Prompt: "Give the Acme folder a briefcase icon in blue."

clear_icon

Parameter Type Required
target string yes

Returns {target, icon: null}; the target falls back to its default glyph.

upload_icon

Team and above. Add an SVG to the workspace library.

Parameter Type Required Notes
name string yes 1 to 64 characters of letters, digits, spaces, ., _ and -
svg string yes the file's text, at most 32 KiB

The SVG is validated rather than rewritten: scripts, event-handler attributes, <foreignObject> and similar, DOCTYPE and entity declarations, and any href or url() that leaves the file are rejected with the reason. Returns {id, icon: "svg:<id>", name, url, bytes, sha256, uploaded_by, created_at}; pass icon to set_icon. Uploading identical bytes returns the existing entry.

list_icons

No parameters. Returns {lucide: [names], colors: [12 names], library: [{id, icon, name, url, bytes, sha256, uploaded_by, created_at}], library_available, grammar}. Call it before set_icon with a name you are not sure ships.

Agents as agent memory

Background: Agents, Skills and Workflows. An agent is a set of typed markdown files (BRAIN.md, INDEX.md, skills/*.md, workflows/*.md, memory/**/*.md) plus a journal, sources and includes. The tools are written for any harness. Two rules hold for every call here:

  • Protected files change by proposal. BRAIN.md, skills and workflows are never changed by these tools directly; a write files a proposal that a person with write access decides in the app. Agents cannot decide proposals.
  • Sources only narrow. Source documents appear only when the calling user may open them.

brain is a slug from list_brains; "personal" (also "me", "my", "~") is the user's own private agent. An agent the user cannot read answers "no agent" as if it did not exist.

The agent object:

{
  "id": 7, "slug": "support", "name": "Support", "kind": "brain", "personal": false, "owner": null,
  "icon": "lucide:life-buoy", "description": "How we answer customers: policies, macros, escalation.",
  "journal_budget": 40000, "legacy_navigation_id": null,
  "files_count": 12,
  "counts": { "skills": 2, "workflows": 1, "memory": 7, "sources": 3, "includes": 1 },
  "pending_proposals": 1,
  "created_at": "…", "updated_at": "…",
  "my_level": "edit", "can_write": true, "can_manage": false
}

kind is brain or personal_brain; owner is the owner's email for a personal agent. my_level is view (read and propose) or edit (write and decide). legacy_navigation_id is set on an agent converted from the earlier form.

list_brains

No parameters. Returns {brains: [brain], can_create}: the workspace agents the user can read, then their personal agent. Agents call it at the start of a session and pick the agent whose name or description matches the task. Prompt: "Which agents do we have?"

load_brain

Everything an agent needs to start working on a concern, in one payload. Several agents can be loaded in one session.

Parameter Type Default Notes
brain string "personal"
budget integer 60000 character budget for the payload; at least 2000
{
  "brain": { "slug": "support", "…": "…" },
  "how_to": "Read instructions_markdown (BRAIN.md) first and follow it. …",
  "instructions_markdown": "# Support\n\n…",
  "index_markdown": "# Support: index\n\n…",
  "skills": [ { "path": "skills/reply-style.md", "title": "Reply style", "markdown": "# Reply style\n\n…", "from_brain": "support" },
              { "path": "skills/brand.md", "title": "Brand", "markdown": "…", "from_brain": "brand" } ],
  "workflows": [ { "path": "workflows/weekly-digest.md", "title": "Weekly customer digest",
                   "summary": "The Monday summary of open escalations.", "triggers": ["weekly digest"], "from_brain": "support" } ],
  "memory": [ { "path": "memory/customers/acme.md", "title": "Acme", "updated_at": "…", "excerpt": "# Acme\n\nRenews in March…" } ],
  "recent_notes": [ { "id": 311, "ts": "…", "topic": "acme", "text": "Acme wants the invoice split by team.", "actor": "ada@acme.com" } ],
  "sources": [ { "id": 5, "kind": "folder", "target": "general:Support", "label": "general/Support", "note": "Policies and macros.", "exists": true } ],
  "source_index": [ { "doc_id": "01J9…", "title": "Refund policy", "alias": "refund-policy", "kind": "markdown", "root": "general",
                      "folder": "Support/Policies", "tags": ["customer-facing"], "updated_at": "…", "via": ["folder:general:Support"] } ],
  "includes": [ { "slug": "brand", "name": "Brand" } ],
  "includes_withheld": 0,
  "pending_proposals": 1,
  "consolidation": { "needed": false, "unconsolidated_chars": 8120, "unconsolidated_notes": 14, "budget": 40000, "hint": "No consolidation needed." },
  "truncated": []
}

skills carry the Agent's own guidance followed by included guidance in include order; earlier entries take precedence on conflict. workflows describe jobs the agent may recognize; read the recipe with brain_read. memory provides excerpts and file paths, and recent_notes provides recent unconsolidated notes. source_index lists only documents the caller may open, with root ("~" for a personal document) and via, the matching sources; read them with get_file. includes_withheld counts included Agents unavailable to the caller.

The payload may omit excerpts or bodies to fit the requested budget. Check truncated; a skill with markdown: null and truncated: true needs a follow-up brain_read. If the source index is shortened, source_index_total gives its full count. Do not treat an omitted body as an empty file. The load is recorded in the Agent's journal. Prompt: "Load the support agent, then answer…"

brain_read

Parameter Type Required Notes
brain string yes
path string yes BRAIN.md, INDEX.md, skills/<name>.md, workflows/<name>.md or memory/<topic>.md

Returns {brain, path, name, type, protected, doc_id, title, version, bytes, updated_at, updated_by, markdown, can_write, write_mode}. type is instructions, index, skill, workflow or memory. write_mode is direct for the index and memory and proposal for protected files. Pass version as base_version when writing the file back. Reads are recorded in the journal. Source documents are not agent files; read those with get_file.

brain_write

Write a whole file; creates it when new.

Parameter Type Required Notes
brain string yes
path string yes an agent path, as for brain_read. Segments use letters, digits, ., _ and -; skills/ and workflows/ hold files directly; memory/ may nest four folders deep
content string yes the complete markdown
message string no one line on what changed and why; shown in history, the journal and the proposal
base_version integer no the version from brain_read; when the file has moved on the call fails with a 409 and a diff instead of overwriting

For INDEX.md and memory/ the file is saved at once as a new version (write access needed) and the call returns {brain, mode: "direct", written: true, changed, path, type, version, …}; changed is false when the content was identical. For a protected path the same call files a proposal and returns {brain, path, type, mode: "proposal", proposal, note}; the file is not live yet, and agents are told to say so and not to retry. Prompt: "Write up what we learned about the billing migration in the support agent."

brain_remember

Append one note to the journal: a durable fact, decision, preference or correction.

Parameter Type Required Notes
brain string yes "personal" for facts about the user
note string yes at most 4,000 characters
topic string no a short label that groups notes, up to 120 characters

Needs write access. Returns {event, consolidation}: the journal event (see brain_log) and the consolidation status. Prompt: "Remember that Acme renews in March."

Parameter Type Default
q string required
brain string "personal"
limit integer 10, at most 50

Searches the Agent's files, included skills/workflows and source documents the caller may open. Returns relevant keyword/text matches and matching INDEX.md lines. Each result includes a snippet and read_with, the tool call to retrieve its content. This is not a semantic-search endpoint.

{
  "brain": "support", "query": "refund window", "count": 2,
  "index_hits": [ { "line": 14, "text": "- memory/refunds.md: refund window, exceptions, who approves" } ],
  "results": [
    { "doc_id": "01JA…", "path": "memory/refunds.md", "score": -3.2, "snippet": "…the refund window is 30 days…", "snippet_html": "…",
      "title": "Refunds", "retriever": "fts", "where": "brain", "from_brain": "support", "type": "memory",
      "read_with": "brain_read{brain: 'support', path: 'memory/refunds.md'}" },
    { "doc_id": "01J9…", "path": "source.md", "score": -2.1, "snippet": "…", "snippet_html": "…",
      "title": "Refund policy", "retriever": "fts", "where": "source", "alias": "refund-policy", "kind": "markdown",
      "folder": "Support/Policies", "url": "https://…/d/…/", "read_with": "get_file{doc_id: '01J9…', path: 'source.md'}" }
  ]
}

Lower score is better. retriever is fts or grep. Searches are recorded in the journal. Prompt: "What does the support agent say about refunds?"

brain_activity

Local preview: factual event totals by UTC month and day, newest period first. Requires read access to the Agent. Counts cover the complete matching period, independently of how many event details a client has loaded.

Parameter Type Default Notes
brain string required Agent slug or personal
kinds string list all Same event kinds as brain_log
before_month string none Exclusive month cursor, YYYY-MM
limit integer 6 Number of months, at most 12

Returns {brain, timezone: "UTC", months, next_before_month}. Each month is {month, count, counts, days}; each day is {day, count, counts}. counts maps event kinds to their counts. Empty periods are omitted. Pass next_before_month as before_month to load older periods; null means no more matching history. Keep the same kinds filter while paging.

For example, “What changed in the Delivery Agent this month?” can start with brain_activity using kinds: ["write", "propose", "decide"], then read the relevant events through brain_log. Totals describe recorded events; they are not generated session summaries or a count of completed tasks.

brain_log

The journal, newest first: an agent's short-term memory.

Parameter Type Default Notes
brain string required
since string inclusive ISO 8601 time or date
until string exclusive ISO 8601 time or date
before_id integer pagination cursor from next_before_id
kinds string list all from note, load, read, write, search, propose, decide, consolidate
limit integer 50 at most 500
unconsolidated boolean false with kinds: ["note"], the notes still waiting to be folded into memory

Returns {brain, count, events, consolidation}. An event is {id, ts, kind, actor, topic, text, path, doc_id, diff, meta, consolidated, consolidated_at, memory_path}. A write carries a small unified diff, the commit message as text, and meta with version, added and removed. Prompt: "What did we change in the support agent yesterday?"

The response also includes next_before_id: pass it as before_id with the same time/kind filters to read the next page. null means no more matching events. To expand one UTC day, use since: "2026-09-29T00:00:00Z" and until: "2026-09-30T00:00:00Z". A page's count is the number of events returned; use brain_activity for the entire period's count.

brain_consolidate_mark

Finish a consolidation after the notes were folded into a memory file with brain_write.

Parameter Type Required Notes
brain string yes
event_ids integer list yes note ids from brain_log or load_brain
memory_path string yes the file under memory/ that now holds their content; it must exist

Returns {marked, skipped, memory_path, consolidation}. Marked notes stop counting against the journal budget and drop out of recent_notes; nothing is deleted. Needs write access.

brain_export

Parameter Type Required
brain string yes

Returns {brain, filename, files, chars, markdown}: the instructions, index, skills, workflows (included agents' files marked with their origin) and memory as one markdown document, for harnesses without MCP. A snapshot; edits to it do not flow back. The journal and the source documents are not part of it.

propose_change

File a proposal for a protected file, including a new skill or workflow. brain_write on a protected path does the same thing.

Parameter Type Required Notes
brain string yes read access is enough
path string yes BRAIN.md, skills/<name>.md or workflows/<name>.md
content string yes the complete new text of the file
message string no why; up to 512 characters
base_version integer no as for brain_write

Returns the proposal:

{
  "id": 18, "brain": "support", "brain_name": "Support", "path": "BRAIN.md", "type": "instructions",
  "new_file": false, "status": "pending", "message": "Tone rule contradicted the brand skill.",
  "actor": "ada@acme.com", "created_by": "ada@acme.com", "created_at": "…",
  "base_version": 4, "current_version": 4, "added": 3, "removed": 2, "hunk_count": 1,
  "decided_by": null, "decided_at": null, "decision_note": null, "accepted_hunks": null, "result_version": null,
  "link": "/app/agents/support?tab=review&proposal=18"
}

Everyone with write access to the agent is notified in the app. A proposal identical to the current file is refused, and a free-write path is refused with a pointer to brain_write. Prompt: "The tone rule in the support instructions is wrong, propose a fix."

list_proposals

Parameter Type Default Notes
brain string required
status string pending pending, approved, partial, rejected, stale or all

Returns {brain, status, count, proposals: [proposal], can_decide}. partial means some hunks were accepted; stale means the file changed and the proposal no longer applies, so read the file and propose again. can_decide reflects the user's write access; the decision itself is made by a person in the app.

create_brain

Workspace owners and admins only.

Parameter Type Required Notes
name string yes up to 120 characters
description string no up to 512 characters
slug string no defaults to the name; personal, me, my, ~ and anything starting with personal- are reserved; shares one namespace with shared roots

Creates the agent with a starter BRAIN.md and INDEX.md and returns the agent object. 409 when the slug exists. The personal agent needs no creation. Prompt: "Create an agent for the Q3 launch."

add_brain_source

Parameter Type Required Notes
brain string yes write access needed
kind root, folder, tag, doc yes
target string yes root: a shared root's slug, or ~ for personal documents. folder: <root slug or ~>:<folder path>, e.g. general:Support or ~:customers/acme. tag: the tag. doc: a document id or alias
note string no a line for agents on what the source is for, up to 255 characters

Returns {id, kind, target, label, note, exists}. Adding the same source twice returns the existing one. Agent files cannot be sources. Prompt: "Let the support agent read the Support folder in General."

Connected Agent tools

The Agent sessions and hosted work reference covers generic agent-assisted setup, hosted runs, private/shared sessions and knowledge proposals.

The relay accepts compatible remote MCP servers; it is not specific to Linear or project management. An Agent owner or workspace admin can set up connection metadata through the app or MCP; people sign in or add credentials securely in the app. Paste a Claude/Codex setup command or configuration, or use manual setup. Agents can prepare credential-free instructions with prepare_brain_connection_from_setup. For API tokens, prepare_brain_connection_credentials returns a short-lived, single-use curl receiver. Send the values locally through stdin or a JSON file; the agent only needs the connection and key names. See the secure connection setup guide. Supported transports are Streamable HTTP and SSE; authentication can use OAuth sign-in, bearer/API tokens, custom headers or no credential. Local command-based servers and private-network gateways are not hosted by this connection flow. See setup and reconnect behavior.

The Agent's access list controls use of its attached tools. The connection's account label tells you which linked account acts upstream; stored credentials are never returned. Workspace policy and the selected tool rules still apply to every action.

list_brain_connections

Parameter Type Default
brain string "personal"

Returns {connections, can_manage, policy, ai_configured, capabilities}. Each connection includes its id, name, account_label, enabled state, current revision, discovered tools and tool_rules. Tool entries include their name, description and input schema. has_credentials indicates a stored credential; it does not expose its value. can_manage describes the caller's management permission. Agents can configure/discover connections using the setup tools; credential entry and tool-policy changes remain signed-in human controls.

Load the Agent first, select an enabled connection, and use the discovered schema to form the request. Tool names and argument fields depend on the connected server.

request_brain_tool_call

Parameter Type Required Meaning
brain string yes Agent slug
connection_id string yes ID from list_brain_connections
tool string yes Exact discovered tool name
arguments object yes Values matching that tool's input schema
intent string yes What the user wants to accomplish and why this action is needed
request_key string yes A unique UUID for this intended action; reuse only to recover that same request

Returns a call record with id, brain, connection_id, tool, status, decision, evidence, reviewed, created_at, expires_at and audit_url. An executed call can include result. Evidence contains the reviewable intent and policy assessment; it is not a transcript of hidden model reasoning.

Decision Client behavior
allow The request may execute immediately; inspect status and any result
review Execution is allowed with a visible review flag; show that flag to the user
approval_required Wait for an authorized human decision; no upstream action has run yet
deny Explain the restriction; do not retry with a different intent to bypass it

An intended use does not replace permission. A request such as “compare the brief with this project” does not authorize creating issues unless the user also requested that action. If the response is interrupted, reuse the same request key and exact request to find its record. Changing the action requires a new request key.

get_brain_tool_call

Takes call_id (required string). Returns the caller's request, its current decision, review status and audit link; a completed result is available to its original requester. An authorized reviewer can inspect review information without receiving another person's result.

Use this to resume after a pending approval. pending means waiting for review; approved means ready for the original requester to execute. completed and failed describe a recorded outcome. unknown means an interrupted call may have affected the provider: reconcile with the provider before requesting another action.

execute_brain_tool_call

Takes call_id (required string). The original requester executes the approved, unexpired action. Approval itself does not dispatch it. Fermium checks current access and whether the approved request is still valid; changed permissions, connection settings, instructions or policy can require a new request.

Admins and Auditors make decisions in the app. Workspace policy can require an independent approver, including when the requester is an admin or Auditor. Agents cannot approve their own actions through these tools.

Example: “Load the Delivery Agent. Compare the client brief with the connected project and draft the missing issues. Request their creation, then wait for the required approval.” The agent uses the discovered tools and reports the proposed work, sources, approval state and eventual result. The same individual tools remain available alongside optional hosted work.

Earlier agents

These tools work on agents of the earlier form: a curated member tree with a pinned instructions document and attached skill documents, which also drives the earlier form of the wiki. They keep working next to agents as agent memory. Three of them were renamed when the names list_brains, brain_search and create_brain moved to the new agents: they are now list_legacy_brains, legacy_brain_search and create_legacy_brain. Reading is open to every member; create_legacy_brain, set_brain_instructions, attach_skill and detach_skill need the workspace owner or admin role. Every payload includes only documents the user may open; skipped counts the rest. Agent limits by plan: Free 1, Team 5, Business and above unlimited.

The agent object:

{
  "id": 2, "slug": "support", "name": "Support", "icon": "lucide:life-buoy", "version": 9,
  "description": "How we answer customers: policies, macros, escalation.",
  "is_default": false, "wiki_default": true,
  "instructions": { "doc_id": "01J7…", "title": "Support brain instructions", "alias": "support-brain", "url": "https://fermiumusercontent.app/d/…/", "folder": "Brains/support", "updated_at": "2026-09-16T09:12:40+00:00" },
  "skills": [ { "doc_id": "01J8…", "title": "Reply style", "alias": "reply-style", "url": "https://…/d/…/", "folder": "skills", "updated_at": "…", "position": 0 } ],
  "all_workflows": true,
  "workflows": [ { "id": 3, "slug": "all-hands-deck", "name": "All-hands deck" } ],
  "member_count": 14, "entry_count": 17,
  "created_at": "…", "updated_at": "…"
}

is_default marks the workspace's default agent; wiki_default says whether the wiki publishes this agent. instructions is null for an agent with no instructions document. all_workflows says whether the agent hands out every workflow in the workspace (the default) or only the picked set; workflows is the list it hands out either way. See Workflows.

list_legacy_brains

No parameters. Returns {brains: [brain], default, can_manage, limit}. default is the default agent's slug; limit is null when unlimited. The default agent is created on first use if the workspace has none. Prompt: "Which agents do we have?"

use_brain

Load an agent at the start of a task in its domain: the instructions, the skills, the workflows and an index of the members, in one payload.

Parameter Type Default Notes
brain string the default agent slug or id
include_index boolean true false when only the rules are needed
max_chars integer 60000 character budget for the payload; at least 2000
{
  "brain": { "slug": "support", "…": "…" },
  "instructions_markdown": "# Support brain\n\n…",
  "skills": [ { "doc_id": "01J8…", "title": "Reply style", "alias": "reply-style", "url": "https://…/d/…/", "markdown": "# Reply style\n\n…" } ],
  "workflows": [
    { "id": 3, "slug": "all-hands-deck", "name": "All-hands deck", "description": "The monthly all-hands slide deck.",
      "triggers": ["all hands deck", "monthly all-hands"],
      "inputs": [ { "name": "month", "description": "The month the deck covers", "required": true, "example": "September 2026" } ] }
  ],
  "index": [
    { "kind": "group", "label": "Getting started", "children": [
      { "kind": "doc", "label": "Refund policy", "doc_id": "01J9…", "alias": "refund-policy", "doc_kind": "markdown", "folder": "Policies", "tags": ["customer-facing"], "updated_at": "…", "url": "https://…/d/…/" }
    ]},
    { "kind": "folder", "label": "Runbooks", "folder": "Runbooks", "children": [ { "kind": "doc", "…": "…" } ] },
    { "kind": "tag", "label": "#macros", "tag": "macros", "children": [ { "kind": "doc", "…": "…" } ] },
    { "kind": "url", "label": "Status page", "url": "https://status.acme.com" }
  ],
  "skipped": { "instructions": false, "skills": 0, "docs": 1 },
  "truncated": [],
  "search_hint": "brain_search{brain: \"support\", q: \"...\"} searches only this brain's documents …"
}

Skills come in their attached order. workflows lists the enabled workflows the agent hands out (every workflow in the workspace, or the picked set in order), each as {id, slug, name, description, triggers, inputs}: enough to recognise a job; the full recipe comes from use_workflow. Folder and tag members are expanded to the live documents under them; dividers and empty sections are dropped. When the payload exceeds max_chars the index is flattened to a list of documents and then shortened, then the last skills are cut or dropped, then the instructions are cut; truncated names each step taken. The workflow list is never trimmed. Agents are instructed to read instructions_markdown first, follow every skill, use index as the map of what exists, read a document with get_file(doc_id, "source.md"), and call use_workflow before doing a job that matches a workflow's triggers. Prompt: "Use the support agent, then answer…"

Ranked full-text search inside one agent of the earlier form: its curated documents, the documents under its folder and tag members, its instructions and its skills, restricted to what the user may open.

Parameter Type Default
q string required
brain string the default agent
limit integer 10

Returns {brain, query, count, results: [{doc_id, path, title, alias, kind, folder, url, snippet, …}]}, the fields of a ranked search result plus the document's alias, kind and folder. Agents are told to prefer it over search_docs when the question belongs to an agent's domain, then read the hit with get_file or grep_doc. Prompt: "What does the support agent say about refunds?"

create_legacy_brain

Parameter Type Required Notes
name string yes up to 120 characters
description string no one line, up to 512 characters, shown in listings
slug string no defaults to the name; letters, digits and dashes
icon string no icon grammar

Creates the agent with a starter BRAIN.md already set as its instructions: a team-listed markdown document kept forever, in the folder Brains/<slug> with the alias <slug>-brain. Returns the agent object. Fails past the plan limit with a message naming the plan, and with a 409 when the slug exists. Prompt: "Create an agent called Sales enablement for the pricing and objection docs."

set_brain_instructions

Parameter Type Required Notes
brain string yes slug or id
doc_id string yes a markdown document's id or alias

Points the agent at another instructions document; the previous BRAIN.md is unlinked, not deleted. A document that is not markdown is refused. An instructions document cannot be deleted while an agent uses it; delete_doc fails with a message naming the agent. Returns the agent object.

attach_skill

Parameter Type Required Notes
brain string yes slug or id
doc_id string yes a markdown document's id or alias
position integer no index among the skills; appended when omitted. Earlier skills win on conflict

Returns {brain, skill: {doc_id, title, alias, url, folder, updated_at, position}, skills: [...]}. Attaching a document that is already a skill returns it unchanged, or moves it when position is given. An agent holds at most 50 skills. Agents attach only what the user asked for. Prompt: "Attach the brand guidelines as a skill of the marketing agent."

detach_skill

Parameter Type Required
brain string yes
doc_id string yes

Returns {brain, doc_id, detached: true, skills: [...]}. The document itself is untouched.

Workflows

Background: Workflows. These tools work on workflows of the earlier form, defined on the Workflows page and handed out through use_brain. A workflow in an agent that agents load is a file, workflows/<name>.md: it is listed by load_brain and read with brain_read, and needs none of the tools below except duplicate_doc. A workflow of the earlier form is a recipe for one recurring job: a WORKFLOW.md steps document plus trigger phrases, inputs to ask for, templates to duplicate, skills to apply on top of the agent's, and output rules. Reading is open to every member; create_workflow, set_workflow_templates and set_workflow_skills need the workspace owner or admin role. Every recipe includes only documents the user may open; skipped counts the rest. Workflow limits by plan: Free 3, Team 25, Business and above unlimited.

The workflow object:

{
  "id": 3, "slug": "all-hands-deck", "name": "All-hands deck", "icon": "lucide:presentation",
  "description": "The monthly all-hands slide deck.",
  "doc": { "doc_id": "01JA…", "title": "All-hands deck workflow", "url": "https://fermiumusercontent.app/d/…/", "alias": "all-hands-deck-workflow" },
  "triggers": ["all hands deck", "monthly all-hands"],
  "inputs": [
    { "name": "month", "description": "The month the deck covers", "required": true, "example": "September 2026" },
    { "name": "wins", "description": "Team wins to call out", "required": false, "example": null }
  ],
  "output": { "kind": "html", "folder": "Leadership/All hands", "title_pattern": "All hands {month} {year}", "tags": ["all-hands"], "brain": "leadership", "listing": "team", "visibility": "workspace" },
  "enabled": true,
  "templates": [ { "doc_id": "01JB…", "title": "All-hands deck template", "url": "https://…/d/…/", "alias": "all-hands-template", "role": "deck", "kind": "html" } ],
  "skills": [ { "doc_id": "01JC…", "title": "Deck rules", "url": "https://…/d/…/", "alias": "deck-rules" } ],
  "use_count": 12,
  "created_at": "…", "updated_at": "…"
}

doc is the steps document, created with the workflow in the folder Workflows/<slug> with the alias <slug>-workflow, team-listed and kept forever; it is edited like any markdown document and cannot be deleted while the workflow exists. output is null when the workflow leaves placement to the agent; its keys are kind (html, markdown, snippet or file), folder, title_pattern (text the agent follows; the app suggests {<input name>}, {date}, {month} and {year} placeholders), tags, brain (a slug), listing (private or team) and visibility (unlisted, password or workspace). enabled: false removes the workflow from what use_brain hands out; it still lists here.

list_workflows

Parameter Type Required Notes
brain string no slug or id: only the workflows that agent hands out, in its order

Returns {workflows: [workflow], can_manage, limit}; limit is null when unlimited. With brain, adds brain (the slug) and all_workflows. Disabled workflows are included with enabled: false. use_brain already lists an agent's workflows, so agents rarely need this on its own. Prompt: "Which workflows do we have?"

use_workflow

Load a workflow's recipe before doing a recurring job. Agents are told to call it when a request matches a workflow's triggers, or when the user names the workflow, and to call it once per job.

Parameter Type Default Notes
workflow string required slug or id
brain string none the agent the agent loaded; brings its skills and instructions
include_templates boolean true false to leave the templates out
max_chars integer 60000 character budget for the payload; at least 2000
{
  "workflow": { "slug": "all-hands-deck", "…": "…" },
  "brain": "leadership",
  "steps_markdown": "# All-hands deck workflow\n\n## Purpose\n\n…",
  "inputs": [ { "name": "month", "description": "The month the deck covers", "required": true, "example": "September 2026" } ],
  "output": { "kind": "html", "folder": "Leadership/All hands", "title_pattern": "All hands {month} {year}", "tags": ["all-hands"], "brain": "leadership", "listing": "team", "visibility": "workspace" },
  "templates": [
    { "doc_id": "01JB…", "title": "All-hands deck template", "kind": "html", "role": "deck", "url": "https://…/d/…/", "alias": "all-hands-template", "markdown": null, "files": ["index.html", "assets/logo.svg"] },
    { "doc_id": "01JD…", "title": "Speaker notes outline", "kind": "markdown", "role": "outline", "url": "https://…/d/…/", "alias": null, "markdown": "# Speaker notes\n\n…", "files": ["index.html", "source.md"] }
  ],
  "skills": [
    { "doc_id": "01J8…", "title": "Chart formatting", "alias": "chart-formatting", "url": "https://…/d/…/", "source": "brain", "markdown": "# Chart formatting\n\n…" },
    { "doc_id": "01JC…", "title": "Deck rules", "alias": "deck-rules", "url": "https://…/d/…/", "source": "workflow", "markdown": "# Deck rules\n\n…" }
  ],
  "instructions_markdown": "# Leadership brain\n\n…",
  "skipped": { "steps": false, "instructions": false, "templates": 0, "skills": 0 },
  "truncated": [],
  "search_hint": "Duplicate a template with duplicate_doc{doc_id, title, folder} and fill the copy; publish to the output rules …"
}

steps_markdown is the WORKFLOW.md source. A markdown or snippet template carries its source in markdown; other kinds carry null and are copied by id. skills come with the agent's first, then the workflow's own, deduplicated, each marked with its source. instructions_markdown is the agent's BRAIN.md when brain is given, else null. When the payload exceeds max_chars the templates' text is cut from the last one back, then the skills, then the instructions, then the steps; truncated names each cut. Every call counts one use of the workflow and records workflow.used in the audit log. Agents are instructed to read the steps first, ask for every required input before starting, duplicate templates with duplicate_doc rather than starting from scratch or editing the template, keep the skills in front of them, and publish to output (move_doc, the title on the update tools, set_tags, set_listing, set_visibility; brain only when the user asked). Prompt: "Put together the all hands deck for October."

create_workflow

Parameter Type Required Notes
name string yes up to 120 characters
description string no one line, up to 512 characters
triggers list of strings no up to 20 phrases of up to 120 characters; a comma-separated string is accepted
inputs list no up to 30 of {name, description?, required?, example?}; required defaults to true; a bare string is a required input with that name
output object no {kind?, folder?, title_pattern?, tags?, brain?, listing?, visibility?}; unknown keys are dropped, bad values refused
slug string no defaults to the name; letters, digits and dashes
icon string no icon grammar

Creates the workflow with its starter WORKFLOW.md (sections Purpose, Inputs, Steps, Output, Checks) and returns the workflow object. Write the steps with update_markdown or edit_file on doc.doc_id, then attach templates and skills with the two tools below. Fails past the plan limit with a message naming the plan, and when the slug is already in use. Agents create only what the user asked for. Prompt: "Define an all-hands deck workflow that triggers on 'all hands deck', asks for the month and the three headline metrics, and publishes to Leadership/All hands."

set_workflow_templates

Parameter Type Required Notes
workflow string yes slug or id
templates list yes [{doc_id, role?}] in order; a bare string is a document id or alias with no role; [] clears

Replaces the template list. Any document kind; role (up to 64 characters) says what the document is for inside the job: deck, outline, email. At most 20. Returns {workflow, templates: [{doc_id, title, url, alias, role, kind}]}. Prompt: "Make the Q3 deck the template of the all-hands workflow, role deck."

set_workflow_skills

Parameter Type Required Notes
workflow string yes slug or id
doc_ids list of strings yes markdown document ids or aliases, in order; [] clears

Replaces the workflow's own skills, applied after the agent's when the workflow runs. Markdown documents only; at most 20. Returns {workflow, skills: [{doc_id, title, url, alias}]}. Prompt: "Add the deck rules as a skill of the all-hands workflow."

duplicate_doc

Copy a document the user may open into a new document they own. This is how an agent starts from a template.

Parameter Type Required Notes
doc_id string yes id or alias of the source
title string yes the copy's title
folder string no defaults to the source's folder
tags list of strings no defaults to none; the source's tags are not copied
brain string no place the copy in this agent's first group; only when the user asked

The copy has the same kind and files as the source's current version (a markdown template keeps its source and is re-rendered with the new title; an HTML deck keeps its pages and assets), the source's retention, no alias, restricted visibility and private listing. Returns the document object plus source_doc_id, and nav with the placement when brain was given. Counts against the publish quota and records doc.duplicated in the audit log. Agents then edit the copy with update_markdown, update_html or edit_file, never the template. Prompt: "Start from the deck template and call it All hands October 2026."

Agent members and the wiki

Background: Wiki. These tools edit the member tree of an agent of the earlier form, which is the left sidebar of the earlier agent wiki. To build a wiki today, publish a folder with set_folder_meta instead; the tool and payload names keep the older word navigation for the tree. Reading is open to every member; every tool that writes needs the workspace owner or admin role. Agent limits by plan: Free 1, Team 5, Business and above unlimited.

list_navigations

No parameters. Returns {navigations: [{id, slug, name, icon, version, entry_count, is_default, created_at, updated_at}], can_manage, limit}. limit is null when unlimited.

get_navigation

Parameter Type Required Notes
nav string no slug or id; omit for the agent the wiki publishes, else the workspace's default agent

Returns the tree as the editor sees it: every entry with id, kind (group, doc, folder, tag, url, divider), label, target, icon, icon_url, visibility, group, collapsed, show_sign_in_hint, sort, position, children, and broken: true when a document target no longer resolves. Document nodes add doc_id, title, alias, ref, url, doc_kind, doc_visibility, folder, tags, updated_at and label_is_custom. Folder and tag entries come expanded with their live documents under children with dynamic: true. The top level carries version, which set_navigation expects back, and orphans: documents inside a folder or tag a dynamic entry covers that have no curated entry of their own, each with doc_id, title, alias, ref, folder, tags, kind, icon, url, updated_at and via.

create_navigation

Parameter Type Required Notes
name string yes up to 120 characters
slug string no defaults to the name; letters, digits and dashes; appears in /w/~<slug>/
icon string no icon grammar

Creates an agent of the earlier form with an empty tree and no instructions document; create_legacy_brain does the same and adds the starter BRAIN.md. Returns the navigation object. Fails past the plan limit with a message naming the plan, and with a 409 when the slug exists.

delete_navigation

Parameter Type Required
nav string yes

Deletes the agent: its member tree and skill links go, and its instructions document is unlinked, not deleted. The workspace's default agent is refused. Documents are untouched; a wiki that published it falls back to none. Returns {navigation, deleted: true}.

set_navigation

Replace the whole member tree in one call, the natural shape for an agent that maintains an agent's members from a spec.

Parameter Type Required Notes
nav string yes slug or id
tree list of entries yes
version integer no the version read with get_navigation

An entry is {kind?, target?, label?, icon?, visibility?, group?, collapsed?, show_sign_in_hint?, sort?, children?, id?}:

  • target uses the link grammar: doc:<id>, an alias, #tag (dynamic tag), folder:<path> (dynamic folder) or https://.... Kind is inferred from the target. An explicit document id that does not resolve is an error; an alias that does not resolve yet is allowed and renders broken until a document takes it.
  • An entry with a label and no target is a group and may carry children; only groups hold children. {kind: "divider"} draws a rule.
  • Groups and links need a label; leave it off document entries so renames follow.
  • visibility: public (default), signed_in, or group with group: "<slug>" (Business and above; visibility: "group:<slug>" works too).
  • sort on folder and tag entries: title or updated.
  • Entries that carry an id from get_navigation keep their row, so icons set on nav:<id> survive; everything else is recreated, and rows not mentioned are removed.

Limits: 4 levels deep, 500 entries, labels up to 160 characters, link URLs up to 600 characters. If version does not match, nothing is written and the response is {conflict: true, status: 409, error, current: <tree>}; merge and retry. Otherwise returns the new tree as get_navigation would.

[
  { "label": "Getting started", "icon": "lucide:rocket", "children": [
    { "target": "welcome" },
    { "target": "doc:01J7ZK3N9QW0X4B2C6D8E0F1G2", "label": "Install the CLI" }
  ]},
  { "target": "folder:Runbooks", "label": "Runbooks", "sort": "updated", "visibility": "signed_in" },
  { "target": "#customer-facing", "icon": "lucide:briefcase/green" },
  { "kind": "divider" },
  { "label": "Resources", "children": [
    { "target": "https://status.acme.com", "label": "Status page" }
  ]}
]

Prompt: "Rebuild the docs agent's members from this outline."

add_nav_entry

Parameter Type Required Notes
nav string no slug or id; omit for the default
target string no as in set_navigation; omit for a group or divider
parent string no an entry id or a group label such as Getting started; omit for the top level
label string no required for groups and links
icon string no
position integer no index among siblings; appended when omitted
visibility string no public, signed_in, group
group string no group slug with visibility: group
kind string no group or divider when there is no target; otherwise inferred
sort string no title or updated on folder and tag entries

Returns the entry {id, parent_id, position, kind, target, target_doc_id, label, icon, visibility, group, collapsed, show_sign_in_hint, sort} plus navigation and the new version. Prompt: "Add the on-call rotation under Runbooks in the wiki."

update_nav_entry

Parameter Type Required Notes
nav string yes
entry_id integer yes
label string no "" drops a custom label so the document title shows again
icon string no "" clears
target string no must stay the same kind; to change kind, remove and add
visibility, group string no
collapsed, show_sign_in_hint boolean no
sort string no "" resets

Only the fields passed change. Returns the entry with navigation and version.

move_nav_entry

Parameter Type Required Notes
nav string yes
entry_id integer yes moves with its children
parent string no entry id or group label; omit for the top level
position integer no index among the new siblings; end when omitted

An entry cannot be moved inside itself, and the result must stay within 4 levels.

remove_nav_entry

Parameter Type Required
nav string yes
entry_id integer yes

Removes the entry and any curated children under it. Documents are never touched. Returns {navigation, entry_id, deleted: true, version}.

publish_home

Turn the wiki on, or reconfigure it, and get its public URL. Read get_wiki_site first so a curator's settings are not overwritten. Fields left out keep their value; pass "" to clear one.

Parameter Type Required Default Notes
title string no the workspace name up to 120 characters
tagline string no up to 255 characters
navigation string no slug or id of the agent to publish
home_doc string no alias or document id; without one the root shows an overview of the tree
indexable boolean no only honoured with an active custom domain on a paid plan; the shared host is never indexed
domain string no hostname or id of an active custom domain of the workspace; a domain that is not active yet is refused
enabled boolean no true
footer_md string no markdown, up to 4,000 characters
accent string no a hex colour such as #2563eb

Returns {workspace, enabled, title, tagline, footer_md, navigation, navigation_id, home_doc: {doc_id, title, alias, url}, logo_doc_id, domain, custom_domain_id, indexable, accent, url, updated_at}. url is https://fermiumusercontent.app/w/@<workspace>/ or https://<domain>/w/. Enabling the site exposes nothing the published agent does not reach. Prompt: "Turn on the wiki as Acme Docs with the handbook as the home page."

get_wiki_site

No parameters. The same payload as publish_home plus can_manage.

Background: Links between documents.

set_alias

Give a document a stable human name, unique within the workspace, that [[alias]] and /x/alias links resolve to. Owner only.

Parameter Type Required Default
doc_id string yes
alias string yes lowercase slug, 1 to 64 characters; spaces and underscores become dashes
force boolean no false; move the alias from the document that holds it

Returns the document object plus link: "/x/<alias>". If another live document holds the alias and force is false, the error names the holder: alias 'q3-roadmap' already points at 'Q3 roadmap (old)' (01J7…); pass force=true to move it. Prompt: "Make q3-roadmap point at this document instead."

clear_alias

Parameter Type Required
doc_id string yes

Returns the document object. Existing [[alias]] links render visibly broken afterwards; prefer set_alias with force when a newer document takes over.

Check what a reference points at before writing it. ref is an alias, doc:<id>, a bare document id, or @<workspace-slug>/<alias>; [[ref]] and /x/ref spellings are accepted too. Bare aliases resolve in the user's workspace.

Parameter Type Required
ref string yes
{ "ref": "q3-roadmap/img/chart.png", "found": true, "doc_id": "01J7…", "title": "Q3 roadmap", "url": "https://fermiumusercontent.app/d/…/img/chart.png", "path": "img/chart.png", "visibility": "workspace", "alias": "q3-roadmap" }

Not found: {"ref": "...", "found": false, "detail": "no live document with that alias or id in this workspace; publish it first, or call set_alias on the doc that should own the name"}. Prompt: "Link to the roadmap from this page." The agent resolves first, then writes the link.

Outbound [[refs]] from a document the user owns, and every live document that links to it.

Parameter Type Required
doc_id string yes
{
  "doc_id": "01J7…", "alias": "postmortem-2026-09",
  "outbound": [{ "ref": "q3-roadmap", "path": null, "href": "/x/q3-roadmap", "resolved": true, "doc_id": "01J8…", "title": "Q3 roadmap", "alias": "q3-roadmap", "url": "https://…/d/…/" }],
  "inbound": [{ "doc_id": "01J9…", "title": "Weekly notes", "alias": null, "url": "https://…/d/…/", "ref": "postmortem-2026-09", "path": null }]
}

Prompt: "What links here? I want to re-point the alias."

Finding and reading

list_docs

Your documents, newest-updated first.

Parameter Type Default Notes
limit integer 50 capped at 500
offset integer 0
folder string only documents directly in this folder; "" is the root; omit for every folder
recursive boolean false with folder, include its subfolders
tag string only documents carrying this tag, in any folder
root string "general" or a shared root's slug lists that root instead: what the user may open there

folder and tag combine. Returns {"docs": [...]}. Prompt: "What is in Customers/Acme? Update the proposal instead of making a new one."

Parameter Type Required
doc_id string yes

Returns the document object. Prompt: "Give me the link to the roadmap again."

usage_guide

No parameters. Returns the full best-practices guide as markdown. Agents call this when unsure which tool fits.

Folders

A folder is where a document lives, exactly one path. Tags are separate: a topic that cuts across folders (see set_tags). Stars have no MCP tool; they are personal shortcuts set in the app. Background in Folders, tags, and stars.

A folder object looks like this:

{ "path": "Customers/Acme", "name": "Acme", "parent": "Customers", "doc_count": 3, "explicit": true }

doc_count counts documents directly in the folder, not in subfolders. explicit is true when the folder was created on its own (with create_folder, or left behind by a rename); folders implied by a document's path are listed too, with explicit: false.

move_doc

Move a document into a folder, and optionally into another root. The URL and every link to it stay the same; only its place in the tree changes. The destination is created if needed. Returns the document object with its new folder and root.

Parameter Type Required Notes
doc_id string yes
folder string yes "" for the top of the root
root string no omit to stay in the current root. "general" or a shared root's slug hands the document to that root; "~" takes it back into My documents

In My documents only the owner moves a document. Into a shared root: the owner, with edit on the target folder; the document then belongs to the root and stays when its author leaves. Inside or between shared roots: edit on the document and on the target folder. Back to My documents: the author, or a root manager, who then becomes its owner. Agent files cannot be moved with this tool.

Prompts: "Move the Q3 roadmap into Planning/2026." "Move my incident write-up into the Engineering root under Incidents."

list_folders

One optional parameter, root. Without it returns {"folders": [...]}: every folder you have in My documents, explicit or implied, with their ancestors, sorted by path case-insensitively. With a shared root's slug it returns {"root", "folders"} for that root: only folders the user can see, each also carrying index_doc_id, view_mode, published, my_level, root, shared and restricted (the folder has its own access list). Agents use it to pick where a new document belongs before publishing. Prompt: "Where do I keep customer proposals?"

create_folder

Create an empty folder. Publishing into a path creates it implicitly, so this is only needed for an empty folder that should exist now. Returns the folder object.

Parameter Type Required Notes
path string yes
root string no a shared root's slug to create it there; needs edit

rename_folder

Rename or move a folder and everything under it: documents, subfolders and stars. Document URLs do not change. Renaming to an existing path merges into it. Refused: renaming the root, an empty new_path, and moving a folder inside itself. Returns the folder object at new_path.

Parameter Type Required
path string yes
new_path string yes

Prompt: "Rename Clients to Customers."

delete_folder

Delete a folder. Without force it must be empty (no documents, no subfolders) and the call fails with folder is not empty; move its contents first or pass force=true. With force, its documents and subfolders move to the parent folder (the root for a top-level folder) and then the folder is removed. Documents are never deleted by this tool. The root cannot be deleted.

Parameter Type Required Default
path string yes
force boolean no false

Returns {"path": "...", "deleted": true}. Prompt: "Delete the Scratch folder but keep what is in it."

Roots and folder features

Background: Roots and folders. Every document lives in one root: the user's My documents ("~", the default everywhere), the workspace's General root ("general"), or a shared root addressed by its slug. A document in General or a shared root belongs to the root, not to its author.

The root parameter is accepted by publish_html, publish_markdown, publish_snippet, publish_file, move_doc, list_docs, list_folders, create_folder, list_tree, download_bundle, search_docs and list_changes. Omit it or pass "~" for My documents. An unknown root, or one the user cannot see, answers "root not found"; a root the user can see but not write to answers with the level they hold.

list_roots

No parameters. The roots this user can see: General always, other shared roots when the user holds any level inside them or manages the workspace.

{
  "personal": { "slug": "~", "name": "My documents" },
  "roots": [
    { "id": 1, "slug": "general", "name": "General", "kind": "shared",
      "description": "Shared by everyone in the workspace.", "icon": null,
      "is_general": true, "my_level": "edit", "doc_count": 42,
      "can_manage": false, "published": false, "agent_write": false,
      "created_at": "2026-09-17T08:00:00+00:00", "updated_at": "2026-09-17T08:00:00+00:00" },
    { "id": 4, "slug": "engineering", "name": "Engineering", "kind": "shared",
      "description": "Designs, runbooks and incident reviews.", "icon": "lucide:wrench/blue",
      "is_general": false, "my_level": "view", "doc_count": 17,
      "can_manage": false, "published": true, "agent_write": true, "created_at": "…", "updated_at": "…" }
  ]
}

my_level is view, comment or edit at the top of the root (null when the user only reaches a folder deeper inside); writing needs edit. doc_count counts the documents the user may open. published says whether the top of the root is published as a site. agent_write says whether agents may publish or move documents into the root; when it is false, publishing there is refused with a 403 whose message says to publish into My documents instead, and an owner or admin turns it on in the root's settings. Prompt: "Which shared roots can I publish to?"

create_root

Workspace owners and admins only. Agents create a root only when the user asked for a new shared space.

Parameter Type Required Notes
name string yes up to 120 characters
slug string no defaults to the name; lowercase letters, digits and dashes, up to 64 characters. ~, personal, me, brain, brains, shared and r are reserved
description string no up to 512 characters
icon string no icon grammar

Returns the root row shown under list_roots. The new root starts restricted to managers. Add its audience using share_root, Root settings → Access, or POST /api/roots/{root}/access. An empty list never opens the space to the workspace. Fails with 409 when the slug exists and with 400 past 200 shared roots. Prompt: "Create a shared root called Customer success."

Sharing and organizing shared spaces

Tool Parameters Purpose
list_root_access root, optional folder Inspect direct grants, inherited grants and whether the folder overrides its parent
share_root root, subject, optional level="view", folder="" Managers grant user:<email>, group:<slug> or workspace access
unshare_root root, grant_id, optional folder="" Remove an exact grant. Removing the last entry keeps the space/folder restricted
inherit_folder_access root, folder Deliberately discard this folder's audience and restore its parent's permissions
configure_root root, agent_write Managers explicitly enable or disable agent publishing and moves

An omitted folder when publishing or moving into a shared root uses a stable Members/<member name> destination. An explicit project path wins; an explicit empty string targets the root itself. Member folders inherit access from the space; their names do not make them private. Renaming one preserves the member's default destination.

Prompt: “Create an Engineering space for the Developers group. Give them edit access and enable agent publishing. Put unfiled contributions in each member's default folder; use Projects/Launch when I name that project.”

get_folder_view

One folder the way the app's navigator shows it.

Parameter Type Default Notes
root string My documents a shared root's slug, or "~"
path string "" "" is the top of the root
{
  "root": { "id": 1, "slug": "general", "name": "General", "kind": "shared", "is_general": true },
  "path": "Handbook",
  "crumbs": [ { "name": "General", "path": "" }, { "name": "Handbook", "path": "Handbook" } ],
  "meta": { "icon": "lucide:book-open", "description": "How we work.", "index_doc_id": "01J7…",
            "view_mode": "page", "child_order": ["d:01J7…", "f:Tools", "s:12"],
            "published": true, "publish_visibility": "signed_in", "publish_group_id": null },
  "index": { "doc_id": "01J7…", "title": "Welcome", "kind": "markdown", "url": "https://…/d/…/",
             "html": "<article class=\"doc md\">…</article>", "toc": [ { "id": "welcome", "text": "Welcome", "level": 1 } ] },
  "folders": [ { "name": "Tools", "path": "Handbook/Tools", "icon": null, "description": null, "doc_count": 6,
                 "shared": false, "has_index": true, "view_mode": null, "published": false, "my_level": "edit" } ],
  "docs": [ { "doc_id": "01J7…", "title": "Welcome", "is_index": true, "mine": false, "owner_email": "ada@acme.com", "…": "…" } ],
  "shortcuts": [ { "id": 12, "label": "Pricing FAQ", "folder": "Handbook", "target": { "doc_id": "01J9…", "…": "…" } } ],
  "order": ["d:01J7…", "f:Tools", "s:12"],
  "my_level": "edit",
  "can_manage": false
}

For My documents root is {id: null, slug: "~", name: "My documents", kind: "personal"}. index is null when the folder has no index page; html is the rendered page for markdown and snippets, and null for an HTML page or a file share, which the app shows in a sandboxed frame from url. A folder's doc_count counts the documents under it that the user may open, and shared is true when the folder carries its own access list (in My documents: a folder grant). docs are document objects plus is_index, mine and owner_email. Shortcuts whose target is gone, or that the user may not open, are left out. order is the merged order of children as keys f:<folder name>, d:<doc_id>, s:<shortcut_id>. Use list_tree for several levels at once. Prompt: "What is in the Handbook folder of General?"

set_folder_meta

Folder settings. Only the arguments passed change; name keys in clear to reset them. Needs edit on the folder; the three publish keys need a workspace owner or admin (or the root's creator) in a shared root, and are always allowed in the user's own My documents.

Parameter Type Notes
path string "" is the top of the root
root string a shared root's slug; omit for My documents
icon string icon grammar
description string up to 512 characters
index_doc_id string the folder's landing page: a document of the same root (in My documents, one of the user's own)
view_mode grid, list, page the view the folder opens in
child_order string list keys f:<folder name>, d:<doc_id>, s:<shortcut_id>, at most 2,000; anything not listed follows
published boolean publish the folder and everything under it as a site
publish_visibility public, signed_in, group who can read the site; inherited by subfolders unless they are published with their own setting
publish_group_id integer required with group; ids come from list_groups
clear string list keys to reset, e.g. ["index_doc_id"]; "published" unpublishes

Returns the folder's settings: the meta fields above plus root, path, my_level and can_publish. A published folder is served at /w/@<workspace>/r/<root-slug>/<path>/ on the shared content host and at /w/r/<root-slug>/<path>/ on a custom domain; a tree in My documents uses ~<user id> as the root segment. Agents publish a folder only when the user asked: it can make documents publicly reachable. Prompts: "Make the welcome page the index of Handbook." "Publish the Handbook folder for signed-in readers."

create_shortcut

Show a document in a second folder without copying it.

Parameter Type Required Notes
target_doc_id string yes any document of the same workspace that the user may open
folder string no "" is the top of the root
root string no a shared root's slug; omit for My documents
label string no up to 160 characters; defaults to the document's title

Returns {id, label, folder, root, target} where target is the document object. The key s:<id> places it in child_order. Needs edit on the folder. People who cannot open the target do not see the shortcut. Prompt: "Show the pricing FAQ in the Sales folder too."

Settings

Each returns the updated document object. Visibility, retention, listing and alias are owner-only; tags may also be changed by an editor.

set_visibility

Parameter Type Required
doc_id string yes
visibility restricted, unlisted, password, workspace yes
password string when password

A workspace policy may restrict the allowed values; the error lists them. Prompt: "Password-protect it with harbor-42." or "Lock this to the company."

set_tags

Replaces the whole tag set; [] clears. Read the current tags from any response before adding one. Tags are topics that cut across folders; a tag view lists every document carrying the tag wherever it lives. Use move_doc to change where a document lives.

Parameter Type Required
doc_id string yes
tags string list yes

set_retention

Parameter Type Required
doc_id string yes
retention_days integer >= 0 yes

A workspace policy may set a minimum or maximum; out-of-range values are refused with the bound. Prompt: "Keep this one forever."

set_listing

Parameter Type Required
doc_id string yes
listing private, team yes

Prompt: "List this for the whole team."

Sharing and comments

Background and the permission matrix: Members and roles. Individual and whole-workspace grants are available on every plan. Groups need Business or above; comments need any paid plan.

get_identity

No parameters. Returns {email, role, workspace: {id, name, slug}} for the connected account. Call this before working with files when the user has multiple accounts. It cannot switch accounts.

Access requests

Tool Parameters Purpose
request_access slug, optional message (up to 1,000 characters) Request view access to a known restricted link; existing workspace members only
access_request_status slug Read this account's status and whether another request is allowed, without private document metadata
list_access_requests doc_id Sharing managers list up to 100 requests, with pending requests first
decide_access_request request_id, decision: "approve" | "deny" Sharing managers decide one request; approval grants this document's view permission only

Requesting access sends notifications. Call request_access and decide_access_request only with the user's authorization. A repeated pending request reuses the existing request; decisions are single-use. Poll at most once every five seconds while pending, stop on a decision, and back off after errors. Requests expire after 30 days; denial has a 24-hour retry cooldown. An approved request does not restore a subsequently revoked grant.

Group and personal-folder tools

Tool Parameters Purpose
create_group name Workspace managers create a group (Business+)
set_group_member group, email, optional present=true Managers add or remove an existing member; no invitation is sent
share_folder folder, subject, optional level="view" Share a folder in My documents, including descendants
list_folder_access folder Inspect direct grants on your personal folder
unshare_folder folder, grant_id Remove an exact personal-folder grant

Use share_root with folder for folders in shared spaces. All tools run as the connected account. An email mentioned in a prompt never switches the authenticated account.

list_access

Who can do what on a document the user can see, and what the user may do with it.

Parameter Type Required
doc_id string yes
{
  "doc_id": "01J7…",
  "owner": { "id": 3, "email": "ada@acme.com" },
  "my_level": "edit",
  "can_manage": true,
  "narrowed": true,
  "grants": [
    { "id": 12, "doc_id": "01J7…", "folder": null, "folder_owner_id": null, "subject_type": "group", "subject_id": 2, "subject": { "id": 2, "slug": "design", "name": "Design" }, "subject_label": "group:design", "level": "comment", "created_by": "ada@acme.com", "created_at": "2026-09-15T10:00:00+00:00", "inherited": false },
    { "id": 9, "doc_id": null, "folder": "Customers/Acme", "folder_owner_id": 3, "subject_type": "user", "subject_id": 7, "subject": { "id": 7, "email": "bo@acme.com" }, "subject_label": "user:bo@acme.com", "level": "edit", "created_by": "ada@acme.com", "created_at": "2026-09-14T09:12:00+00:00", "inherited": true }
  ]
}

my_level is null, view, comment or edit. Inherited grants come from the document's folder and can only be removed from the folder. Prompt: "Can I edit this, or should I ask Ada?"

share_doc

Grant a subject a level on a document. Document owner or a workspace admin.

Parameter Type Required Default
doc_id string yes
subject string yes user:<email>, group:<slug>, or workspace
level view, comment, edit no view

Returns the grant object. Sharing an existing subject again changes its level. Grants authorize restricted links, app access and MCP access. They do not close an existing public/password link; use set_visibility with restricted for that. Prompt: "Share the proposal with the design group so they can comment."

unshare_doc

Parameter Type Required
doc_id string yes
grant_id integer yes

Returns {doc_id, grant_id, removed: true}. Folder grants cannot be removed here.

list_groups

No parameters. Returns {groups: [{id, name, slug, source, member_count, members: [{id, email, role}], created_at}]}. source is manual or scim. Any member may read; creating and editing groups is done in the REST API by owners and admins.

list_comments

Parameter Type Required Default
doc_id string yes
include_resolved boolean no false
{
  "doc_id": "01J7…", "title": "Q3 proposal", "open_count": 1, "can_comment": true,
  "comments": [
    { "id": 31, "doc_id": "01J7…", "author": { "id": 7, "email": "bo@acme.com" }, "body": "The pricing table is a quarter old.", "path": "source.md", "anchor": "pricing", "resolved": false, "resolved_at": null, "created_at": "2026-09-15T11:20:00+00:00", "mine": false }
  ]
}

Prompt: "Any feedback on the proposal since I published it?"

A request posted with Ask an agent in the app arrives here as an ordinary open comment whose body is the instruction and whose path and anchor say where. Apply it with edit_file, apply_patch or write_file, then resolve_comment. See Editing in the browser.

add_comment

Parameter Type Required
doc_id string yes
body string yes, up to 8,000 characters
path string no
anchor string no, a heading id or a line number

Returns the comment object. Needs at least comment access, or membership on a team-visible document no grant has narrowed.

resolve_comment

Parameter Type Required Default
comment_id integer yes
resolved boolean no true; false reopens

Returns the comment object. Prompt: "Fix what Bo pointed out, publish, then mark the comment resolved."

get_policy

No parameters. The workspace's policy and what the plan allows:

{
  "allowed_visibilities": ["workspace", "password"],
  "min_retention_days": null,
  "max_retention_days": 365,
  "secret_scan": "block",
  "ip_allowlist": null,
  "enabled": true,
  "plan": "business",
  "ip_allowlist_available": false,
  "legal_hold_available": false,
  "updated_at": "2026-09-15T09:00:00+00:00"
}

Agents call this when a publish is refused for policy reasons. enabled is false below Business, where a stored policy is not enforced. See Workspace policy.

Enterprise. Freeze a document against expiry, deletion and purge, or release it. The document's owner, or a workspace owner or admin.

Parameter Type Required Default
doc_id string yes
on boolean no true

Returns {doc_id, title, legal_hold}. Prompt: "Put the Q2 contract under legal hold."

list_audit

Business and above. Recent audit events for the workspace, newest first; owners and admins see everything, members see events about their own documents.

Parameter Type Required Default
action string no one action name such as doc.publish
limit integer no 50, at most 200

Returns {events: [{id, action, actor, actor_user_id, target_type, target_id, workspace_id, meta, ip, created_at}]}. Action names are listed in Audit log. Prompt: "Who changed the visibility of the Acme proposal?"

Deleting

delete_doc

Soft-delete. The link stops working at once; content is removed after 7 days. Owner only; a document under legal hold is refused.

Parameter Type Required
doc_id string yes

Returns {"doc_id": "...", "deleted": true, "purge_at": "..."}.

Errors

Tool errors come back as MCP tool errors with a plain message, for example publish rate limit exceeded (120/hour); slow down and retry later, storage quota exceeded: 1 GiB on the free plan; ..., visibility 'workspace' (sign-in gate) needs a Team plan or above; upgrade at https://fermium.app/pricing, old_string matches 3 times in 'source.md'; add surrounding lines to make it unique, or pass replace_all=true to change all of them, workspace policy does not allow visibility 'unlisted'; allowed: workspace, locked by ada@acme.com until 2026-09-16T09:22:40+00:00, unknown lucide icon 'rockets'; call list_icons for the curated names, or only workspace owners and admins can edit navigations. Agents are expected to relay these. See Errors.