Potion/Docs

The MCP

An AI assistant plugged into a stack of notes

The Potion MCP lets an AI assistant work directly in your workspace: find and write notes, tag them, set properties, build views, set up the graph, read attached files, and design html blocks. It uses the Model Context Protocol, so any client that supports MCP servers can connect — Claude, Cursor, VS Code, Zed, and others. Potion is not tied to one assistant.

This page covers connecting, how the tools behave, and every tool by area. For what the things themselves are, start with Concepts.

This page is also available as plain Markdown at /docs/mcp.md.

Connecting

  • Server URL: https://mcp.potion.run (Streamable HTTP)
  • Sign-in: OAuth with the Google account you use for Potion. Your assistant opens the sign-in page the first time.
  • Plan: Pro. On another plan every tool answers with an upgrade message, except get_user_info.

How you add it depends on the client. The server URL is the only part that matters; everything else is that client's own shape.

Claude Code: claude mcp add --transport http potion https://mcp.potion.run, or install the Potion plugin, which bundles this server with skills describing how Potion works.

Claude desktop, claude.ai, and other hosted assistants: add a custom connector with the server URL above.

Cursor, VS Code, Zed, and most editors: add it to the MCP server list in settings, as an HTTP server pointing at that URL.

An agent reading the docs: point it at /llms.txt, which lists every section as Markdown, or /llms-full.txt for all of them in one file.

Anything else: a config like this one.

{
  "mcpServers": {
    "potion": { "type": "http", "url": "https://mcp.potion.run" }
  }
}

Choosing the workspace

Every tool works in one workspace at a time. Without anything extra that is your default workspace, which set_default_workspace changes.

To pin a connection to a specific workspace, for example one per project folder, send a workspace header with the workspace's name:

{
  "mcpServers": {
    "potion": {
      "type": "http",
      "url": "https://mcp.potion.run",
      "headers": { "workspace": "Research" }
    }
  }
}

The header matches by name, so renaming that workspace breaks the connection until the header is updated.

How the tools behave

Everything is scoped to you and one workspace. The server knows who you are from sign-in; no tool takes a user id, and the database itself refuses any row that is not yours.

Names in, ids out. Tools accept the names people use (tag names, property keys, preset and lens names, note aliases) and return ids for precise follow-up calls. A name that does not exist is refused with a list of what does, never guessed at.

Changing is not rewriting. Notes, view filters and lens scopes each have a precise tool for a small change beside the one that replaces everything:

ToPreciseReplaces all
change a noteappend_to_note (add), edit_note (one passage)update_note
change a view's filteredit_view_filter (one condition, by id)update_view with filter
change a lens's scopeedit_lens_scope (one condition, by id)update_lens with scope

The precise tools exist because an AI resending a whole note or filter can quietly change the parts it was not asked about.

Destructive actions are guarded. Deleting a note moves it to the trash for 30 days. delete_property_def previews what would be lost and only deletes when called again with confirm. A property still used by a view, preset or lens is refused, with the names of what uses it. Workspaces cannot be renamed or deleted through the MCP at all, and neither can a structure be deleted (it takes every row with it); both are done in the web app.

Errors explain themselves. A refused call returns success: false, an error saying why, and often the next step to take.

Tools

Workspaces and account

ToolDoes
get_user_infowho is signed in
list_workspacesyour workspaces, and which is the default
get_default_workspacethe workspace tools use when no header is sent
set_default_workspacechange it
create_workspacea new, empty workspace
init_workspacecreate a first workspace if there is none
get_contextthe headers the server received, to check a workspace header

Notes

ToolDoes
find_notefind a note you named, checking aliases first, then titles
list_aliasesevery note that has an alias
read_notea note with its tags, properties, files, and links in both directions
list_notesthe 30 newest notes
create_notea new note, with tags, alias, cover and pin
append_to_noteadd text to the end, optionally under a heading
edit_notereplace one exact passage
update_notereplace the title, body or tags (guard with if_unchanged)
delete_notemove a note to the trash
list_revisionsearlier versions of a note, kept 7 days
restore_revisionput an earlier version back (itself undoable)
upload_imagestore an image, from a public URL, an upload (upload_id) or as data, and get its permanent address /api/images/<id> for a body, a cover, or a property a block inlines
ToolDoes
search_notesranked full-text search: titles, then tags, then text; "phrases" and -exclusions
search_notes_by_tagsnotes with all, or any, of some tags
search_notes_by_propertycompare, sort and find missing values: rating gt 7, status eq unread, rating empty
ToolDoes
list_note_linkswhat a note links to, and its backlinks
lint_linkslinks to notes not written yet, and notes nothing links to
get_agendawhat is unfinished in the workspace, ranked, each with a suggested next call

Tags

ToolDoes
list_tagsevery tag with its note count and linked tags
link_tagsadding one tag also adds others, including on notes already tagged
unlink_tagsremove those rules (tags already applied stay)
list_tag_linksthe rules, and why a tag appeared on a note

Tags are created by using them on a note; there is no separate create step.

Properties

ToolDoes
list_property_defsthe workspace vocabulary, with how many notes and views use each
create_property_defdefine a typed property: text, number, date, bool, select, json
update_property_defrename, relabel, reorder, or widen a select's options
delete_property_defpreview, then with confirm delete a property and every value of it
set_propertyset or clear one property on one note

Views

ToolDoes
list_viewsevery view with its filter, sort and display
read_viewone view and the notes it matches, in its order
create_viewa feed view (filter, sort, display) or a note view
update_viewchange the name, filter, sort, display, cover, colour, icon or pin
edit_view_filteradd, replace, remove, regroup or move one condition
delete_viewdelete the view (not its notes)

The filter these take is described in Filters and views.

Graph

ToolDoes
list_lensesevery lens, its scope summary, settings, and a link to open it
read_lensone lens with node ids and how many notes its scope holds
create_lensname, scope, and only the settings that differ from the defaults
update_lensrename, replace the scope, change some settings, make default, reorder
edit_lens_scopechange one condition of the scope, like edit_view_filter
delete_lensdelete the setup; the next lens becomes default if needed

A lens is a saved way of looking at the graph. See Graph.

Records

Structured rows that are not notes — a weight on a date, a rating, a logged idea. They appear in no note surface, and exist to be queried and charted.

ToolDoes
list_structuresthe shapes that exist, what each is for, and its fields. Call it first
create_structuredeclare a shape before any row of it exists; a file field takes a file's id, and a record field takes a row's id of the structure named in structure; a field can be required and unique (one row per value), a number can carry a unit and min / max, any field a display label; unique_together makes one row per combination; max_rows: 1 makes a settings object whose write replaces its one row, max_rows: N caps a list; immutable makes rows written once, never edited
update_structurechange the description or label, add a field or a choice, turn unique on or off, set max_rows, or turn immutable on or off. Tightening a rule (required, unique, removing a choice, a lower max_rows) is refused while rows disagree; renames, retypes, removals and a changed unit are refused
write_recordadd one row, or many at once. Refuses an unknown structure; names every missing required field at once; refuses a unique value already taken, naming the row, unless on_conflict: "update" updates that row instead (a batch is all or nothing: one bad row writes none, and is named); after a write, lists the optional fields left empty so the assistant can offer to fill them
list_recordsthe rows of one structure, filtered, sorted and paged; every row carries its permanent id, and a filter on id pulls one
summarize_recordscount, sum, average, min and max of a number field, computed by the database: whole, per day/week/month/year, or per value of a field
update_recordcorrect one row; a partial change is merged and re-validated
delete_recordremove one row (soft)

A record cannot exist before its structure, and field changes are additive only. See Record.

ToolDoes
list_webhooksthe subscriptions on the workspace's structures: URL, events, narrowers, on, off or paused
create_webhooksubscribe a URL the user gave to a structure; narrow it with filter (rows that match) and fields (updates to those fields). Answers with the signing secret, once
update_webhookchange the URL, events, filter or fields; turn it off or on; resume after a pause
delete_webhookunsubscribe
list_webhook_deliverieswhat was sent and how the receiver answered; send_test queues a test event first

How events are sent and signed: Webhooks.

Presets

ToolDoes
list_presetsevery preset with what it sets and asks
read_presetone preset in full, by id or name
create_presettags, fixed values, and values to ask for
update_presetchange it (lists passed replace the whole list)
delete_presetdelete it; notes it was applied to keep their tags and values
apply_presetapply to a note, with answers for its questions

Tasks

A task is one AI job you start on an open note from its Tasks menu, run on your own AI account: a name, a kind, and the instructions the model is given every time. An assistant can write tasks for you here; it cannot run one, because running needs your AI key, which the MCP never holds. See Tasks.

ToolDoes
list_tasksthe tasks you wrote, the built-in ones (listed, not editable here), and the kinds with what each may do
create_taska name, a kind and instructions, with an optional description and model
update_taskchange the name, description, instructions or model, by id or name; the kind never changes
delete_taskdelete a task you wrote, by id or name; no note is touched
KindMay
writeread the note and write text, appended to the end of the note when it is done
speakread the note and say its answer aloud; nothing is kept
watchwatch the YouTube video the note holds and write text, appended to the note when it is done

A kind that writes only appends to the note it was started on; no kind edits what is there, reads another note or reaches the network, and instructions cannot widen that. A workspace holds 30 tasks; a name is 60 characters and unique, a description 140, instructions 2,000.

Layouts

ToolDoes
list_layoutsevery layout, or the ones that fit a note or preset
read_layoutits html and the notes using it
create_layoutfrom html, or lifted from a note's block
update_layoutchange it, which changes every note using it
apply_layoutmake a note render through a layout
detach_layoutgive a note its own copy instead of the reference
load_live_layout, read_live_layout, write_live_layout, publish_live_layoutwork on a draft in the web app's live editor, then publish it

Html blocks

ToolDoes
list_block_librariesthe libraries a block may load, and when to use each
read_block_data_apithe full Block Data API reference

Files

ToolDoes
upload_fileadd a file you send (PDF, EPUB, DOCX, markdown, text, HTML, CSV, JSON), as an upload (upload_id) or as data: attached to a note, or to the workspace alone when note_id is left out
start_uploadget a one-time link to send a file from your machine, so its bytes never pass through the conversation (see below)
fetch_fileattach a file straight from a public URL
list_filesfiles, attached or kept in the workspace alone: their status and storage used
describe_filea file's outline and opening, to decide what to read
read_fileits text by pages, section or chunks
search_filefull-text search inside attachments
rename_filerename it; [[file:…]] references follow
process_filerun text extraction again
delete_filedelete a file and its text

Sending a file without typing it

Every argument of a tool call is written out by the model, so a long page or a binary file cannot sensibly travel inside one. start_upload moves the bytes over plain HTTP instead:

  1. start_upload({ kind }) returns an upload_id, a link and a ready curl command. kind is text (up to 2 MB), image (10 MB) or file (50 MB).
  2. Send the file to the link with one PUT, headers unchanged. The link works once, for ten minutes.
  3. Use the id: write {{upload:<upload_id>}} in the content of create_note, update_note or append_to_note (inside an html fence for a page), or pass upload_id to upload_image or upload_file.
  • An upload is used once and then removed; one that is never used is deleted after an hour. A person may have ten waiting at a time.
  • Pass the file's sha256 to start_upload and anything else sent to the link is refused: the link is the only credential, and it appears in the conversation.
  • A note built from uploads may hold 2,097,152 characters at most. The tool then answers with the note's length and not its content; read_note returns it.
  • Uploaded text is the note's content like any other: an html block still runs in the sandbox, with no network.
ToolDoes
list_quick_accessthe notes pinned to the sidebar, in order
add_to_quick_accesspin a note, with an icon and colour
update_quick_accesschange a pin's icon or colour
remove_from_quick_accessunpin it

Good first requests

  • "What's in my Potion?" — the assistant lists workspaces, views and tags.
  • "Save this article to Potion with the Article preset."
  • "Make a view of films I rated 8 or more, best first."
  • "Make a lens of my Rust notes with their tags, and give me the link."
  • "Write me a task that lists the action items of a note as a checklist."
  • "Which of my notes link to Machine Learning?"
  • "Read the PDF on my Research note and summarise chapter 3."