Give your AI coding agent a publish-HTML button (with MCP)

Give your AI coding agent a publish-HTML button (with MCP)

为你的 AI 编程助手添加一个“发布 HTML”按钮(基于 MCP)

Your coding agent writes HTML all day. A quick dashboard to eyeball some data. A PR writeup with a rendered diff. A status report, a Mermaid diagram, a one-off internal tool. Then what? You screenshot it into Slack, paste it into a gist, or spin up a Vercel project for a file you will delete tomorrow. There is a verb missing from the agent toolbox: publish. Not deploy. Publish. One file in, one private URL out. This post is about how to add that verb to any MCP-compatible agent, why “private by default” matters more than it sounds, and what the publish primitive looks like in practice.

你的编程助手整天都在编写 HTML。可能是为了快速查看数据的仪表盘、带有渲染差异的 PR 文档、状态报告、Mermaid 图表,或者是一个一次性的内部工具。然后呢?你把它截图发到 Slack,粘贴到 Gist,或者为了一个明天就会删除的文件去部署一个 Vercel 项目。在助手的工具箱里,缺少了一个动词:发布(Publish)。不是部署(Deploy),而是发布。输入一个文件,输出一个私有 URL。本文将介绍如何为任何兼容 MCP 的助手添加这个功能,为什么“默认私有”比听起来更重要,以及这种发布原语在实践中是什么样子的。

The shape of the problem

问题的本质

A deploy is a project. It expects a repo, a build, a config, a domain. That is the right amount of ceremony for a product and far too much for the artifact-shaped output an agent emits dozens of times a session. What an agent actually needs is closer to a paste-bin with teeth: It writes a file. It gets back a URL. No project, no build step. The URL is private by default, because agent output routinely embeds real data: API responses, customer rows, the prompt context itself. When the agent revises the artifact, the same URL updates in place. You do not want 30 links from 30 iterations.

部署是一个项目。它需要仓库、构建过程、配置和域名。对于一个产品来说,这些流程是必要的,但对于一个助手在一次会话中产生几十次的“制品”输出来说,这太繁琐了。助手真正需要的是一个更强大的“粘贴板”:它写入一个文件,然后得到一个 URL。没有项目,没有构建步骤。该 URL 默认是私有的,因为助手的输出通常包含真实数据:API 响应、客户数据行,甚至是提示词上下文本身。当助手修改制品时,同一个 URL 会原地更新。你肯定不希望因为 30 次迭代而产生 30 个链接。

The Model Context Protocol (MCP) is the clean way to expose exactly this.

模型上下文协议 (MCP) 是实现这一功能的简洁途径。

If you have not used MCP, the one-line version: it is the standard every major AI assistant now speaks for calling tools, so a tool you expose once works in Claude Code, Cursor, Codex, Claude.ai, and the rest. (Longer primer: https://stacktr.ee/blog/mcp-servers-explained-for-developers)

如果你还没用过 MCP,简单来说:它是目前主流 AI 助手用于调用工具的标准协议。因此,你只需配置一次工具,它就能在 Claude Code、Cursor、Codex、Claude.ai 等所有平台中通用。(详细入门指南:https://stacktr.ee/blog/mcp-servers-explained-for-developers)

The publish tool, conceptually

发布工具的概念

A publish-oriented MCP server needs one core tool and a few supporting ones. In MCP terms the action is a tool (model-controlled), the list of what you have published is a resource (the client reads it), and a saved “publish this privately” flow is a prompt (the user invokes it). If that tools/resources/prompts split is new to you, here is the breakdown: https://stacktr.ee/blog/mcp-resources-vs-tools-vs-prompts

一个面向发布的 MCP 服务器需要一个核心工具和几个辅助工具。在 MCP 的术语中,动作是一个工具(由模型控制),已发布内容的列表是一个资源(由客户端读取),而保存的“私有发布”流程是一个提示词(由用户调用)。如果你对工具/资源/提示词的划分感到陌生,可以参考这里:https://stacktr.ee/blog/mcp-resources-vs-tools-vs-prompts

The tool call itself is mundane, which is the point: // the agent calls: publish_html({ file: “dashboard.html” }) // it gets back: { “url”: “https://stacktr.ee/p/3f9a2c7b1e”, “expires”: “24h” } That is the whole interaction. The agent hands you a link. You send it to a teammate, who opens it with no account and no login.

工具调用本身非常简单,这正是重点所在: // 助手调用:publish_html({ file: “dashboard.html” }) // 返回结果:{ “url”: “https://stacktr.ee/p/3f9a2c7b1e”, “expires”: “24h” } 这就是整个交互过程。助手给你一个链接,你发给队友,他们无需账号、无需登录即可打开。

Why “private by default” is the real feature

为什么“默认私有”才是真正的核心功能

Public-by-default hosting made sense when humans hand-published finished pages. It is the wrong default for machine-generated HTML, because the agent does not pause to ask whether this particular file contains anything sensitive. It usually does. Private by default flips the burden. The URL is unguessable, so the link itself is the credential. There is no directory listing, no search indexing, no crawl. If you need more, you add a layer: a shared password, or an email-domain gate so only @yourco.com addresses can open it. For the genuinely sensitive case, end-to-end encryption keeps the host from ever seeing plaintext. None of that requires the agent to make a judgment call. The safe thing is the default thing.

当人类手动发布成品页面时,默认公开的托管方式是有意义的。但对于机器生成的 HTML 来说,这是错误的默认设置,因为助手不会停下来询问该文件是否包含敏感信息——而它通常确实包含。默认私有改变了这种负担。URL 是不可猜测的,因此链接本身就是凭证。没有目录列表,没有搜索引擎索引,没有爬虫。如果你需要更多保护,可以增加一层:共享密码,或者电子邮件域名限制(例如仅限 @yourco.com 地址打开)。对于真正敏感的情况,端到端加密可以确保托管方永远看不到明文。所有这些都不需要助手去进行判断。安全,应该是默认选项。

Try it in one command

一条命令即可尝试

I build Stacktree, which is one implementation of this primitive: an MCP server for publishing HTML, private by default. Wiring it into your agent is a single command: npx stacktree-install. It detects Claude Code, Cursor, Codex, OpenCode, and Amp, writes the MCP config for each, and the publish tools show up in your agent automatically. The first publish is anonymous and the link lives 24 hours, so you can prove it out before you even make an account. It is MIT-licensed on the client side and listed in the official MCP registry as ee.stacktr/publish. Source for the server package: https://github.com/stevysmith/stacktree-mcp

我开发了 Stacktree,这是实现该原语的一种方式:一个用于发布 HTML 的 MCP 服务器,默认私有。将其集成到你的助手中只需一条命令:npx stacktree-install。它会自动检测 Claude Code、Cursor、Codex、OpenCode 和 Amp,并为每个工具写入 MCP 配置,发布工具会自动出现在你的助手中。首次发布是匿名的,链接有效期为 24 小时,因此你可以在注册账号前先进行测试。它在客户端采用 MIT 协议,并已在官方 MCP 注册表中列出,名称为 ee.stacktr/publish。服务器包源码:https://github.com/stevysmith/stacktree-mcp

The broader point

更深层的意义

Whether or not you use Stacktree, the pattern is worth internalizing: as agents generate more artifacts, the missing infrastructure is not more compute or a fancier framework. It is small, sharp primitives that fit the agent loop. Publish is one of them. A file goes in, a private link comes out, and the agent can replace it in place when it iterates. Happy Building!

无论你是否使用 Stacktree,这种模式都值得内化:随着助手生成越来越多的制品,我们缺失的基础设施不是更多的算力或更花哨的框架,而是适合助手循环的小巧、精准的原语。“发布”就是其中之一。输入一个文件,输出一个私有链接,并且助手可以在迭代时原地替换它。祝开发愉快!