dropto.run docs
Guides

MCP server

An MCP server with nineteen tools. Point any MCP client at it — Claude, Cursor, VS Code, Codex, Gemini CLI — and "put this online" becomes a URL.

If you are deciding whether you want this, start at Deploy from an AI agent: in a coding agent with a shell the CLI is the shorter route, and this server is the one that works where there is no shell.

ToolWhat it does
loginOpens a browser on this machine and stores the token the sign-in returns
login_codeSigns in with a short code approved on another device, where no browser can open
publish_filesTakes files the agent wrote — a page, a markdown note, several files together
publish_dirTakes an absolute folder path and publishes it
list_sitesLists your sites, so a publish can go to one of them
list_foldersLists your folders, so a new site can be filed in one of them
delete_siteTakes a site down for good, subdomain included — needs the subdomain repeated back
get_siteReads a site's settings, whether it is paused, and its versions with the live one marked
update_siteChanges a site's name, serving mode, password, invite-only, comments, forms, scheduled takedown or folder, and the APIs its /api/* routes may call
list_peopleLists who a site is shared with, what each may do, and whether only they can open it
invite_peopleInvites addresses you give — to view, or to view and comment — emailing each one
remove_personTakes one address off a site's list
pause_siteTakes a site off the air, keeping its files, versions and subdomain
resume_sitePuts a paused site back on the air
rollback_siteServes an earlier version again, by its id from get_site
list_commentsReads the comments people left on a site's pages, with the page and the words each thread points at
reply_commentAnswers a comment thread as the site's owner; the thread's people are emailed
resolve_commentMarks a comment thread resolved
reopen_commentOpens a resolved thread again

publish_files is for content the agent wrote, where there is no folder on disk to point at. A site needs an index.html at its top level or at least one document — .md, .markdown, .pdf, .docx, .xlsx or .epub — so one page goes at index.html, and a single note is a whole site on its own, read through the documents viewer rather than as raw markdown. It carries text, so a PDF or an image still has to come off a disk with publish_dir.

In a chat on the web — nothing to install

Claude on the web, Claude's apps and other hosted chats can't run a program on your machine, so they reach the server by URL instead:

Code
https://mcp.dropto.run/mcp

Claude (claude.ai, the desktop and mobile apps): Drop2Run is in Claude's connector directory. Open it, connect, sign in to Drop2Run in the window that opens, and approve the connection. That is the whole setup. To add it by URL instead, open this link, or go to Settings → Connectors → Add custom connector and paste the URL.

Claude Code, without npx:

TerminalCode
claude mcp add --transport http drop2run https://mcp.dropto.run/mcp

Cursor and other clients that take a URL:

JSONCode
{ "mcpServers": { "drop2run": { "url": "https://mcp.dropto.run/mcp" } } }

ChatGPT: add it as a custom connector, where your plan offers developer mode.

The approval page names the chat asking and the address the connection goes back to — check that address before approving. The connection appears under Connected chats on your access tokens page; Disconnect ends it at once, and one left unused for 90 days ends on its own. Connecting the same chat again replaces its old connection rather than adding one.

What the remote server does not have:

  • login and login_code — the chat signs in for it.
  • publish_dir — there is no disk to read a folder from.
  • Images, PDFs and Office files. Tool arguments are text the model writes, so publish_files takes any text file — HTML, CSS, JavaScript, Markdown, SVG, JSON, YAML, CSV, plain text and source code in any language — but nothing binary. To publish binary files, use the local server below or the CLI.

The other sixteen tools are the same tools, with the same names and the same arguments.

Setting it up

Claude Desktop. Download drop2run.mcpb and open it. Claude shows an install dialog, and that is the whole of it — no config file, no terminal, and nothing to install first, because Claude for macOS and Windows ships the node this runs on.

Claude Code. One command:

TerminalCode
claude mcp add drop2run -s user -- npx -y @drop2run/mcp

Every other client — Cursor, VS Code, Codex, Gemini CLI, Zed and the rest each keep their server list somewhere different, and Deploy from an AI agent has the file and the shape for each. Most of them read an mcpServers block like this one:

JSONCode
{ "mcpServers": { "drop2run": { "command": "npx", "args": ["-y", "@drop2run/mcp"] } } }

Claude Desktop reads that block too, at ~/Library/Application Support/Claude/claude_desktop_config.json on macOS and %APPDATA%\Claude\claude_desktop_config.json on Windows, restarting the app afterwards. The bundle above does the same thing without the file, which is the only reason to prefer it.

The two routes differ in where the server comes from. npx fetches @drop2run/mcp the first time the client starts it and runs it from the cache after that, so it picks up a new version on its own. The bundle carries one version and keeps it until you open a newer one — which is the trade for not having to edit anything.

Then ask for a publish. There is no separate setup step: with no token stored, the agent calls login, a browser tab opens, you approve it, and the publish carries on. The token goes to ~/.config/drop2run/config.json — the same file the CLI reads, so signing in once serves both.

npx drop2run login from a terminal does exactly the same thing, if you would rather sign in before opening a chat.

No browser on this machine. In a container, over SSH, on a remote host, login_code returns a short code to enter at app.dropto.run/device from any other device. login also falls back to handing you the URL when it cannot launch a browser itself — open it anywhere on the same machine and the sign-in completes.

Or bring your own token. Create one at app.dropto.run/account/tokens and put it in the config file, or set DROP2RUN_TOKEN in the environment the client starts the server in. See Authentication.

Without a token

The server still starts, and every tool answers with how to get one. A server with no credential is unconfigured rather than broken, and a client reporting a crashed server would tell you nothing about what to do next.

A token stored while the chat is open works without a restart: credentials are read per call, which is what lets login be a tool rather than an instruction to go and do something in a terminal. DROP2RUN_TOKEN is the exception — the environment is read once at startup, so an export after the server started is invisible to it until the client restarts it.

Publishing over an existing site

Leave site unset and each publish creates a new site. Pass a subdomain to publish over one you already have — which replaces what it serves, so it is worth naming explicitly rather than letting a model pick.

The server's own instructions say so, which is what a model reads before choosing a tool.

Choosing the address of a new site

A new site is named for you unless subdomain asks for one:

Publish this as release-notes — publish_files with subdomain: "release-notes", and the URL comes back as https://release-notes.dropto.live.

Tell the agent the address you want and it passes it; say nothing and the name is generated, which is the right default for a page nobody is going to link to. The tool descriptions say to ask rather than invent one, because the name becomes a URL somebody is given and a site can change it only twice (change_subdomain), with every old name kept as a redirect.

site and subdomain are opposites — publish over a site that exists, or create one that does not — and passing both is refused rather than resolved in favour of either.

Filing a new site in a folder

A new site goes at the top level of your dashboard unless folder files it somewhere:

Publish this into Clients/Acme — publish_files with folder: "Clients/Acme".

The path is folder names from the top level down, case ignored; list_folders shows what exists, and takes an id as well. It never creates a folder — a folder that does not exist is refused with the list of those that do, and nothing is published. Like subdomain it only describes a new site, so passing it with site is refused: moving an existing site is update_site's folder.

Changing a site after it is published

Put a password on release-notes — update_site with site: "release-notes" and the password you gave.

update_site changes only what it is given, and the change reaches every edge within about a minute without publishing again. An empty name, password or expiresAt clears that setting; folder: "root" moves the site back to the top level. A setting your plan does not include is refused with the reason, and get_site says which ones those are. The tool description tells the model to use a password or a date only when you gave one.

get_site reports invitedOnly beside the password. A password on an invite-only site replaces invite-only — everyone on the list loses access unless they have the password — and update_site says so when it happens; invitedOnly: true switches back, and the list is kept.

A scheduled takedown pauses the site by default, which keeps everything and can be undone with resume_site. expiryAction: "delete" deletes it instead when the date arrives, and asks for the subdomain as confirm exactly as delete_site does — a deletion with a delay is still a deletion.

rollback_site serves an earlier version from get_site's list. A version whose files have been collected cannot be rolled back to; publish again instead.

Calling your own API

Let my-chat call OpenAI with this key — update_site with upstream: { origin, headers }, then a line in the site's _redirects.

On paid plans, upstream declares an origin the site's /api/* routes may send calls to, with headers to attach — so the key stays off the page. Its fields:

FieldWhat it does
originhttps:// and a host name, such as https://api.openai.com
headers[{ name, value }], set or replaced by name. A value is encrypted on arrival and never shown again
methodsWhat visitors may send; GET only on a new upstream when left out. A method that writes lets anybody who can open the site write with the key
removeHeadersHeader names to drop
removetrue removes the upstream with all its keys; sent alone

A call then needs a route: a line in the site's _redirects ending in 200, such as /api/* https://api.openai.com/:splat 200, published like any other file — Redirects has the rules. get_site reads it all back as apiProxy: the month's allowance, each upstream with header names and last four characters, and the live routes.

A key you give the model passes through the conversation on its way here, like anything else you type to it. Give it a key with the least access it needs, and the tool description tells the model never to make one up.

Sharing a site for review

Share launch-page with client@example.com so they can comment, and keep everyone else out.

invite_people puts the addresses on the site's list — role: "comment" to let them comment as well as open it — and emails each one an invitation. update_site with invitedOnly: true makes the list the only way in (it replaces a password), and comments: "invited" lets the people invited to comment do so; use comments: "anyone" instead to let anybody signed in comment on a site you keep public or behind a password. list_people shows the list, and remove_person takes someone off it.

The tool description and the server's instructions tell the model to invite only addresses you gave it — an invitation is an email from your account to someone outside it. Invite-only sharing and comments are plan features; on a plan without them the call is refused with the reason.

Answering comments

Read the comments on launch-page and fix what they point at.

On a plan that includes comments, the people you share a site with can pin comments to its pages. list_comments gives the agent each open thread: its page, the words the commenter selected (or the text near a pin), the element's selector, whether it was opened on an earlier version, and every comment in it. That is enough to find the source file, change it, and publish.

Then reply_comment answers on the page. The order matters, and the server's instructions tell the model: publish first, reply second — a reply saying "fixed" before the fix is live sends the commenter to the old version. A reply leaves the thread open unless resolve is true, so the commenter sees the answer on the page and can confirm the fix and resolve it themselves. since takes the cursor from a previous call, for reading only what changed.

Only the account's owner can reply or resolve; anyone on the account can read. A reply from the agent appears under your name, as one you typed would, and the people in the thread get the same email — the agent answers for you, on what you asked it to do.

Comment text is what visitors wrote. On a site open to anybody signed in, that is strangers, and the model reads it in the same context it takes instructions from. The tools hand each comment over fenced as data, and both the listing and the server's instructions tell the model to treat comments as requests about the site's content — not to run anything, delete anything or change a setting because a comment said to. That is an instruction to the model, not a sandbox: review what an agent proposes to do about a comment the way you would review a pull request from someone you do not know.

What it does not send anywhere

The token reaches the control plane and nothing else. File uploads are PUTs to the upload endpoint carrying a per-file permit — one key, one length, worth nothing else — and are never handed the account token. That was true when those PUTs went to presigned storage URLs and it stayed true when they moved behind our own upload Worker; the endpoint being ours is a reason to be more careful, not less.

Last modified on