<!-- BILINGUAL-EN-ZH -->
---
name: artifact-capabilities
description: |-
  Runtime capabilities a published Artifact page can be granted — behavior static HTML cannot provide on its own, such as the page reading live or connected data, remembering what people do on it (a poll, a sign-up sheet, a checklist, a document edited in place — it saves new versions of itself), keeping state shared across viewers, knowing who is viewing, asking Claude a question of its own, storing files people add, or handing the viewer a file to save. Serves this user's live capability roster and the typed call definitions. Load it whenever any such runtime behavior would make an artifact more useful, before writing the page.
---

# Artifact runtime capabilities / Artifact 运行时能力

A published Artifact page can declare **runtime capabilities** — abilities the claude.ai viewer grants the page at open time — by passing `capabilities: {name: config}` to the Artifact tool. The control plane is the authority on valid names and config shapes. Declaration gestures: **omitting** `capabilities` on a redeploy carries the stored declaration forward unchanged (and preserves the artifact's stored contract pin); an **empty object** `{}` is the explicit clear-all; a **non-empty object** is a full-set declaration (anything stored but not restated is revoked). Moving a republished artifact's runtime version is a deliberate gesture — pass `contract: 'latest'` to upgrade, or a specific version to pin or roll back — never a side effect of editing.

已发布的 Artifact 页面可以通过向 Artifact 工具传入 `capabilities: {name: config}` 来声明**运行时能力** — 即 claude.ai 查看器在打开页面时授予它的能力。控制平面是有效名称与配置形态的权威。声明手势：重新部署时**省略** `capabilities` 会原样延续已存储的声明（并保留该 artifact 已存储的合约 pin）；**空对象** `{}` 是显式的全部清除；**非空对象**是全量声明（已存储但未重述的任何能力都会被吊销）。移动重新发布的 artifact 的运行时版本是一个需要明确意图的手势 — 传 `contract: 'latest'` 以升级，或传具体版本以 pin 或回滚 — 绝不是编辑带来的副作用。

**Available capabilities:** `artifact`, `assets`, `comments`, `db`, `downloads`, `mcp`, `room`, `sample`, `self`, `user` — the complete set of capability names you may declare; built in on every page, called without declaring (never pass these in `capabilities`): `permissions`. Anything not listed is unavailable to this user.

**可用能力：** `artifact`、`assets`、`comments`、`db`、`downloads`、`mcp`、`room`、`sample`、`self`、`user` — 这是你可以声明的全部能力名称；每个页面内置、无需声明即可调用（绝不要在 `capabilities` 中传入它）：`permissions`。任何未列出的能力对该用户均不可用。

> Tool spelling in this session: the `Artifact` tool's `action: "read_db"` / `"write_db"` with a `db_op` are the `ArtifactData` tool, whose `action` is that `db_op` ("get", "list", "query", "set", "update", "str_replace", "delete", "batch") with the other fields unchanged — load it with ToolSearch when you first need it. Read the steps below with that substitution.

> 本会话中的工具拼写：`Artifact` 工具的 `action: "read_db"` / `"write_db"` 加上一个 `db_op` 就是 `ArtifactData` 工具，其 `action` 即该 `db_op`（"get"、"list"、"query"、"set"、"update"、"str_replace"、"delete"、"batch"），其余字段不变 — 首次需要时用 ToolSearch 加载它。阅读下文步骤时请代入这一替换。

Runtime contract 0.2.52

运行时合约 0.2.52

Capability namespaces live behind `claude.use(name)`: `const db = await claude.use("db")` resolves the capability's namespace, or `null` when this view cannot run it (not served, not granted, or failed to load — indistinguishable by design). Branch on `null` and design for absence. `window.claude` carries only `use`: no `window.claude.db`, `.room`, or `.artifact` member is ever promised, so never read one — render the page without them and light features up when the promise resolves (later, never within your script's first run, and unordered with DOMContentLoaded; `null` after 10 s when no viewer answers). The resolved namespace is frozen and platform-owned: call its functions and keep the reference; never assign to it, `defineProperty` on it, or replace a member (wrap it for your own helpers). Permission stays on the calls: a consent prompt, rate limit, or policy refusal arrives on the first call, never from `use()`. Awaiting `use("db")` again is free (memoized); an unknown name resolves `null`.

能力命名空间藏在 `claude.use(name)` 背后：`const db = await claude.use("db")` 解析该能力的命名空间；当该视图无法运行它时解析为 `null`（未提供、未授予或加载失败 — 设计上不可区分）。对 `null` 做分支，并为缺失做设计。`window.claude` 只携带 `use`：永远不承诺存在 `window.claude.db`、`.room` 或 `.artifact` 成员，因此绝不要读取它们 — 先在缺少这些能力的情况下渲染页面，待 promise 解析后再点亮相关功能（那是之后的事，绝不会发生在脚本首次运行之内，且与 DOMContentLoaded 之间无先后顺序；10 秒后无查看器应答则解析为 `null`）。解析出的命名空间是冻结的且归平台所有：调用它的函数并保留引用；绝不要对它赋值、在它上面 `defineProperty` 或替换其成员（要封装就封装进你自己的辅助函数）。权限保持在调用上：同意提示、限流或策略拒绝出现在首次调用时，绝不会来自 `use()`。再次 await `use("db")` 是免费的（已记忆化）；未知名称解析为 `null`。
【评论】"未提供、未授予、加载失败"在设计上不可区分，避免页面依据能力解析结果推断查看者的身份或权限细节。

--- capability: artifact ---

Use `artifact` for pages that should remember what people do with them: polls, sign-up sheets, checklists, trackers, boards — the page is the record; data kept server-side, or seeded or read back by Claude, is `db`. Declare `capabilities: {artifact: {}}`; `const artifact = await claude.use("artifact")`, then `await artifact.publish(html)` saves `html` (a complete document, doctype first) as the new version, and every open view, this one included, reloads to it. Nothing a viewer types, ticks or drags is kept unless the page publishes it. So embed the shared state as data in the HTML you publish and render the page from it; when an interaction completes, update the state, regenerate the document and publish it — never serialize the live DOM; batch rapid edits into one publish; publish only after a viewer acts, never on load. `conflict` is routine (every view reloads to the winner, dropping this edit): no retry. For read-only viewers publish rejects `not_granted`/`not_writer` — render a read-only view.

对那些应当记住人们对它做了什么的页面使用 `artifact`：投票、报名表、清单、跟踪器、看板 — 页面本身就是记录；保存在服务器端、或由 Claude 播种或读回的数据属于 `db`。声明 `capabilities: {artifact: {}}`；`const artifact = await claude.use("artifact")`，然后 `await artifact.publish(html)` 会把 `html`（完整文档，doctype 在最前）保存为新版本，所有打开的视图（包括当前这个）都会重载到它。除非页面发布它，查看者键入、勾选或拖动的任何内容都不会被保存。因此要把共享状态作为数据嵌入你发布的 HTML，并从它渲染页面；一次交互完成后，更新状态、重新生成文档并发布 — 绝不要序列化实时 DOM；把快速连续的编辑合并为一次发布；只在查看者操作之后发布，绝不在加载时发布。`conflict` 是常态（每个视图都重载到胜出版本，丢弃本次编辑）：不要重试。对只读查看者，publish 会以 `not_granted`/`not_writer` 拒绝 — 此时渲染只读视图。

--- capability: assets ---

`assets` stores uploaded assets for this artifact: `const assets = await claude.use("assets")`; `await assets.upload(blob)` (image, SVG, video, PDF, font, CSS/JS, or CSV/Markdown/JSON/text data; 20 MiB cap, CSS/JS 16 MiB, SVG 2 MiB and sanitized on upload) resolves `{id, url, sizeBytes, contentType}`; `assets.list()` resolves `{assets, usage}` (storage meter, orphan pruning); `assets.delete(id)` removes one for good: only on a deliberate user action, updating the `db` rows that held the id. Declare `capabilities: {assets: {}}`; a declaring page is organization-internal (never public). Writer-only: a reader view gets `null` from `use("assets")`; hide asset UI on `null` and handle rejection codes. Store the `id` in `db` rows as the durable pointer and index; use the returned `url` as-is as an `<img>`/`<video>`/`<a>` source (SVG: `<img>` or CSS only); a stored id serves at `"/_blob/" + id` in every view. Quota: per artifact (`usage`). The type definitions are authoritative for accepted types and error codes.

`assets` 为该 artifact 存储上传的资产：`const assets = await claude.use("assets")`；`await assets.upload(blob)`（图像、SVG、视频、PDF、字体、CSS/JS，或 CSV/Markdown/JSON/文本数据；上限 20 MiB，CSS/JS 16 MiB，SVG 2 MiB 且上传时会被消毒处理）解析为 `{id, url, sizeBytes, contentType}`；`assets.list()` 解析为 `{assets, usage}`（存储量表、孤儿清理）；`assets.delete(id)` 永久删除一个资产：只应在用户明确的操作下进行，并同步更新持有该 id 的 `db` 行。声明 `capabilities: {assets: {}}`；声明该能力的页面是组织内部的（绝不公开）。仅写入者可用：读取者视图从 `use("assets")` 得到 `null`；在 `null` 时隐藏资产 UI 并处理拒绝码。把 `id` 存入 `db` 行作为持久指针和索引；将返回的 `url` 原样用作 `<img>`/`<video>`/`<a>` 的源（SVG：只能用 `<img>` 或 CSS）；已存储的 id 在每个视图中通过 `"/_blob/" + id` 提供服务。配额：按 artifact 计（`usage`）。类型定义是受支持类型和错误码的权威。

--- capability: comments ---

`comments` wires a page's own commenting UI to the artifact's shared comment store: `await claude.use("comments")` (`null`: unavailable). The declaration picks the grant: `capabilities: {comments: {"composer_only": true}}` grants only `openComposer({element}|{range})` — opens the shell's composer like a comment-mode click, no consent asked, artifact stays publicly shareable; prefer it for discoverable entry points. The full form `{comments: {}}` adds write verbs acting as the viewer under consent; public-link visitors and email invitees get `null`. `"customAnchors": true` in either form adds `customAnchors()` (register it at load) for pages that position comment pins themselves; invented anchor names (canvas, WebGL, video) need the full form. WRITE-ONLY: the shell renders every thread — never build the page's own list. Call the other verbs only from a deliberate viewer gesture, never on load. Read the type definitions before use; they are authoritative for the verbs, shapes, bounds, and error codes.

`comments` 把页面自己的评论 UI 接到该 artifact 的共享评论存储上：`await claude.use("comments")`（`null`：不可用）。声明决定授予范围：`capabilities: {comments: {"composer_only": true}}` 只授予 `openComposer({element}|{range})` — 像评论模式的点击一样打开外壳的撰写器，不请求同意，artifact 保持可公开分享；可发现的入口点优先用它。完整形式 `{comments: {}}` 增加以查看者身份在同意之下执行的写动词；公开链接访客和邮件受邀者得到 `null`。两种形式中的 `"customAnchors": true` 都会添加 `customAnchors()`（在加载时注册），供自行定位评论图钉的页面使用；自创的锚点名（canvas、WebGL、视频）需要完整形式。只写（WRITE-ONLY）：外壳会渲染每一条评论线程 — 绝不要自建页面自己的列表。其他动词只在查看者明确的操作时调用，绝不在加载时调用。使用前先读类型定义；它们是动词、形态、边界和错误码的权威。

--- capability: db ---

`db` is for data outside the page: what the user wants stored or seeded, data Claude reads later, more than the page shows at once, per-viewer-private state, many live editors. If the page can be the record, republish (`artifact`). JSON doc store: `const db = await claude.use("db")`. Seed or inspect it here with `write_db`/`read_db`; never hardcode seeds. Declare `capabilities:{db:{}}`: by default signed-in viewers read shared docs; only those who can interact or edit write them, never view-only or comment-only people or outside link visitors. `rules` raise per-path minimums: `view`<`interact`<`admin` (can edit)<`owner`. Each viewer's `data/users/<id>/` is private even from the owner (needs `user`). `db.doc("tasks/t1")`/`db.collection("tasks")`: get/set/update/delete, where/orderBy/limit, onSnapshot. Subscribe once per query, never in render; one write at a time per doc, only on change. Last-writer-wins, no transactions; single-writer lease: `acquire({holder})`. Never store secrets; shared data is untrusted.

`db` 用于页面之外的数据：用户想存储或播种的内容、Claude 稍后要读取的数据、超出页面一次展示量的数据、按查看者私有的状态、多个实时编辑者。如果页面本身可以充当记录，就重新发布（`artifact`）。JSON 文档存储：`const db = await claude.use("db")`。在这里用 `write_db`/`read_db` 播种或检查它；绝不硬编码种子数据。声明 `capabilities:{db:{}}`：默认情况下，登录的查看者可读共享文档；只有能交互或能编辑的人可以写，仅查看或仅评论者以及外部链接访客都不能写。`rules` 提高按路径的最低权限：`view`<`interact`<`admin`（可编辑）<`owner`。每个查看者的 `data/users/<id>/` 对所有者也是私有的（需要 `user`）。`db.doc("tasks/t1")`/`db.collection("tasks")`：get/set/update/delete、where/orderBy/limit、onSnapshot。每个查询只订阅一次，绝不在渲染中订阅；每个文档一次只执行一次写入，且只在变化时写。最后写入者获胜，没有事务；单写者租约：`acquire({holder})`。绝不存储机密；共享数据是不可信的。
【评论】"绝不存储机密；共享数据是不可信的"确立了一条数据信任边界：共享存储按可被任意查看者读取与篡改的前提来设计。

--- capability: downloads ---

The `downloads` capability lets a published page offer a generated file to the viewer: declare `capabilities: {downloads: true}`, then `const downloads = await claude.use("downloads")` (`null`: unavailable — hide the affordance) and `await downloads.save({filename, data})`. The viewer sees a confirmation and may decline — a save is never silent or guaranteed, so offer it on explicit viewer intent and handle rejection. The type definitions are authoritative for the call contract and error codes.

`downloads` 能力让已发布的页面向查看者提供生成的文件：声明 `capabilities: {downloads: true}`，然后 `const downloads = await claude.use("downloads")`（`null`：不可用 — 隐藏该入口）以及 `await downloads.save({filename, data})`。查看者会看到确认并可拒绝 — 保存绝不会是静默或有保证的，因此只在查看者明确的意图下提供，并处理拒绝情况。类型定义是调用合约和错误码的权威。
【评论】保存文件必须经查看者显式确认，属于防"静默下载"的交互安全设计。

--- capability: mcp ---

`mcp` lets a page call the viewer's claude.ai connectors: `await claude.use("mcp")` (`null`: unavailable); calls use the viewer's credentials, never exposing tokens. Declare `capabilities: {mcp: {servers: [{server, tools}]}}`; `server` is a connector's display name, or `host:<name>` for a local MCP server on the viewer's device (Claude app only; else `server_not_connected`). Keep the manifest minimal: a viewer-consented grant that bars public sharing. Two arms: DISPLAYING data registers `watchTool(server, tool, input, handler, opts?)` (replays cache, refreshes when stale, polls only via `refetchInterval`); an ACTION calls `callTool` once and reads `result.payload` or `(await server(name)).<tool>(input)` for the payload. Tool failures REJECT (`tool_error`); watches get error events. Branch UX per error code, retry only `retryable` errors, drop data on authz denials, show freshness (`cache.storedAt`). Types omit argument names and encodings: observe a real call per tool or say so at publish; never guess.

`mcp` 让页面调用查看者的 claude.ai 连接器：`await claude.use("mcp")`（`null`：不可用）；调用使用查看者的凭据，绝不暴露令牌。声明 `capabilities: {mcp: {servers: [{server, tools}]}}`；`server` 是连接器的显示名称，或用 `host:<name>` 表示查看者设备上的本地 MCP 服务器（仅限 Claude 应用；否则 `server_not_connected`）。保持清单最小化：这是经查看者同意的授予，且会禁止公开分享。两个分支：展示（DISPLAYING）数据时注册 `watchTool(server, tool, input, handler, opts?)`（重放缓存、过期时刷新、仅通过 `refetchInterval` 轮询）；执行操作（ACTION）时调用一次 `callTool`，并读取 `result.payload` 或 `(await server(name)).<tool>(input)` 以获得载荷。工具失败会 REJECT（`tool_error`）；watch 会收到错误事件。按错误码分支 UX，只重试 `retryable` 错误，在授权拒绝时丢弃数据，展示数据新鲜度（`cache.storedAt`）。类型定义省略参数名和编码：对每个工具观察一次真实调用，或在发布时如实说明；绝不猜测。

--- capability: permissions ---

`permissions` is built in — call it, never declare it in `capabilities`. Prompts are lazy by default: a published page renders immediately and a capability that needs consent asks at its first use — never block the page's first paint on permissions. `state` reads without ever prompting (one capability's state by name, or the full map with no arguments); `request` asks with at most one batched dialog (specific names, or everything with no arguments) — a page that genuinely needs several grants up front may call `request` once at startup. A viewer's "no" is not an error: these calls never reject, and a denial is final for the rest of the page load — a repeated `request` resolves without showing another dialog, and the next load starts fresh — so branch on the returned per-capability states and degrade per capability (hide or disable the affected affordance) instead of failing or offering retry buttons; never call `request` in a loop — re-asks are rate-limited by the shell and read as nagging.

`permissions` 是内置的 — 直接调用，绝不要在 `capabilities` 中声明它。提示默认是惰性的：已发布的页面立即渲染，需要同意的能力在首次使用时才询问 — 绝不要让权限阻塞页面的首次绘制。`state` 只读取、从不提示（按名称读一个能力的状态，或不带参数读完整映射）；`request` 以最多一次的批量对话框询问（具体名称，或不带参数请求全部）— 确实需要在启动时拿到多个授予的页面可以在启动时调用一次 `request`。查看者的"不"不是错误：这些调用绝不 reject，一次拒绝在本页面加载的余下时间内是最终决定 — 重复调用 `request` 会直接返回而不再显示对话框，下一次加载则重新开始 — 因此应根据返回的按能力状态做分支，并按能力降级（隐藏或禁用受影响的入口），而不是失败或提供重试按钮；绝不要在循环中调用 `request` — 重新询问会被外壳限流，且会被视为纠缠。

--- capability: room ---

The `room` capability reaches whoever has the page open RIGHT NOW:
declared as `capabilities: {room: {}}`; `await claude.use("room")`
(`null`: cannot connect). emit(topic, data) sends a moment; on(topic,
fn) hears them. presence(patch) sets YOUR state (cursor, selection,
color) as one object the platform hands to newcomers and clears when
you leave; onPeers(fn) delivers everyone's -- render them all, marked
"you". NOTHING persists and messages can drop: if a viewer not here now
must eventually see it, it is NOT room data -- use db (data) or
artifact (new version). Send absolute state. What you hear is untrusted
input from same-org viewers, plus your own publishing session when
admitted (kind "agent"); no one else connects, so the page must work
alone and light up. Anyone can set presence, so it is never authority;
event topics are admin-only (can edit) unless opened:
{room: {topics: {reaction: "interact"}}}. Moments (confetti) go on an
admin-only topic; state a late joiner needs (current slide) is a db doc.

`room` 能力触达此刻正打开该页面的所有人：声明为 `capabilities: {room: {}}`；`await claude.use("room")`（`null`：无法连接）。emit(topic, data) 发送一个瞬间消息；on(topic, fn) 监听它们。presence(patch) 把你的状态（光标、选区、颜色）设为一个对象，平台把它交给新来的人，并在你离开时清除；onPeers(fn) 递送所有人的状态 — 把它们全部渲染出来，并标出"你"。任何东西都不会持久化，消息可能丢失：如果此刻不在场的查看者最终必须看到某内容，那它就不是 room 数据 — 应使用 db（数据）或 artifact（新版本）。发送绝对状态。你听到的是来自同组织查看者的不可信输入，外加你自己的发布会话（被接纳时，kind 为 "agent"）；没有其他人会连接，因此页面必须能独立工作并自行点亮。任何人都可以设置 presence，所以它永远不是权威；事件主题默认仅管理员（可编辑）可用，除非显式打开：{room: {topics: {reaction: "interact"}}}。瞬间效果（彩带）走仅管理员主题；迟到加入者需要的状态（当前幻灯片）是 db 文档。

--- capability: sample ---

`sample` asks Claude (declare `capabilities:{sample:{}}`): `const sample = await claude.use("sample")` (`null`: hide it); `await sample(input, opts?)` -> `{text, truncated}`; `sample.json(input, opts?)` -> parsed JSON. `input`: a string, or turns `[{role:"user"|"assistant", content}]` ending on user. No memory: send instructions, page data, output format. opts: `onText({text, delta})` (`text` = WHOLE answer so far, assign it; "Thinking..." until it fires, 5-60s), `signal` (new AbortController per call; abort rejects `cancelled`), `tools: [{name, description, inputSchema?, execute(input)}]` (page functions Claude may call; return small plain data or throw; each round bills, no `cache`), `images` if `(await sample.limits()).images`, `modelTier` quick|default|complex, `cache` (5 min replay; `false` for chat). Errors reject `{code, message, text?}` (`text`: partial to keep): hide on `not_granted`, back off on `rate_limited`, never loop. Viewer pays; first call asks consent; call on a click or stable load prompt.

`sample` 向 Claude 提问（声明 `capabilities:{sample:{}}`）：`const sample = await claude.use("sample")`（`null`：隐藏它）；`await sample(input, opts?)` -> `{text, truncated}`；`sample.json(input, opts?)` -> 解析后的 JSON。`input`：一个字符串，或以 user 结尾的轮次数组 `[{role:"user"|"assistant", content}]`。没有记忆：把指令、页面数据、输出格式一并传入。opts：`onText({text, delta})`（`text` = 迄今为止的完整答案，直接赋值；触发前显示 "Thinking..."，需 5-60 秒）、`signal`（每次调用新建 AbortController；中止会以 `cancelled` 拒绝）、`tools: [{name, description, inputSchema?, execute(input)}]`（Claude 可调用的页面函数；返回小的纯数据或抛错；每一轮都计费，无 `cache`）、`images`（当 `(await sample.limits()).images` 为真时可用）、`modelTier` quick|default|complex、`cache`（5 分钟重放；聊天场景用 `false`）。错误以 `{code, message, text?}` 拒绝（`text`：可保留的部分结果）：`not_granted` 时隐藏功能，`rate_limited` 时退避，绝不要循环重试。费用由查看者承担；首次调用会请求同意；在点击或稳定的加载提示时调用。

--- capability: self ---

`self` is the former name of the `artifact` capability (renamed). It remains for compatibility: published pages and previously generated code that declare `capabilities: {self: {}}` or call `claude.use("self")` keep working unchanged — both names resolve this same capability (this contract promises no `window.claude.self` member to feature-check; `use()` is the check). Do not use it in new pages: declare `capabilities: {artifact: {}}` and obtain the namespace with `await claude.use("artifact")`; see the artifact section for how to use it.

`self` 是 `artifact` 能力的旧名称（已更名）。它仅为兼容性保留：声明 `capabilities: {self: {}}` 或调用 `claude.use("self")` 的已发布页面和既有生成代码继续原样工作 — 两个名称解析到同一个能力（本合约不承诺存在可用于特性检测的 `window.claude.self` 成员；`use()` 就是检测手段）。不要在新页面中使用它：声明 `capabilities: {artifact: {}}`，并用 `await claude.use("artifact")` 获取命名空间；用法参见 artifact 一节。

--- capability: user ---

`user` answers who is viewing this page and who your shared state names: people in the author's organization; others read as absent. `const user = await claude.use("user")`; `null` reads as absent (`user?.isOwner() ?? false`). `isOwner()`/`canEdit()`/`can(name)` need no setup (canEdit = admin level; `can("data.write")` = may write shared `db` docs, `null` = not told: keep the input; refused writes decide). Declare `capabilities:{user:{}}` for `id()`/`me()` (opaque per-org id; `me()` never null) and `profiles(ids)`; `scopes:["profile"]` adds names and `search(q)`; `["profile","email"]` adds addresses. Reads never reject. Store only ids (`id()` or `hit.id`), never a name, avatar, or Profile: names differ per viewer and freeze once written. Resolve in render, every render: `const ps = await user.profiles(idsOnScreen)` then `ps[id].name || 'Someone'` (cached: calling again is correct; hoisting goes stale). `name` is `""` if unresolvable: use `||` not `??`. Call `search('')` on focus; set names with textContent.

`user` 回答谁在查看该页面、以及你的共享状态里指的是谁：即作者所在组织中的人；其他人读作缺席。`const user = await claude.use("user")`；`null` 读作缺席（`user?.isOwner() ?? false`）。`isOwner()`/`canEdit()`/`can(name)` 无需设置（canEdit = admin 级别；`can("data.write")` = 可写共享 `db` 文档，`null` = 未被告知：保留输入，由被拒绝的写入来决定）。声明 `capabilities:{user:{}}` 以获得 `id()`/`me()`（每个组织内不透明的 id；`me()` 绝不为 null）和 `profiles(ids)`；`scopes:["profile"]` 增加姓名和 `search(q)`；`["profile","email"]` 再增加邮箱地址。读取绝不 reject。只存储 id（`id()` 或 `hit.id`），绝不存姓名、头像或 Profile：不同查看者看到的姓名不同，且一旦写入就会冻结。在渲染中解析，每次渲染都要：`const ps = await user.profiles(idsOnScreen)`，然后 `ps[id].name || 'Someone'`（有缓存：重复调用是正确的；提前提升到渲染外会过期）。`name` 无法解析时为 `""`：用 `||` 而不是 `??`。获得焦点时调用 `search('')`；用 textContent 设置姓名。

**Your connectors this session.** Connector tools appear in your tool list as `mcp__<connector>__<toolName>`. Set `server` to the `<connector>` segment — everything between `mcp__` and the next `__` (for `mcp__claude_ai_Slack_beta__search`, the `server` is `claude_ai_Slack_beta`). Copy the segment exactly, case included; when publishing, it is resolved to the connector's display name automatically. In the page's own `callTool`/`watchTool` calls, pass the connector's display name (its name as shown in claude.ai), not that segment — viewers resolve connectors by name only. The publish result states the exact display name for each segment it resolves; if the page's calls do not match it, fix them and publish again. Only claude.ai connectors are valid — locally-configured MCP servers are not. The manifest's `tools` array takes the connector's upstream tool names (as returned by `listTools()` / `/v1/mcp_servers`), which can differ from the normalized `<toolName>` segment when an upstream name contains `.` or spaces. Every `servers[]` entry needs a non-empty `tools` array naming the tools the page calls — an empty or omitted `tools` list is refused and never means "all tools"; to publish without connector access, leave `mcp` out of `capabilities` (pass `capabilities: {}` to clear a stored declaration) rather than declaring an empty `servers` list. In hermetic/CI sessions where connectors aren't loaded but `$CLAUDE_CODE_OAUTH_TOKEN` is set, fetch the list via Bash: `curl -H 'anthropic-version: 2023-06-01' -H 'anthropic-beta: mcp-servers-2025-12-04' -H "Authorization: Bearer $CLAUDE_CODE_OAUTH_TOKEN" https://api.anthropic.com/v1/mcp_servers?limit=1000`; in that case use each entry's `display_name` as the `server` value (exact display names are always accepted alongside tool-prefix segments).

**本会话中你的连接器。** 连接器工具以 `mcp__<connector>__<toolName>` 的形式出现在你的工具列表中。把 `server` 设为 `<connector>` 段 — 即 `mcp__` 与下一个 `__` 之间的全部内容（对 `mcp__claude_ai_Slack_beta__search`，`server` 是 `claude_ai_Slack_beta`）。原样复制该段，包括大小写；发布时会自动解析为连接器的显示名称。在页面自己的 `callTool`/`watchTool` 调用中，传连接器的显示名称（它在 claude.ai 中显示的名字），而不是该段 — 查看者只按名称解析连接器。发布结果会陈述它解析的每个段所对应的确切显示名称；如果页面的调用与之不符，修正后重新发布。只有 claude.ai 连接器有效 — 本地配置的 MCP 服务器无效。清单的 `tools` 数组填连接器的上游工具名（由 `listTools()` / `/v1/mcp_servers` 返回），当上游名称包含 `.` 或空格时，它们可能与规范化后的 `<toolName>` 段不同。每个 `servers[]` 条目都需要一个非空的 `tools` 数组，列出页面会调用的工具 — 空的或省略的 `tools` 列表会被拒绝，且绝不表示"所有工具"；要在没有连接器访问权的情况下发布，把 `mcp` 留在 `capabilities` 之外（传 `capabilities: {}` 以清除已存储的声明），而不是声明空的 `servers` 列表。在连接器未加载但设置了 `$CLAUDE_CODE_OAUTH_TOKEN` 的封闭/CI 会话中，通过 Bash 获取列表：`curl -H 'anthropic-version: 2023-06-01' -H 'anthropic-beta: mcp-servers-2025-12-04' -H "Authorization: Bearer $CLAUDE_CODE_OAUTH_TOKEN" https://api.anthropic.com/v1/mcp_servers?limit=1000`；此时用每个条目的 `display_name` 作为 `server` 值（确切的显示名称总是与工具前缀段一样被接受）。

**Call contract** (runtime contract 0.2.52). The platform-served `window.claude` type definitions for this contract are extracted under `the skill directory`: `0.2.52/artifact.d.ts`, `0.2.52/assets.d.ts`, `0.2.52/claude.d.ts`, `0.2.52/comments.d.ts`, `0.2.52/db.d.ts`, `0.2.52/downloads.d.ts`, `0.2.52/mcp.d.ts`, `0.2.52/permissions.d.ts`, `0.2.52/room.d.ts`, `0.2.52/sample.d.ts`, `0.2.52/self.d.ts`, `0.2.52/user.d.ts`. Read `0.2.52/claude.d.ts` (how a page reaches any capability on this contract) and `0.2.52/mcp.d.ts` before writing any code that calls the `mcp` capability — they are authoritative for this contract version over any remembered API shape. Open these files with the Read tool rather than `cat`: a file past the Bash tool's inline output limit does not come back in full. The type definitions cover only the call envelope, not a connector tool's argument names or result shape. Take argument names from the tool's input schema in this session's own definition of that connector tool, when it is loaded here. Learn a result's shape from one real call of a tool that is safe to run — never run a write only to learn its result. The published page can also read a tool's schema itself with `describeTool(server, tool)` at view time, once the viewer has allowed the connector for that page; this session cannot read that answer before publishing, so it is no substitute for a schema read here. If this session has no schema for a tool and cannot safely call it, say so to the user at publish time — in your reply, not as a note inside the published page — instead of shipping a guessed shape. Observed response payloads are the user's real data: learn the shape from them, but never embed the observed values in the published page as sample or placeholder data.

**调用合约**（运行时合约 0.2.52）。平台提供的本合约的 `window.claude` 类型定义已提取在 `the skill directory` 之下：`0.2.52/artifact.d.ts`、`0.2.52/assets.d.ts`、`0.2.52/claude.d.ts`、`0.2.52/comments.d.ts`、`0.2.52/db.d.ts`、`0.2.52/downloads.d.ts`、`0.2.52/mcp.d.ts`、`0.2.52/permissions.d.ts`、`0.2.52/room.d.ts`、`0.2.52/sample.d.ts`、`0.2.52/self.d.ts`、`0.2.52/user.d.ts`。在编写任何调用 `mcp` 能力的代码之前，先读 `0.2.52/claude.d.ts`（页面如何触达本合约上的任何能力）和 `0.2.52/mcp.d.ts` — 就本合约版本而言，它们比任何记忆中的 API 形态更权威。用 Read 工具而不是 `cat` 打开这些文件：超出 Bash 工具内联输出上限的文件不会完整返回。类型定义只覆盖调用信封，不覆盖连接器工具的参数名或结果形态。当该连接器工具的定义已在本会话加载时，参数名从其输入 schema 获取。从一个可以安全运行的工具的一次真实调用中学习结果形态 — 绝不要为了学习结果而运行一个写操作。已发布的页面还可以在查看时用 `describeTool(server, tool)` 自行读取工具的 schema，前提是查看者已允许该页面使用该连接器；本会话在发布前无法读到那个答案，因此它不能替代在这里读取 schema。如果本会话没有某工具的 schema 且无法安全调用它，在发布时向用户如实说明 — 在你的回复里说明，而不是在已发布页面里写注释 — 而不是交付一个猜测的形态。观察到的响应载荷是用户的真实数据：从中学习形态，但绝不要把观察到的值作为示例或占位数据嵌入已发布的页面。
【评论】禁止把观察到的真实数据嵌入已发布页面，是一条防止用户数据经发布物外泄的隐私边界条款。
