Agent access over MCP
Markest speaks the Model Context Protocol, so an AI agent can search, read and write your pastes as tools rather than by constructing HTTP requests.
The quick start above covers connecting. This page is the reference behind it.
If you want to call Markest from a script rather than an agent, the API reference covers the REST endpoints instead — same permissions, same quotas, same rate limits.
For artifacts encrypted end to end, which this endpoint cannot open, the markest command runs a small MCP server on your own machine, described under Encrypted artifacts: the local server below.
The endpoint
POST https://marke.st/mcp
The transport is streamable HTTP with JSON-RPC 2.0, revision 2025-06-18 — one
endpoint, no session setup, one JSON-RPC message per POST. A batch is refused
with 400; a notification is accepted with 202 and no body. Opening the
endpoint in a browser shows the connection guide; any other GET gets 405,
because the server offers no event stream.
Authentication happens before anything is dispatched, tools/list included, so
an unauthenticated call returns 401 with a WWW-Authenticate challenge
pointing at the server's protected-resource metadata. Tool discovery is not
anonymous.
A request carrying an Origin header is refused with 403 unless it comes from
Markest itself or a supported client's web app, so a hostile page cannot drive
the endpoint through someone's browser; clients calling from their own servers
send no Origin and are unaffected. An MCP-Protocol-Version header naming a
revision the server does not implement gets 400.
Two ways to connect
An API key suits anything that can set its own headers — Claude Code,
Codex, Cursor, VS Code, your own client. Create one under
Account → API keys and send it as Authorization: Bearer mk_live_….
OAuth is for ChatGPT, which cannot present an API key at all, and for Claude Desktop, which adds a remote server only as a custom connector in its settings. You add the endpoint, sign in to Markest, and approve the scopes on Markest's own consent screen. Nothing is shared with the application beyond what you approve.
Agent access is a plan feature. If your plan does not include it, calls return a message saying so rather than failing obscurely.
Tools
Shared tools
Discovery now offers 35 purpose-named tools. Shared calls use target (type is artifact or site, plus id for existing resources) and options. mode selects an explicit variant: create/replace, template, preview, upload/inline, or add/remove. The schema lists only variants allowed by your credential. Site writes require expected_revision; public artifact edits appear immediately, while site edits change drafts. Picture selection is separate from site build preview.
{"target":{"type":"site","id":"01M..."},"options":{"path":"index.html","offset":1,"limit":80}}
| Tool | Scope | Does |
|---|---|---|
search |
pastes.read / sites.read | Find your pastes by title, folder or document path / List sites |
fetch |
pastes.read / sites.read | Read a paste in full, or one document of it / Read site structure / Read which picture a paste shows where it is listed |
markest_list_files |
pastes.read / sites.read | List a paste's documents with their size in lines and bytes / Find files by glob, with sizes and hashes |
markest_read_file |
pastes.read / sites.read | Read a range of lines of one document, numbered / Read source file lines |
markest_grep |
pastes.read / sites.read | Search the text of documents across several pastes, like grep / Search source with numbered matches and context |
markest_list_versions |
pastes.read / sites.read | List a paste's saved versions and what each changed / List immutable preview builds |
markest_diff_versions |
pastes.read / sites.read | Compare two versions, or a version and now, as a diff / Compare builds and draft, including manifest changes |
markest_create |
pastes.write / pastes.read + pastes.write / sites.read + sites.write | Create a paste from one or more documents / Make an artifact from a template, filled with fields / Create a private site draft |
markest_fork |
pastes.read + pastes.write / sites.read + sites.write | Copy pastes you can read into your account, up to ten at once / Copy a full site or its reusable structure into a new draft |
markest_write_file |
pastes.write / sites.read + sites.write | Append a document to an existing paste / Replace or rename one document |
markest_edit_file |
pastes.write / sites.read + sites.write | Replace exact text in one document / Exact text edits with revision checks and dry run |
markest_apply_patch |
pastes.write / sites.read + sites.write | Change one or more documents with a unified diff / Atomic multi-file patches with dry run |
markest_update |
pastes.write / sites.read + sites.write | Update draft files and structure / Change title, folder, tags, expiry, password, burn-after-reading, version history, or move it to another space / Choose the picture a paste shows where it is listed |
markest_add_images |
pastes.write | Add image files from your disk to a paste - one, or a whole folder - with no base64 / Upload an image to a paste, for its documents to show |
markest_update_collaborators |
pastes.write | Let people open a private paste by their e-mail addresses / Stop people opening a paste as collaborators |
markest_get_version |
pastes.read | Read one saved version, and a document's text as it was |
markest_list_templates |
pastes.read | List the templates you can make an artifact from, with their fields |
markest_restore_version |
pastes.write | Put an earlier version back, as a new version |
markest_delete_document |
pastes.write | Remove one document |
markest_set_visibility |
pastes.write | Change who can see one or many pastes |
markest_create_signed_link |
pastes.write | Create an expiring link that opens a private paste |
markest_list_collaborators |
pastes.read | List who, besides you, may open a paste |
markest_list_images |
pastes.read | List a paste's images, with the markdown to show each |
markest_get_image |
pastes.read | Read one image of a paste |
markest_delete_image |
pastes.write | Delete an image uploaded to a paste |
markest_list_comments |
pastes.read | Read what readers said under your pastes, thread by thread |
markest_reply_to_comments |
pastes.read + pastes.write | Answer several comments at once, as yourself |
markest_resolve_comments |
pastes.read + pastes.write | Mark conversation threads resolved, or reopen them |
markest_get_views |
pastes.read | How many times your pastes were read, by day and document |
markest_delete_paste |
pastes.write | Delete a paste and everything in it |
markest_list_workspaces |
pastes.read | List your workspaces, and which of them this agent may reach |
markest_request_workspace_access |
pastes.read | Ask you, with a link, to let this agent into a workspace |
markest_preview_site |
sites.read + sites.write | Build a private ten-minute preview |
markest_publish_site |
sites.read + sites.write + sites.publish | Request browser approval for an immutable build |
markest_validate_site |
sites.read | Validate draft structure without creating a build |
Compatibility aliases
The native names and examples below remain callable for existing integrations, but aliases are omitted from discovery. New clients should use the shared table above. Unsupported target/mode combinations are refused without changes. Site binary image uploads are not yet supported.
| Tool | Scope | Does |
|---|---|---|
search |
read | Find your pastes by title, folder or document path |
fetch |
read | Read a paste in full, or one document of it |
markest_list_documents |
read | List a paste's documents with their size in lines and bytes |
markest_read_document |
read | Read a range of lines of one document, numbered |
markest_grep |
read | Search the text of documents across several pastes, like grep |
markest_list_versions |
read | List a paste's saved versions and what each changed |
markest_get_version |
read | Read one saved version, and a document's text as it was |
markest_diff_versions |
read | Compare two versions, or a version and now, as a diff |
markest_create_paste |
write | Create a paste from one or more documents |
markest_list_templates |
read | List the templates you can make an artifact from, with their fields |
markest_create_from_template |
read, write | Make an artifact from a template, filled with fields |
markest_fork_paste |
read, write | Copy pastes you can read into your account, up to ten at once |
markest_add_document |
write | Append a document to an existing paste |
markest_add_images |
write | Add image files from your disk to a paste - one, or a whole folder - with no base64 |
markest_upload_image |
write | Upload an image to a paste, for its documents to show |
markest_list_images |
read | List a paste's images, with the markdown to show each |
markest_get_image |
read | Read one image of a paste |
markest_delete_image |
write | Delete an image uploaded to a paste |
markest_get_preview |
read | Read which picture a paste shows where it is listed |
markest_set_preview |
write | Choose the picture a paste shows where it is listed |
markest_update_document |
write | Replace or rename one document |
markest_edit_document |
write | Replace exact text in one document |
markest_apply_patch |
write | Change one or more documents with a unified diff |
markest_restore_version |
write | Put an earlier version back, as a new version |
markest_delete_document |
write | Remove one document |
markest_update_paste |
write | Change title, folder, tags, expiry, password, burn-after-reading, version history, or move it to another space |
markest_set_visibility |
write | Change who can see one or many pastes |
markest_create_signed_link |
write | Create an expiring link that opens a private paste |
markest_list_collaborators |
read | List who, besides you, may open a paste |
markest_add_collaborators |
write | Let people open a private paste by their e-mail addresses |
markest_remove_collaborators |
write | Stop people opening a paste as collaborators |
markest_list_comments |
read | Read what readers said under your pastes, thread by thread |
markest_reply_to_comments |
read, write | Answer several comments at once, as yourself |
markest_resolve_comments |
read, write | Mark conversation threads resolved, or reopen them |
markest_get_views |
read | How many times your pastes were read, by day and document |
markest_delete_paste |
write | Delete a paste and everything in it |
markest_list_workspaces |
read | List your workspaces, and which of them this agent may reach |
markest_request_workspace_access |
read | Ask you, with a link, to let this agent into a workspace |
markest_list_sites |
sites.read | List sites |
markest_get_site |
sites.read | Read site structure |
markest_read_site_file |
sites.read | Read source file lines |
markest_create_site |
sites.read, sites.write | Create a private site draft |
markest_update_site |
sites.read, sites.write | Update draft files and structure |
markest_preview_site |
sites.read, sites.write | Build a private ten-minute preview |
markest_publish_site |
sites.read, sites.write, sites.publish | Request browser approval for an immutable build |
markest_list_site_files |
sites.read | Find files by glob, with sizes and hashes |
markest_grep_site |
sites.read | Search source with numbered matches and context |
markest_edit_site_file |
sites.read, sites.write | Exact text edits with revision checks and dry run |
markest_apply_site_patch |
sites.read, sites.write | Atomic multi-file patches with dry run |
markest_list_site_builds |
sites.read | List immutable preview builds |
markest_diff_site |
sites.read | Compare builds and draft, including manifest changes |
markest_validate_site |
sites.read | Validate draft structure without creating a build |
markest_fork_site |
sites.read, sites.write | Copy a full site or its reusable structure into a new draft |
search and fetch use the input and result shapes ChatGPT expects of a
knowledge source, so Markest can be cited as one.
Tools that change state are annotated destructiveHint, which lets a client ask
for confirmation before running them. Annotations are a hint to the client, not
a substitute for the checks the server performs regardless.
Building a paste over several calls
This is the expected pattern rather than a workaround:
markest_create_paste → { id: "01J…", documents: ["README.md"] }
markest_add_document → { paste_id: "01J…", path: "docs/setup.md", … }
markest_add_document → { paste_id: "01J…", path: "docs/api.md", … }
Working on part of a paste
The tools an agent uses on a repository work on a paste too, so a long document never has to travel whole:
markest_list_documents → { paste_id: "01J…" }
markest_grep → { pattern: "TODO", pastes: ["01J…", "01K…"], context: 2 }
markest_read_document → { paste_id: "01J…", path: "docs/setup.md", offset: 120, limit: 40 }
markest_edit_document → { paste_id: "01J…", path: "docs/setup.md", edits: [{ old_text: "…", new_text: "…" }] }
markest_diff_versions → { paste_id: "01J…", from: 3 }
Line numbers agree across markest_read_document, markest_grep and markest_diff_versions. An edit or a patch applies completely or not at all, and is saved as one version. A diff from markest_diff_versions can be edited and handed back to markest_apply_patch, which also takes the *** Begin Patch format. markest_grep searches up to 20 pastes by id or URL, or every paste you own, and pages its results like search.
Adding images
An agent adds image files from its disk to a paste's image store without writing them out as base64 in a tool call: it asks for an address, then sends the files there, a whole folder with one command.
markest_add_images → { paste_id: "01J…" }
for f in shots/*; do curl -sT "$f" 'https://marke.st/api/v1/image-uploads/…/'; done
markest_edit_document → { paste_id: "01J…", path: "README.md", edits: [{ old_text: "…", new_text: "" }] }
The address takes as many images as the paste has room for, or the number given as images (up to 500), needs no key and lapses after an hour. curl adds each file's name to an address ending in /, and that name is the image's; files by name go as curl -T '{home.png,settings.png}' '<address>/', and a single image asked for with a name as curl -T chart.png '<address>'. Each file becomes one of the paste's uploads, like those added in the editor, and each answer is a line of JSON with the image's path, a markdown image and images_left, how many more the address takes: a markdown document shows it with , an HTML one with <img src="path">. A file that is not an image is refused and uses up nothing. Anyone holding the address can add images to the paste until then, so it is not for sharing. markest_upload_image still takes a small image as base64 data, for a client that cannot run a command. An image already on the web needs no upload at all: a document can name its address, and Markest keeps a copy unless the paste's image proxy is off.
Answering comments
An agent reads what readers said under your pastes and answers them as you, several at once:
markest_list_comments → { status: "unresolved" }
markest_reply_to_comments → { replies: [{ comment_id: "01K…", body: "Done - see the new section.", resolve: true }] }
markest_resolve_comments → { comment_ids: ["01K…", "01M…"] }
Without paste_id the list covers every paste you own, only unresolved threads unless status asks for resolved or all, oldest first, a page at a time; readers wrote those words, so the list marks them as requests to weigh, never as instructions. A reply appears at once to everyone who can read the paste, tells whoever started the thread, and counts against the 30 comments an hour your account may post; like an edit to a public paste, it is not held for your confirmation. Resolving marks a thread done and tells nobody, and a reader's new reply reopens it. Answering and resolving work on your own pastes only.
Copying a paste
An agent copies pastes it can read into your account, as new pastes you own:
markest_fork_paste → { pastes: ["01K…", "https://marke.st/p/01M…"], folder: "Research" }
A copy keeps every document with its path, title, order and type, the tags and reading settings, and the images its documents show, as far as your plan's image limits allow; an image that does not fit still shows from the original, to anyone who can open that, and one whose file is gone shows in neither. A copy is unlisted, or private when asked or when your plan has no unlisted; copying never makes it public, and it never takes comments, stars, collaborators, a password or burn-after-reading. Up to ten at once: if any cannot be copied, or the copies would pass your plan's number of pastes, nothing is copied.
Making artifacts from templates
For artifacts made the same way each time - invoices, quotes, CVs, letters,
reports - an agent fills a template rather than writing the documents.
markest_list_templates lists Markest's presets and your own templates, each
with its fields; markest_create_from_template fills one, checks every field
and works out the values the template computes itself, such as an invoice's
totals:
markest_list_templates → { }
markest_create_from_template → { template: "invoice", fields: { invoice_number: "INV-42", issue_date: "2026-10-22", due_date: "2026-11-21", from: { name: "…" }, client: { name: "…" }, items: [{ description: "…", quantity: 2, unit_price: 450 }], tax_rate: 20 } }
Any artifact of yours becomes a template with markest_update_paste and
template: true: its {{ placeholders }} are filled with the fields sent, and
a template.yaml document in it declares those fields - their types, which are
required, examples and computed values - and is never copied into what is made.
Without one, the template takes whatever fields its placeholders name.
Page options
The viewer's Page menu, in its top bar, sets how an artifact's page looks and
saves it as a PDF: the typeface of a markdown document read as a page, the
paper, and whatever options its template declares. The invoice, quote, CV,
cover letter, proposal and timesheet presets offer an accent colour; the CV
and the proposal also offer their spacing. Send page_options to choose them
for everyone who opens the artifact - readers can still choose their own:
markest_create_from_template → { template: "invoice", fields: { … }, page_options: { accent: "#7a1f3d", paper: "letter" } }
markest_update_paste → { paste_id: "…", page_options: { font: "plex" } }
A template declares its own under options in its template.yaml: a
colour, a choice or a toggle, each with a name, a label, a default and
what it offers. An HTML document reads each value as --option-<name> and
data-option-<name> on its root element, and Save PDF prints it on the paper
chosen:
options:
- name: accent
type: colour
label: "Accent colour"
default: "#0b5f70"
choices: ["#0b5f70", "#7a1f3d", "#2e6b3f"]
Publishing needs your confirmation
Restricting a paste — to unlisted or private — happens immediately, in batches of up to 100. Making one public is the single change that cannot be walked back: once something has been read, copied or indexed, setting it private again retracts nothing.
So by default an agent cannot publish directly. markest_set_visibility returns
a link instead, and so does markest_create_paste when asked for a public paste,
which it creates unlisted in the meantime; nothing is public until you open the
link and approve.
The confirmation screen covers every paste in the request at once, with a checkbox each — so an agent publishing thirty documents costs you one visit, not thirty, and you can approve a subset. The link works once and expires after 24 hours.
What you approve is what you saw: a paste that changes after the confirmation page opened is not published, and you are asked to review it again. Asking for the same change while a confirmation is pending returns the same link.
A well-behaved agent will hand you the link and stop. It should not retry.
Changing a paste that is already public is not gated: markest_update_document
and markest_add_document publish the change as soon as it is saved, and their
descriptions tell the agent so.
Turn the gate off under Account → Publishing if you would rather publish directly. Restricting a paste is never gated either way.
Workspaces need your approval
An agent does not reach your shared workspaces on its own. Until you let it into
a workspace, it reads that workspace's pastes only as someone with no account
would - public and unlisted ones - and search, markest_grep and
markest_list_comments pass over them.
To reach one, the agent asks: markest_request_workspace_access names the
workspace, and so does any tool that reaches for a paste in it. Either hands back
a link for you to open in Markest, signed in, where you choose how long it may
reach that workspace:
- for 10, 30 or 60 minutes;
- for this session - until the agent disconnects or restarts, or after a day unused;
- until you revoke it.
It then acts with your role there. Each workspace is approved on its own, the link
waits a day, and asking again while it waits gives the same link.
markest_list_workspaces lists your workspaces and what this agent may reach in
each.
Once let in, an agent can also work in one workspace on purpose. search and
markest_grep take a workspace - its id or name, or personal for your own
space - and say which workspace each paste they find is in.
markest_create_paste creates a paste in that workspace, and
markest_update_paste moves a paste between your own space and your workspaces.
A paste made in a workspace belongs to the workspace and counts against the plan
that pays for it; making one there takes your editor or owner role, and moving
one out takes your owner role in the workspace it leaves.
Account → API keys lists what each agent may reach, with Revoke. Leaving a workspace ends what your agents were let into there, and disconnecting your applications ends theirs. A key made for a workspace itself needs no approval for that workspace.
A session is the connection an agent makes when it starts. Markest names it in
the Mcp-Session-Id header of its answer to initialize, and the agent sends it
back with every request; a DELETE to the endpoint with that header ends it. A
request naming a session that ended is answered 404, and the agent starts a new
one.
What an agent can and cannot do
Every tool runs as the owner of the credential presented, and re-checks that credential's permissions on each call:
- A key without
create_pastesees no write tools and cannot call them. - A read-only OAuth grant (
pastes.read) likewise cannot write. - Tools a credential cannot use are omitted from
tools/listentirely. - Asking for a paste that is not yours returns the same answer as one that does not exist, so ids cannot be probed.
- Plan quotas — paste count, documents per paste, visibility, expiry — apply exactly as they do on the web.
- A paste's version history is readable by its owner only, even when the paste is public.
markest_restore_versionputs an earlier version back as a new version: history is never rewritten, and who can see the paste does not change.markest_create_signed_linkissues links for private pastes only, lasting from an hour to 90 days. A link opens the whole paste until it expires; Revoke all signed links in Markest stops every link to a paste.markest_add_collaboratorslets people open a private paste by signing in to Markest with a verified account under the address given. Each address is added once however it is written or repeated, your own is left out, and a call with any bad address, or one that would pass your plan's collaborator limit, changes nothing. Markest sends no invitation, and no answer says whether an address has an account.markest_reply_to_commentsandmarkest_resolve_commentsact only under your own pastes and need a credential that may read as well as write, so a key made only to create pastes cannot answer anyone. A batch with any comment they cannot act on changes nothing.markest_fork_pastecopies only what the credential can read, and needs a credential that may read as well as write. A private paste opened through a signed link can be read but not copied, and one behind someone else's password or that burns after reading is not copied at all.fetchreads a private paste from a signed link until the link expires. A password-protected or burn-after-reading paste still opens only in a browser.- In a workspace, a credential acts with its account's role there once its agent is let in (see above): every member's agent reads the workspace's pastes, private ones included; an editor's also changes them and reads their history; only an owner's changes who may read one - its password, expiry, burning after reading, visibility, signed links and collaborators - or deletes it. A key made for a workspace itself reaches that workspace alone, and is shown only the tools its role there allows: it never publishes, deletes, shares, copies a paste elsewhere or answers comments.
/mcpshares the API rate-limit bucket.
Document bodies over 100 KB are truncated in fetch results with a link to the
full raw document, so one large file cannot exhaust an agent's context.
Disconnecting
Account → API keys → Connected applications disconnects every OAuth application at once. Every access and refresh token issued before that moment stops working immediately; an application that wants back in has to be authorised again. API keys are revoked individually on the same screen.
OAuth details
For anyone implementing a client rather than using one.
| Document | Purpose |
|---|---|
/.well-known/oauth-protected-resource |
Names this resource and its authorization server (RFC 9728) |
/.well-known/oauth-authorization-server |
Endpoints, scopes and PKCE support (RFC 8414) |
Neither is rate-limited, and both are readable without credentials.
| Scope | Grants |
|---|---|
pastes.read |
Read your pastes and the documents in them |
pastes.write |
Create, change and delete your pastes |
Scopes are declared per tool via securitySchemes, so a client knows which
tools need a linked account before calling one.
- Authorization code with PKCE,
S256only —plainis neither advertised nor accepted. - Clients identify themselves by Client ID Metadata Document: the
client_idis an HTTPS URL Markest fetches and checks, and the requestedredirect_urimust appear in it. - A client that cannot publish a document of its own can use one Markest publishes for it. For
mcp-remote, pass--client-metadata-url https://marke.st/.well-known/oauth-client-metadata/mcp-remote.jsonand the port3334, the one its document lists (http://localhost:3334/oauth/callback). As for any app on your own computer, a sign-in waiting on another local port is accepted too, at that address and path only. issis returned on every authorization response, success or error (RFC 9207).- The authorization request must carry
resourcenaming this endpoint; any other value is refused withinvalid_target. - Access tokens are signed JWTs carrying the resource as their audience, valid one hour, verified for signature, issuer, audience and expiry on every request.
- Refresh tokens last 30 days and rotate on each use. A refresh token presented again after its replacement has been used, or more than five minutes after it was replaced, ends the connection: two parties hold the same grant, and the application must be authorised again.
- Authorization codes are single-use and expire in five minutes; redeeming one twice ends the connection it produced.
Tokens are only ever issued after a signed-in person approves the request on Markest's consent screen. There is no client-credentials or machine-to-machine grant: every token belongs to someone.
ChatGPT specifics
A ChatGPT plugin is an MCP server — there is no separate manifest or OpenAPI document to install. Point ChatGPT at the endpoint and it discovers the rest.
If you change plans or Markest adds tools, refresh the connector: the tool list is captured when you install it, so a new conversation alone will not pick up new tools.
Encrypted artifacts: the local server
An artifact encrypted end to end is encrypted in the browser, or on your own machine, and its key never reaches Markest. This endpoint runs on Markest's servers, so it cannot open one.
The markest command includes a small MCP server that runs on your
machine and offers only the tools for encrypted artifacts:
markest_sealed_create, markest_sealed_read, markest_sealed_write,
markest_sealed_remove_documents, markest_sealed_link and
markest_sealed_keys. It encrypts and decrypts there, with keys your machine
keeps. Add it beside this endpoint; in Claude Code, once you have run
markest login:
claude mcp add markest-sealed -- markest mcp
What an agent reads or writes through these tools passes through its conversation, so the company that runs the agent's model sees it. Markest still never does.
See also
- Command line — the markest command, and its local MCP server for encrypted artifacts.
- API reference — the REST endpoints, for scripts rather than agents.
- Account → API keys — create a key, and disconnect connected applications.
- Account → Publishing — the confirmation gate for making pastes public.
Artifact and Sites permissions are independent for OAuth connections and API keys. If an older client requests only artifact access, choose “Include Sites permissions” on the authorization screen to open an explicit new request, then select only the permissions you want. Sites remains limited by the account package. A Sites-only agent can list workspaces and request access, but must wait for the account holder’s workspace approval; it receives no artifact permissions. Publication still needs browser approval.
OAuth tool discovery describes all supported artifact and Sites variants consistently, even before a particular permission is granted. Every invocation still checks the actual credential scopes, package, workspace approval and resource rights. Missing OAuth scopes return a standard authorization challenge. API-key discovery remains filtered by the key’s explicit permissions. After changing an older connection, refresh the plugin’s tools in the client to replace its cached executable schemas; reconnecting alone may not refresh them.