Skip to main content
List, read, create and edit collaborative markdown documents, and inspect their version history.

list_documents

Scope: documents:read (step-up) · Kind: read-only · Annotations: readOnlyHint: true, destructiveHint: false, openWorldHint: false
List the documents and pages you can reach. Narrow to one conversation (conversation_id), one pod (pod_id), or a library section (scope: mine = ones you own, shared = shared with you, pod = every pod you belong to, org = shared with the whole workspace); with none of these, everything you can reach is merged. Each row carries scope and your role (owner | editor | commenter | viewer) — a viewer can read but not edit or comment. Optional query filters by title/content. Returns metadata only; use read_document for content.

Parameters

Pass at most one of scope, conversation_id and pod_id.
'mine' | 'shared' | 'pod' | 'org'
A library section. mine = artifacts you own; shared = artifacts explicitly shared with you; pod = every pod you belong to; org = artifacts shared with the whole workspace.
string
Only artifacts linked to this conversation.
string
Only artifacts in this pod.
string
Case-insensitive substring filter over title and body, applied to the rows already read. It is a funnel, not a ranked search; use search_knowledge for retrieval.
string
The next_cursor from the previous page of the same listing. Opaque and bound to the arguments that produced it: passing it with a different scope, pod_id, query or include_archived is refused.
boolean
default:"false"
Include archived artifacts.
number
default:"50"
Rows to return, 1 to 50. Clamped.

Returns

array
string | null
Where the next page starts. A string on the paged listings (mine, shared, org, pod_id) while more rows exist; null when the listing is exhausted, and always null on the merged listing, scope: "pod" and conversation_id, which cannot page.
boolean
true when more rows exist than were returned: on a paged listing that is the same as next_cursor being a string; on the merged listings it means narrow with pod_id or scope (or an org-wide listing stopped after 50 pods).
The text block has one line per row:
or No documents found.

Completion checklist

  • On mine, shared, org and pod_id, follow next_cursor until it is null before concluding a document does not exist; query filters each page, so a page can be short without being the last.
  • On the merged listing and scope: "pod", truncated: true is inconclusive. Narrow with pod_id, conversation_id or a paged scope and list again.
  • A row’s conversation_id and pod_id are the ids the other tools take; list_conversations turns a room or pod name into them.
  • The task is done when a read-back shows it: after any write, call read_document and check the content, not only the tool’s success.

Permissions and side effects

  • Needs documents:read.
  • Rows you have no role on are dropped, never surfaced.
  • A conversation_id or pod_id you cannot reach fails with Artifact not found, the same as a made-up id.
  • Reads only. Recorded in the agent activity history.

Errors

Example

Request
Response (structuredContent)

read_document

Scope: documents:read (step-up) · Kind: read-only · Annotations: readOnlyHint: true, destructiveHint: false, openWorldHint: false
Read a document as markdown (an HTML page returns its source). ALWAYS returns version — the token edit_document’s expected_version and replace_all writes are checked against. Long documents window: pass offset to continue past 24000 characters.

Parameters

string
required
The artifact id.
number
default:"0"
Character offset to start from. Minimum 0.
number
default:"24000"
Characters to return, 1 to 24,000. Clamped.

Returns

string
string
'document' | 'html_app' | 'table'
string
The requested slice. For an html_app this is the page’s HTML source.
number
The document’s current version. Pass it as expected_version to edit_document.
number
Total length of the full text.
number
The offset that was applied.
boolean
true when text remains after this slice. Call again with offset = offset + window.
boolean
object
Present when the artifact is linked to a conversation: { "conversation_id": "<id>" }.
The text block is:

Permissions and side effects

  • Needs documents:read and at least the viewer role on the artifact.
  • Reads only. Recorded in the agent activity history.

Errors

Limitations

  • Comment marks and other editor-only annotations are not part of the markdown.
  • For a table, the returned text is whatever markdown projection the table exposes; tables cannot be edited through MCP.
  • Reading a page here returns raw HTML. read_page is the page-specific tool and returns the same version.

Example

Request
Response (structuredContent)

create_document

Scope: documents:write (step-up) · Kind: write · Annotations: readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false
Create a new collaborative document, optionally filled with your markdown. Either in a conversation (conversation_id — its card is posted to the room) or in the person’s library with a scope: user (private to them, the default), pod (needs pod_id), or org (every workspace member can edit). The caller becomes its owner. Returns . Pass idempotency_key to make retries safe: a repeat with the same key within 10 minutes returns the original document instead of creating a twin.

Parameters

Pass either conversation_id or scope (with pod_id when the scope is pod), never both.
string
required
The document’s title. Must be non-empty.
string
Create inside this conversation. A document card is posted to the room.
'user' | 'pod' | 'org'
default:"user"
Where the document lives when no conversation_id is given. user is private to the caller; pod needs pod_id; org is editable by every workspace member. No card is posted anywhere; the document gets its own chat.
string
Required with scope: "pod".
string
Initial body. Written verbatim, up to 60,000 characters. Whitespace-only content is treated as empty.
string
Any string you choose. A second call with the same key within 10 minutes returns the original document (deduplicated: true) instead of creating another. Keyed on the key only, never the title, so two deliberate creates with one title both succeed.

Returns

string
string
number
0 for an empty document; otherwise the version after the initial fill. On a deduplicated retry this is present only when the retry filled in missing content.
boolean
Present and true when an idempotency_key matched a recent create.
boolean
Present and true when a deduplicated retry found the original empty and filled it with content_markdown.
Text: Created "<title>" (document_id: <id>, version <n>). or, on a retry, Document already created: "<title>" (document_id: <id>). Returning the original instead of creating a duplicate — ….

Permissions and side effects

  • Needs documents:write.
  • With conversation_id: you must be able to participate in that conversation. The gate runs inside the same transaction as the insert.
  • With scope: "pod": you must be a member of the pod.
  • The caller becomes the owner. The initial fill is a checkpointed write attributed to you via the host, and appears in get_document_versions.
  • With conversation_id, a card lands in the room. With a scope, nothing is posted anywhere.

Errors

Example

Request
Response (structuredContent)

edit_document

Scope: documents:write (step-up) · Kind: write, destructive · Annotations: readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false
Write verbatim markdown into a document while humans may be editing it live. Modes: append (add at the end), replace_section (replace the region an EXACT section_excerpt from read_document resolves to — a stale excerpt fails loudly with candidates, it can never clobber), replace_all (whole body; conflicts if expected_version moved), fill_empty (only if nobody typed yet). Human edits made mid-write are KEPT and reported in kept_blocks, never overwritten. Every write is checkpointed and undoable. dry_run resolves the target and writes nothing.

Parameters

string
required
'append' | 'replace_section' | 'replace_all' | 'fill_empty'
required
What to do with content_markdown.
string
required
The markdown to write, verbatim. Up to 60,000 characters; longer content stops early with stop_reason: "cap".
string
replace_section only, and required there. An exact passage quoted from read_document. It is matched whitespace-insensitively and resolved to the contiguous blocks it covers; those blocks are replaced.
number
The version a prior read_document returned. Required for replace_all, where a mismatch is a hard conflict. On other modes it is optional and only reports drift.
boolean
default:"false"
Resolve the target and version checks, write nothing.

How expected_version is tiered

Returns

On success:
number
The document’s version after the write.
number
boolean
Present when the write did not complete.
'cap' | 'anchor_gone'
Why it stopped. cap: the 60,000-character budget was hit; continue with another call. anchor_gone: someone removed the section being written into; do not re-add it.
string
What the agent should do about the early stop.
boolean
Present when expected_version no longer matched (non-replace_all modes).
number
How many blocks changed since expected_version, when known.
object
Present when a person edited blocks while this write ran. Their edits are kept next to yours: { "count": n, "excerpts": ["…"] }.
On dry_run:
Text: Wrote <n> characters (<mode>); the document is now at version <v>. followed by Stopped early (<reason>). and/or Kept <n> block(s) a person edited while you were writing — their text stands next to yours. when applicable.

Permissions and side effects

  • Needs documents:write and the editor or owner role.
  • Checkpoints the document before writing; the checkpoint appears in get_document_versions and is undoable in the app.
  • Takes a short write lease. Concurrent agent writers queue briefly, then receive write_conflict with a retry hint.
  • Attributed to you via the host on version rows and cursors.
  • Works only on document artifacts; pages and tables are refused with a message naming the right tool.

Errors

A failed replace_section is never degraded into an append.

Recovering from refusals

Retrying without duplicating: a result with stopped_early: true wrote part of the content, so re-read and append only what is missing rather than the whole payload again. A call that timed out with no result may still have landed; read before retrying. A version_conflict on replace_section is not a stale excerpt: the excerpt still resolves, so the retry is the same call with the fresh version.

Example

Request
Response (structuredContent)
Here a person edited the Goals list while the agent was writing; the version had moved to 42 before the write, the excerpt still matched, and the person’s edit was kept.

get_document_versions

Scope: documents:read (step-up) · Kind: read-only · Annotations: readOnlyHint: true, destructiveHint: false, openWorldHint: false
Recent versions of a document, newest first — automatic checkpoints (every agent write checkpoints before touching it, so this is the undo history) and named versions a person saved (pinned: true, with their label; never pruned). Metadata only; restoring and naming stay human acts in the app.

Parameters

string
required

Returns

array
The 20 most recent rows, newest first.
Text, one line per row:
or No versions yet.

Permissions and side effects

  • Needs documents:read and any role on the artifact.
  • Reads only. Recorded in the agent activity history.

Errors

Limitations

  • Only the 20 most recent rows are returned; there is no paging.
  • Automatic checkpoints are pruned over time in the app; named versions are not.
  • There is no MCP tool to restore, name or diff versions.

Example

Request
Response (structuredContent)
instruction is a free-form label set by whatever produced the row; the values shown are illustrative.