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.
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
| 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_add_document |
write | Append a document to an existing paste |
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_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 |
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_delete_paste |
write | Delete a paste and everything in it |
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.
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.
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.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./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.
See also
- 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.