Creating pages
Create a page from HTML or Markdown, update or patch it, and manage large uploads.
A page is a single HTML or Markdown document, hosted at one shareable link. It can be a report, a proposal, a prototype, anything an AI tool or a person produces as a document.
Creating a page
The create_page tool takes exactly one of html or markdown. Markdown is
rendered to HTML server-side. If you omit title, it is inferred from the
document's <title> tag or its first heading. If you give a folder that
doesn't exist yet, it is created. The tool returns the shareable URL.
create_page({
title: "Q3 Report",
markdown: "# Q3 Report\n\n...",
folder: "Reports"
})You can also set visibility and, for password-protected pages, password at
creation time. See Access and sharing for what each visibility
level means.
Markdown rendering
Uploading or passing Markdown (a .md file, or the markdown argument)
renders it to clean, sanitized HTML automatically, no manual formatting
needed:
- GitHub-flavored Markdown (tables, task lists, strikethrough) is supported.
- Code blocks get syntax highlighting.
- Headings get anchor ids, so a heading can be linked to directly.
- Front matter (
title/descriptionat the top of the file) sets the page's title and meta description if present; otherwise the title falls back to the first# heading. - Images must be an absolute
http(s)URL or adata:imageURI; anything else is dropped, since a single Markdown upload has no bundled asset folder to resolve a relative path against. - External links automatically open in a new tab with
rel="noopener noreferrer".
The original Markdown source is preserved and stays downloadable alongside the rendered page, so uploading Markdown never throws away your source of truth.
Same pipeline everywhere
Markdown rendering is identical whether you upload a .md file in the
dashboard or pass markdown through the MCP server (create_page,
update_page, or request_upload_url with format: "markdown").
Updating vs. patching
Two tools change an existing page's content, and they solve different problems:
| Tool | Use it when | Behavior |
|---|---|---|
update_page | You have the full new HTML or Markdown | Replaces the page's content wholesale. Writes a new (reversible) version. |
patch_page | You want to change one part without resending the whole document | Applies a targeted edit: exact find/replace, or insert content after a specific heading. |
patch_page has two mutually exclusive modes:
- Find and replace.
findmust match the current HTML exactly (callget_pageonce to see it, or reuse an inline comment's anchor quote fromlist_comments). By defaultfindmust match exactly once; passexpectedMatchesto change several identical spans at once. - Insert after a heading.
insertAfterHeadingmust exactly match a heading's text fromget_page's outline;insertis placed right before the next heading of the same or higher level (or at the end of the page if there isn't one). This lets you append to a section without ever fetching its contents.
Pass preview: true with either mode to see what would change without
writing a version. Prefer patch_page over update_page for edits to large
pages, since it never requires resending the whole document.
Corrupted-patch guard
If applying a patch would leave the HTML looking truncated (for example, a large inline base64 image got cut off mid-value), the write is aborted and nothing is saved, rather than silently storing broken HTML.
Reading a page
get_page returns a page's current HTML and metadata. For a page you
haven't seen yet, call it with outline: true first: this returns metadata,
size, and a heading outline (with character offsets) instead of the full
HTML. From there, either read a specific range with offset/maxBytes, or
use patch_page to act on one section directly, so a large page never needs
a full fetch.
When a page is large, get_page's response also includes a downloadUrl:
fetch it directly to pull the whole raw HTML to a local file without that
content passing through the model at all.
Finding things in a page a colleague sent you
Open any shared page and there is a Search button in the header, or press Ctrl-F (Cmd-F on a Mac). Type and it counts the matches, Enter walks through them, and every match is highlighted.
This exists because your browser's own find bar cannot help here: a shared page renders in a sandboxed frame, so the browser searches the chrome around it and never the page itself. On a phone there is no find bar at all. For a long reference, that was the difference between reading a document and using one.
It needs nothing switched on, and it works for anyone who can open the page, including people without an account.
Asking a page a question
A page can answer questions about itself in plain language. The reader opens Ask this page in the header, types a question, and gets an answer drawn only from that page.
It is off until the page's author turns it on, one page at a time, in the
page's settings. The owner sees the button on their own page marked Off
until they do; readers only see it once it is on. A workspace with AI switched
off cannot use it at all, whichever pages say otherwise. See
AI and your content for exactly what is sent and
what is kept.
The answer comes with its receipts
Every answer carries the sentences it rests on. Tap one and the page scrolls to that line, highlighted, so you can check the answer instead of trusting it.
Those quotes are verified against the page before you see them: a quote that is not actually in the document is discarded, and an answer left with no real quotes is shown as unsupported rather than as an answer.
What it will tell you it cannot do
A page that does not cover something says so, rather than producing a plausible paragraph from general knowledge.
A page too long to read in one go says that too. There is a limit on how much of a page can be read at once, and a document well past it will tell you it was cut off and that the answer may be further down, rather than reporting that the page does not mention it. This matters on long reference documents, where "no entry for that" reads as authoritative and gets acted on.
Follow-up questions remember the previous couple of exchanges, so "what about the other one" works.
Limits
Asking is capped per reader, per page per day, and per workspace per day, so a widely-shared page cannot run up a surprise. A reader who hits a cap is told which one.
Images in pages
A page keeps its images as hosted assets and references them by URL, rather than
as giant inline base64 blobs. Drop an HTML file together with its images (or a
whole folder) in the browser and the relative references are rewritten
automatically, or host an image over MCP with add_page_image. See
Images in pages for the full details.
Large-page uploads
Sending a page's whole HTML as a tool argument gets awkward past about 50 KB: it can exceed a model's output-token budget or a client's tool-call size limit, and even under those limits it's slow, since every byte has to be emitted token by token. Two tools move the bytes around that entirely:
request_upload_urlreturns a short-lived URL (valid 15 minutes).PUTthe raw HTML (or Markdown, withformat: "markdown") directly to it, for examplecurl -X PUT --data-binary @page.html '<url>'. Pass an existing page'sidto replace it instead of creating a new one.request_download_urlis the read-side counterpart: it returns a short-lived URL toGETa page's raw stored HTML to a local file.
Your plan's page-size limit still applies to an uploaded file (see the FAQ for the exact limits). If your MCP client can't make an HTTP request itself, the dashboard's page uploader and each page's "Replace content" settings do the same thing from a browser.
See also
- Access and sharing for visibility levels, invites, and passwords.
- MCP tool reference for every page tool's full schema.
- AI and your content for what asking a page sends to a model, and what is kept.