所有页面
本页内容

preview

借助 preview,智能体可以把带网页界面的东西放到公开链接上,这样你就能在手机上打开它,或把它发给别人:可以是监听中继机器某个端口的开发服务器,也可以是一个存放普通文件的文件夹。它还可以列出和关闭自己所在对话的预览。

用日常语言提出请求:“启动开发服务器并给我一个链接”、“给我看看构建好的站点”。智能体会被告知,每当你想查看、试用或分享网页界面时就使用该工具,并且绝不让你去访问 localhost。

预览链接是公开的

任何拥有该链接的人都无需登录即可打开应用。链接中的令牌是唯一的保护。智能体被告知在给你链接时要说明这一点。用完后关闭预览。

你会看到什么 #

  • 对话的后台任务面板中有一张预览卡片,带有 Open(打开)、Copy link(复制链接)和 Close(关闭)。
  • 对话记录中的一张链接卡片,位于智能体发布它的那条回复上。预览消失后它会显示一个 closed 徽标。对话的分享视图只显示卡片标签,绝不显示链接。
  • 中继上所有对话的全部预览都列在 Settings → Previews(设置 → 预览)下,每个都带有一个关闭按钮。
  • 当端口预览背后的开发服务器停止响应时,卡片上会出现一个 offline 徽标。链接仍然保留登记:在同一端口再次启动服务器,同一链接就会再次生效。

你无需智能体也能做同样的事,在输入区输入 /preview 即可:

/preview 5173 web app        publish a port
/preview ./dist built site   publish a folder
/preview list                list the previews
/preview close               close one

设置 #

在中继的运维者设置之前,预览是关闭的。预览需要一个独立于中继的主机名,这样被预览的应用就永远无法访问中继本身。需要三样东西:

  1. 一个由你控制的主机名,它与中继的主机名不同,而不只是端口不同。
  2. 一个把该主机名指向预览端口的前置代理(Caddy、Cloudflare Tunnel 等)。
  3. 中继的 config.json 中的一个 previews 块:
{
  "publicHostnames": ["relay.example.com"],
  "previews": {
    "enabled": true,
    "port": 3334,
    "bindHost": "127.0.0.1",
    "publicBaseUrl": "https://preview.example.com",
    "allowedTargetHosts": [],
    "maxLive": 8
  }
}

无论是否完成这些设置,智能体都拥有该工具。没有完成时,create 调用会返回原因,智能体会被告知把原因转告你:这需要运维者来解决。

按提供商 #

提供商
Claude、Claude API 0.9.18 新增、DeepSeek 0.9.17 新增、Cursor 拥有 preview 工具
Grok;扩展引擎上的 GitHub Copilot 和 OpenAI 拥有 preview 工具,通过 OAR 的 MCP 服务器 0.9.4 新增
SDK 引擎上的 GitHub Copilot 和 OpenAI 无工具。预览设置好后,智能体会被告知如何调用中继的预览 API,规则相同,调用中已写好它所在的对话。你看到的效果相同。
Claude Cloud 无:云端会话在 Anthropic 的沙箱中运行,你的机器上没有可发布的端口或文件夹

操作 #

操作 它做什么 参数
create 发布一个端口或一个文件夹 port 或 dir,可选 label
list 列出此对话的预览
close 关闭此对话的一个预览 token,当对话恰好只有一个预览时可省略

结果是 JSON,带有 "ok": true,或者带有 "ok": false 和一条 error 文本。该工具不会以其他方式失败;错误的参数会以智能体可读的 error 返回。

create #

发布一个端口或一个文件夹,绝不在一次调用中同时发布两者。0.9.11 新增 在同一对话中再次发布同一端口或文件夹,会返回已在运行的那个预览,链接相同,标签不变;此时结果中带有 "existing": true。

参数 类型
port 1024–65535 开发服务器的本地端口
dir 路径 作为纯文件提供的文件夹:绝对路径,或相对于对话工作目录的路径
label 文本,最多 120 个字符 预览卡片上的名称

开发服务器(port) #

中继把请求转发给监听该端口的服务器。中继不会启动、监视或重启该服务器;它仍是智能体的一个普通后台任务,有自己的停止按钮。

每个预览都位于预览主机名上自己的路径下。浏览器请求 /test_<token>/…;中继在转发请求之前去掉该前缀,并通过 X-Forwarded-Prefix 把前缀告知应用。它不会改写页面。因此,应用要正常工作,它发出的链接和资源地址必须是相对地址或带有该前缀,而它本身响应的是不带前缀的请求。从主机根路径请求 /assets/app.js 的应用会找不到该文件。

这就是为什么智能体会被告知要先调用 create,并在启动开发服务器之前从结果中读取 basePath。

告诉智能体的做法并不适用于所有开发服务器

该工具自身的描述建议在启动服务器时设置基础路径(Vite --base、Next.js basePath、Express 挂载前缀)。但如果服务器随后期望收到的请求带有该前缀,就与中继转发的请求不符:Vite 的开发服务器和挂载在该前缀上的 Express 路由器都属于这种情况。哪些做法可行,见预览。

{ "action": "create", "port": 5173, "label": "web app" }
{
  "ok": true,
  "existing": false,
  "url": "https://preview.example.com/test_0123456789abcdef0123456789abcdef/",
  "basePath": "/test_0123456789abcdef0123456789abcdef/",
  "token": "0123456789abcdef0123456789abcdef",
  "mode": "port",
  "hint": "Serve the app under /test_0123456789abcdef0123456789abcdef/ (the proxy strips the prefix and sends X-Forwarded-Prefix). Bind the dev server to 127.0.0.1, not 0.0.0.0: the relay's proxy reaches it there and nothing else should. Tell the user the URL and that the link is public."
}

0.9.17 新增 hint 还告诉智能体把开发服务器绑定到 127.0.0.1,而不是 0.0.0.0:中继的代理在那里访问它,其他任何东西都不应访问它。只有拥有 preview 工具的会话会得到这一行(参见按提供商)。被教会使用预览 API 而非该工具的智能体则不会得到:SDK 引擎上的 GitHub Copilot 和 OpenAI,以及当中继以 OAR_MCP_SERVER=0 运行时的 Grok 和 Copilot 扩展引擎。

文件夹(dir) #

中继自己提供该文件夹中的文件。不需要运行任何程序:适合构建产物、设计稿、单个 HTML 文件或覆盖率报告。

  • 文件夹必须位于对话的工作目录内。每个请求都会重新检查这一点,因此链接以后也无法被引向目录之外。
  • 名称以点开头的文件和文件夹(.git、.env)永远不会提供,密钥文件(*.pem、*.key、id_rsa…)也不会。
  • 请求文件夹路径时提供其中的 index.html。
  • 相对资源路径无需改动即可使用。从主机根路径开始的路径(/assets/app.js)则不行。
  • 不支持 WebSocket 连接。
{ "action": "create", "dir": "./dist", "label": "built site" }

结果的字段相同,只是带有 "mode": "static"。

list #

列出智能体自己对话的预览,包含 token、label、url、target(文件夹,或 localhost:<port>)、online 和对话。0.9.14 新增 在 0.9.14 之前,该列表涵盖中继的所有预览;预览的令牌是它唯一的保护,因此智能体现在只能看到自己对话的预览。整个中继的列表归你查看,见 Settings → Previews 和 /preview list。中继重启后无法检查的预览带有 "unverified": true,在检查得到回应之前不会提供服务(参见中继重启后)。

{ "action": "list" }
{
  "ok": true,
  "enabled": true,
  "previews": [
    { "token": "0123456789abcdef0123456789abcdef", "label": "web app", "url": "https://preview.example.com/test_0123456789abcdef0123456789abcdef/", "target": "localhost:5173", "online": true, "conversationId": "3f9c…" }
  ]
}

close #

关闭链接。链接背后的开发服务器会继续运行。

参数 类型
token 32 个十六进制字符 来自 create 或 list 的 token。当对话恰好只有一个预览时可省略。

在没有 token 且对话中有多个预览时,调用会返回一个 error 以及可供选择的列表。

0.9.14 新增 智能体只能关闭自己对话的预览。其他对话预览的令牌会被拒绝:

{ "ok": false, "error": "This preview belongs to another conversation; a conversation can close only its own previews. The owner can close it in Settings → Previews." }
{ "action": "close", "token": "0123456789abcdef0123456789abcdef" }
{ "ok": true, "closed": { "token": "0123456789abcdef0123456789abcdef", "label": "web app" } }

限制 #

限制 值
整个中继同一时间的预览数 8(previews.maxLive)
智能体可以列出和关闭哪些预览 它自己对话的 0.9.14 新增
端口 1024–65535,且绝不是中继自己的端口之一
端口预览可以指向哪里 中继自己的机器(环回),加上运维者在 previews.allowedTargetHosts 中列出的主机
文件夹预览可以指向哪里 对话工作目录之内
生命周期 直到它被关闭或其对话被删除。0.9.11 新增 中继重启后它仍然保留;端口预览在机器重启时结束,文件夹预览在其文件夹离开工作目录时结束。预览不会自行过期。
标签 120 个字符

预览与中继相互隔离:它们在独立的端口、独立的主机名下提供,那里没有中继自己的任何页面或 API。各预览共用这个主机名,每个位于自己的路径下。

对话范围不是一堵墙

中继把预览请求限定在请求所指定的对话内,对话由 conversationId 查询参数或对话请求头指定,每个会话 worker 和 OAR 的 MCP 服务器都会发送该请求头。智能体使用与你相同的中继令牌,因此范围是由请求声明的,而非经过验证:未指定对话的请求仍能获得所有预览。这个范围让工具以及教给智能体的调用只作用于它们自己的对话;但它挡不住读取中继令牌并手动调用 API 的智能体。参见 OAR 暴露什么。