<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>dropthis blog</title><description>Guides, comparisons, and build logs on agent publishing — how AI agents and the people working with them put content on the web.</description><link>https://dropthis.app/blog</link><language>en</language><item><title>How AI Agents Publish to the Web: MCP, CLI, SDK, or API</title><link>https://dropthis.app/blog/how-ai-agents-publish-to-the-web/</link><guid isPermaLink="true">https://dropthis.app/blog/how-ai-agents-publish-to-the-web/</guid><description>MCP server, CLI, SDK, or raw REST — compare the four ways AI agents publish content to the web, with the auth models, setup costs, and failure modes of each.</description><pubDate>Fri, 12 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;An AI agent that can write HTML but can&apos;t put it anywhere is a typewriter without paper. The moment agents started generating reports, dashboards, games, and landing pages, &quot;how do AI agents publish to the web&quot; stopped being a hypothetical and became an architecture decision. There are four real answers — an MCP server, a CLI, an SDK, or a raw REST API — and we ship all four at &lt;a href=&quot;https://dropthis.app&quot;&gt;dropthis&lt;/a&gt;, so this comparison comes from running them in production, not from reading other people&apos;s docs.&lt;/p&gt;
&lt;h2&gt;What are the four ways an AI agent can publish to the web?&lt;/h2&gt;
&lt;p&gt;An agent can publish through an MCP server (tool calls inside a chat client), a CLI (shell commands inside a terminal agent), an SDK (typed library calls inside a codebase), or a REST API (direct HTTP from anywhere). All four can produce the same result — a live URL — but they differ sharply in auth, setup, and who the natural caller is.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Surface&lt;/th&gt;
&lt;th&gt;Natural caller&lt;/th&gt;
&lt;th&gt;Auth model&lt;/th&gt;
&lt;th&gt;Setup cost&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MCP server&lt;/td&gt;
&lt;td&gt;Claude, ChatGPT, Cursor, Windsurf&lt;/td&gt;
&lt;td&gt;OAuth 2.1 (remote) or env key (stdio)&lt;/td&gt;
&lt;td&gt;One connector entry&lt;/td&gt;
&lt;td&gt;Conversational publishing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CLI&lt;/td&gt;
&lt;td&gt;Claude Code, Codex, terminal agents&lt;/td&gt;
&lt;td&gt;API key in env&lt;/td&gt;
&lt;td&gt;One &lt;code&gt;npm install -g&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Repo and file workflows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SDK&lt;/td&gt;
&lt;td&gt;Your application code&lt;/td&gt;
&lt;td&gt;API key in env&lt;/td&gt;
&lt;td&gt;One dependency&lt;/td&gt;
&lt;td&gt;Products and pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;REST API&lt;/td&gt;
&lt;td&gt;Any language, any runtime&lt;/td&gt;
&lt;td&gt;Bearer key&lt;/td&gt;
&lt;td&gt;Zero dependencies&lt;/td&gt;
&lt;td&gt;Everything else&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The split matters because agents don&apos;t choose tools the way developers do. A chat agent picks whatever tools its client exposes; a terminal agent reaches for shell commands; a cron job calls whatever its language can import. You don&apos;t pick the best surface in the abstract — you pick the one that meets the agent where it already runs.&lt;/p&gt;
&lt;h2&gt;When should an agent publish over MCP?&lt;/h2&gt;
&lt;p&gt;Use MCP when the agent lives in a chat client. The &lt;a href=&quot;https://modelcontextprotocol.io/specification/2025-06-18&quot;&gt;Model Context Protocol&lt;/a&gt;, open-sourced by &lt;a href=&quot;https://www.anthropic.com/news/model-context-protocol&quot;&gt;Anthropic in November 2024&lt;/a&gt; and adopted by &lt;a href=&quot;https://openai.github.io/openai-agents-python/mcp/&quot;&gt;OpenAI&apos;s Agents SDK in March 2025&lt;/a&gt;, lets clients discover a server&apos;s tools at runtime — so a publish capability appears to the model as a native tool, with typed inputs and structured results.&lt;/p&gt;
&lt;p&gt;Two properties make MCP the right default for conversational agents:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Runtime discovery.&lt;/strong&gt; The client fetches tool definitions when it connects. When a server adds a verb — say, &lt;code&gt;update_settings&lt;/code&gt; next to &lt;code&gt;publish&lt;/code&gt; — every connected agent gets it without a software update. The tool description itself teaches the model when to use it, which is why well-written descriptions measurably reduce misuse.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Delegated auth.&lt;/strong&gt; Remote MCP servers authenticate with OAuth, so the user clicks &quot;approve&quot; once and no credential ever appears in the conversation. A scoped token (for example &lt;code&gt;drops:write&lt;/code&gt;) limits what a hijacked session can do. The trade-off is operational: someone has to run that OAuth server, handle dynamic client registration, and enforce scopes on every dispatch — we learned each of those the hard way building &lt;code&gt;mcp.dropthis.app&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;MCP&apos;s weakness is the terminal. A coding agent mid-task doesn&apos;t want a connector handshake; it wants a command that exits 0.&lt;/p&gt;
&lt;h2&gt;When does a CLI beat an MCP server?&lt;/h2&gt;
&lt;p&gt;A CLI wins whenever the agent already has shell access and the content already lives on disk. Terminal agents like Claude Code work in repositories — they build a static site, then run one command to put it online. Flags are deterministic, output is parseable, exit codes signal failure, and there&apos;s no server hop between the agent and the publish call.&lt;/p&gt;
&lt;p&gt;The pattern that makes CLIs agent-friendly is strict non-interactivity: every option a human would be prompted for must be a flag, because an agent can&apos;t answer an interactive prompt it can&apos;t see. Our own &lt;a href=&quot;https://docs.dropthis.app&quot;&gt;CLI contract&lt;/a&gt; treats any prompt in non-TTY mode as a bug.&lt;/p&gt;
&lt;p&gt;CLIs also compose with the rest of the shell. An agent that just generated 40 HTML reports can glob, loop, and publish them in one pass — something a chat tool-call loop does slowly and expensively.&lt;/p&gt;
&lt;h2&gt;Where do SDKs and the REST API fit?&lt;/h2&gt;
&lt;p&gt;SDKs and REST are the pipeline surfaces. When publishing happens on a schedule or inside a product — a nightly report generator, a CI artifact step, an app that gives every user a shareable page — there is no chat client and no terminal, just code. An SDK gives you types and retries; raw REST gives you zero dependencies in any language.&lt;/p&gt;
&lt;p&gt;The practical difference between them is small enough that the decision usually reduces to: use the SDK if it exists for your language, use REST if it doesn&apos;t. Either way the contract should be boring — one call in, one URL out:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { Dropthis } from &quot;@dropthis/node&quot;;

const client = new Dropthis(); // reads DROPTHIS_API_KEY from the environment
const drop = await client.drops.publish({
  content: html,
  title: &quot;Q2 revenue report&quot;,
});
console.log(drop.url); // live URL, ~1s later
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One number worth knowing when you wire this into an agent pipeline: payload limits. On dropthis, a free drop carries up to 5 MB and expires after 7 days; Pro raises that to 100 MB and permanent URLs. Agents that generate media-heavy pages hit size ceilings far sooner than humans do, so check limits programmatically instead of letting a 413 surprise your pipeline.&lt;/p&gt;
&lt;h2&gt;How does authentication differ across the four surfaces?&lt;/h2&gt;
&lt;p&gt;Remote MCP uses OAuth 2.1 with PKCE — the user approves once in a browser and the client holds a scoped token. Every other surface uses an API key read from the environment. The rule of thumb: interactive runtimes get OAuth so users never handle keys; non-interactive runtimes get keys because there is no user present to click &quot;approve.&quot;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Surface&lt;/th&gt;
&lt;th&gt;Credential&lt;/th&gt;
&lt;th&gt;Where it lives&lt;/th&gt;
&lt;th&gt;Revocation story&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Remote MCP&lt;/td&gt;
&lt;td&gt;OAuth 2.1 token, scoped&lt;/td&gt;
&lt;td&gt;Chat client&apos;s token store&lt;/td&gt;
&lt;td&gt;Disconnect the connector&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local (stdio) MCP&lt;/td&gt;
&lt;td&gt;API key&lt;/td&gt;
&lt;td&gt;MCP config env block&lt;/td&gt;
&lt;td&gt;Rotate the key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CLI&lt;/td&gt;
&lt;td&gt;API key&lt;/td&gt;
&lt;td&gt;Shell environment / keychain&lt;/td&gt;
&lt;td&gt;Rotate the key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SDK / REST&lt;/td&gt;
&lt;td&gt;API key&lt;/td&gt;
&lt;td&gt;Secret manager, CI secrets&lt;/td&gt;
&lt;td&gt;Rotate the key&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The &lt;a href=&quot;https://datatracker.ietf.org/doc/draft-ietf-oauth-v2-1/&quot;&gt;OAuth 2.1 draft&lt;/a&gt; matters here because it made PKCE mandatory — which is what makes it safe for chat clients to register dynamically and connect without a pre-shared secret. If you&apos;re evaluating any publish-capable MCP server, the two questions to ask: does it enforce scopes on every tool dispatch (not just at token mint), and can a token downscope but never upscope? Both have been real vulnerabilities in shipping MCP servers — including, briefly, ours.&lt;/p&gt;
&lt;h2&gt;The failure mode nobody designs for: ghost drops&lt;/h2&gt;
&lt;p&gt;The most common bug in agent publishing isn&apos;t auth or payload size. It&apos;s identity: an agent generates an artifact, publishes it, loses the returned id, then &quot;fixes a typo&quot; by publishing again. Result: five URLs for one document, four of them stale. We call these ghost drops, and before we redesigned our verbs around the problem, they were the dominant pattern in our production data.&lt;/p&gt;
&lt;p&gt;The fix is a vocabulary constraint, not a feature. &lt;code&gt;publish&lt;/code&gt; never takes an id and always creates; &lt;code&gt;update_content&lt;/code&gt; always takes an id and never creates. The publish response carries &lt;code&gt;next&lt;/code&gt; hints telling the agent exactly which verb to use for follow-ups. Since shipping that split across &lt;a href=&quot;/blog/pillars/agent-publishing/&quot;&gt;all our surfaces&lt;/a&gt;, repeat-publish loops dropped to near zero. If you&apos;re building any publish tool for agents, make creation and mutation impossible to confuse — agents follow the grammar you give them.&lt;/p&gt;
&lt;h2&gt;Which surface should you pick?&lt;/h2&gt;
&lt;p&gt;Match the surface to the runtime, not to taste:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Agent in a chat client&lt;/strong&gt; (Claude, ChatGPT, Cursor) → remote MCP with OAuth. Zero key handling for users.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agent in a terminal&lt;/strong&gt; (Claude Code, Codex) → CLI. Deterministic flags, shell composition.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scheduled or product code&lt;/strong&gt; → SDK in supported languages, REST anywhere else.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unsure&lt;/strong&gt; → start with REST; it&apos;s the substrate the other three are built on.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The deeper point: these aren&apos;t competing options, they&apos;re one capability that has to show up in four runtimes. An agent that publishes a report over MCP today may regenerate it from CI tomorrow — and if your publish layer doesn&apos;t keep ids, URLs, and verbs identical across surfaces, the handoff breaks. That consistency is the actual product. The transport is just plumbing.&lt;/p&gt;
</content:encoded><category>comparisons</category><category>agent publishing</category><category>mcp</category><category>cli</category><category>sdk</category><category>rest api</category><author>damian@dropthis.app (Damjan Malis)</author></item><item><title>How to Share Claude Artifacts at a URL You Control</title><link>https://dropthis.app/blog/how-to-share-claude-artifacts/</link><guid isPermaLink="true">https://dropthis.app/blog/how-to-share-claude-artifacts/</guid><description>Turn a Claude artifact into a permanent URL: get the HTML out of the chat, publish it over MCP or the CLI, and update it later without breaking the link.</description><pubDate>Fri, 12 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Claude artifacts are the fastest way to get a working page, game, or dashboard out of a conversation — and the slowest thing to get out of the chat window. If you want to share Claude artifacts with someone who wasn&apos;t in the conversation, you have two options: Claude&apos;s built-in share link, or publishing the artifact yourself at a URL you control. This tutorial covers the second path, and when it&apos;s worth the extra sixty seconds.&lt;/p&gt;
&lt;h2&gt;What are Claude artifacts, and when do you need your own link?&lt;/h2&gt;
&lt;p&gt;Artifacts are standalone outputs — HTML pages, React components, documents, diagrams — that Claude renders in a side panel (&lt;a href=&quot;https://www.anthropic.com/news/artifacts&quot;&gt;introduced by Anthropic in June 2024&lt;/a&gt;). Claude can share one at a public link, which is fine for show-and-tell. Publish it yourself when you need control: your domain, a password, updates in place, or an expiry you choose.&lt;/p&gt;
&lt;p&gt;The control argument in one table:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Built-in share link&lt;/th&gt;
&lt;th&gt;Published yourself&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;URL&lt;/td&gt;
&lt;td&gt;Anthropic&apos;s domain&lt;/td&gt;
&lt;td&gt;Your drop URL or custom domain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Update in place&lt;/td&gt;
&lt;td&gt;Re-share, new state&lt;/td&gt;
&lt;td&gt;Same URL, new content&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Password protection&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (Pro)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search-engine control&lt;/td&gt;
&lt;td&gt;Platform default&lt;/td&gt;
&lt;td&gt;Your &lt;code&gt;noindex&lt;/code&gt; choice&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Expiry&lt;/td&gt;
&lt;td&gt;Platform policy&lt;/td&gt;
&lt;td&gt;7 days free, permanent or scheduled on Pro&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mix multiple artifacts&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes — publish a folder&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;If none of those rows matter for the thing you just made, use the built-in link and stop reading. If one of them does, the rest takes about a minute.&lt;/p&gt;
&lt;h2&gt;How do you get an artifact out of the chat?&lt;/h2&gt;
&lt;p&gt;Open the artifact panel and either copy the code or download it as a file. HTML artifacts are self-contained — markup, styles, and script in one document — so what you copy is exactly what a browser needs. No build step, no dependencies, no framework runtime to install.&lt;/p&gt;
&lt;p&gt;Two practical notes from publishing a lot of these:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Copy beats screenshot-and-rebuild.&lt;/strong&gt; The artifact source is the deliverable. People rebuild artifacts in site builders far more often than they should.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;React artifacts need an export step.&lt;/strong&gt; Unlike HTML artifacts they assume a React runtime. Ask Claude to &quot;convert this artifact to a single self-contained HTML file&quot; first — it does this reliably, and the result publishes anywhere.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;How do you publish the artifact to a URL?&lt;/h2&gt;
&lt;p&gt;The fastest path never leaves the chat: with a publish MCP connector installed, ask Claude to publish the artifact and it calls the tool itself — content in, URL back as the tool result. With &lt;a href=&quot;https://docs.dropthis.app&quot;&gt;dropthis&apos;s connector&lt;/a&gt;, that&apos;s the &lt;code&gt;dropthis_publish&lt;/code&gt; tool over the &lt;a href=&quot;https://modelcontextprotocol.io/specification/2025-06-18&quot;&gt;Model Context Protocol&lt;/a&gt;, authenticated once with OAuth.&lt;/p&gt;
&lt;p&gt;From a terminal it&apos;s one command on the downloaded file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dropthis artifact.html --url
# → https://yourslug.dropthis.app/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Either way the response carries two things: the live URL and a drop id. The URL is for your audience. The id is for you — write it down next to the artifact, because it&apos;s the difference between updating the page and accumulating five stale copies of it. (Why ids and not URLs? See &lt;a href=&quot;/blog/how-ai-agents-publish-to-the-web/&quot;&gt;how agents keep publish and update straight&lt;/a&gt;.)&lt;/p&gt;
&lt;h2&gt;How do you update the page without changing the URL?&lt;/h2&gt;
&lt;p&gt;Pass the saved id to an explicit update verb: &lt;code&gt;update_content&lt;/code&gt; over MCP, or &lt;code&gt;dropthis drops update &amp;lt;id&amp;gt; artifact.html&lt;/code&gt; from the terminal. The URL your audience has keeps working; only the content changes. Publishing again instead would mint a second URL and strand everyone holding the first one.&lt;/p&gt;
&lt;p&gt;This is the step both humans and agents get wrong, which is why it&apos;s a &lt;a href=&quot;/blog/pillars/sharing-ai-work/&quot;&gt;whole topic of its own&lt;/a&gt; in agent publishing. The rule is short: &lt;strong&gt;publish once, update forever.&lt;/strong&gt; One artifact, one id, one URL for its whole life.&lt;/p&gt;
&lt;p&gt;If you need the page to behave differently — password it, hide it from search, hang it on your own domain — that&apos;s &lt;code&gt;update_settings&lt;/code&gt;, separate from content updates, so changing one never clobbers the other.&lt;/p&gt;
&lt;h2&gt;What about ChatGPT canvases and other AI output?&lt;/h2&gt;
&lt;p&gt;The same workflow applies to anything that produces HTML: ChatGPT canvases, Gemini outputs, agent-generated reports, or a folder your coding agent just built. Get the markup out, publish it, keep the id. The only thing that changes per platform is the export step — the publish-and-update loop is identical.&lt;/p&gt;
&lt;p&gt;That&apos;s the deeper point behind this tutorial: AI tools are converging on generating publishable things, and the missing piece is rarely generation quality — it&apos;s a stable URL with your name on it. Whether the generator is Claude, ChatGPT, or &lt;a href=&quot;/blog/how-ai-agents-publish-to-the-web/&quot;&gt;an autonomous agent publishing over MCP&lt;/a&gt;, the contract you want underneath is the same: one call in, one URL out, updates that never break links. The surface — MCP, CLI, SDK, or REST — is just the transport. The permanence is the product.&lt;/p&gt;
</content:encoded><category>tutorials</category><category>claude artifacts</category><category>sharing ai work</category><category>mcp</category><category>publishing</category><author>damian@dropthis.app (Damjan Malis)</author></item></channel></rss>