sandbox_schema
動手做之前先排好要存的資料,用文字、不是圖:資料表、它的欄位,每個欄位寫型別、一句它放什麼的註解(每個欄位都必填),以及關聯到哪張表。Schema 以文字回到工具結果,人在 app 裡這份設計的頁面看到同一版的文字表格,在上面圈線、講話、打字回報。不需要箱子。
| 輸入 | 輸出 |
|---|---|
topic(最多 80 字,每一版都一樣)、tables(1–30 張,每個欄位都要有 note)、選填 note(最多 300 字:這版改了什麼)、選填 id(箱子,讓設計也出現在那個箱子頁) |
ok、designId(dsg_ 開頭,這份設計)、reviewId(dsn_ 開頭,這一版)、kind(schema)、topic、version、appUrl、有傳 id 時有 boxId、next,有提醒時有 warnings,人在 app 留話給這個對話時有 fromHuman;Schema 的文字以一段 text 接在 JSON 後面。沒有圖,也沒有 imageUrls |
工具標題(MCP 的 title,有的 client 用它顯示這個工具)是 Lay out a data schema;註記為 readOnlyHint: false(成功一次就存一版)、destructiveHint: false、openWorldHint: false。
什麼時候用
新做或要改的東西,開發順序是:sandbox_draft(畫面)→ sandbox_architecture(服務)→ sandbox_schema(存的資料)→ 在箱子裡做 → sandbox_review。只有這次工作碰不到的那一步才跳過:沒有多存東西的改動就沒有 Schema。
這次工作會加或改要存的資料(新的表、新的欄位、某個欄位的意思改了)、或人要看 Schema 時,用 sandbox_schema。不要改寫 HTML、SVG、圖片、圖表或 Markdown 檔來代替:這個工具會排好、會留下每一版,也給人回報用的工具。
除了回來的是文字,其他都跟 sandbox_draft 一樣:不需要箱子,設計屬於帳號和這段對話,箱子停掉也還在。id 選填:傳這段工作的箱子,設計也會出現在那個箱子頁;呼叫在伺服器上做(只檢查、存起來,不畫圖),從不叫醒凍結的箱子,id 指的是還沒停掉的箱子時,成功就算對它的一次使用,呼叫的對話就是拿著它的對話(sandbox_draft)。
寫什麼
只列這次工作需要的表和欄位。new: true 標的是跟現在存的資料比起來新的或不一樣的表和欄位:文字裡標 [new],app 裡標「新增」。比的是現在存的資料,不是上一版:跟上一版比改了什麼寫在 note。
每個欄位都要有 note:一句簡短註解,說這個欄位放什麼(「候補第幾位;已預約就是空的」),人不看程式也能判斷這個欄位。有欄位沒給 note、給空字串或只有空白,整個呼叫都會失敗(見錯誤)。label 和每個 note 都用人讀的語言寫:照原樣顯示,不會翻譯。
tables:
| 欄位 | 必填 | 上限 | 說明 |
|---|---|---|---|
name |
必填 | 64 字,不能重複 | 資料表名稱(bookings) |
label |
40 字 | 這張表存什麼,用人讀的語言,寫在表名後面(「預約」) | |
new |
這次工作加的表 | ||
columns |
40 欄 | 欄位,由上往下 |
columns:
| 欄位 | 必填 | 上限 | 說明 |
|---|---|---|---|
name |
必填 | 64 字 | 欄位名 |
note |
必填 | 1–120 字 | 一句簡短註解,說這個欄位放什麼(「已預約/候補/取消」) |
type |
建議寫 | 40 字 | 型別(uuid、text、int、timestamptz) |
ref |
64 字 | 這個欄位指到哪張表,顯示成 → 表名;指到這份 Schema 沒有的表照樣收,warnings 會提醒 |
|
new |
跟現在比起來新的或不一樣的欄位 |
拼錯或多給的欄位一律報錯,錯誤會列出允許的欄位;不會默默忽略。上限算的是字元數(中文一個字算 1)。整個呼叫除了 id、topic、note 以外的部分是 Schema 的規格,JSON 最大 64 KB。
範例
{
"topic": "預約資料",
"tables": [
{
"name": "classes",
"label": "課程",
"columns": [
{ "name": "id", "type": "uuid", "note": "課程編號" },
{ "name": "starts_at", "type": "timestamptz", "note": "開課時間" }
]
},
{
"name": "bookings",
"label": "預約",
"columns": [
{ "name": "id", "type": "uuid", "note": "預約編號" },
{ "name": "class_id", "type": "uuid", "ref": "classes", "note": "哪一堂課" },
{ "name": "status", "type": "text", "note": "已預約/候補/取消" },
{ "name": "waitlist_rank", "type": "int", "note": "候補第幾位;已預約就是空的", "new": true }
]
}
]
}
回傳
Schema 的文字以一段 text 接在 JSON 後面。先讀過,再用一句話告訴人要看哪裡;next 也這樣寫。上面的範例當第一版送出時是:
Schema "預約資料" v1
[new] = added or changed in this version; "→ table" = references that table; "# …" = what the field holds
classes (課程)
id uuid # 課程編號
starts_at timestamptz # 開課時間
bookings (預約)
id uuid # 預約編號
class_id uuid → classes # 哪一堂課
status text # 已預約/候補/取消
waitlist_rank int # 候補第幾位;已預約就是空的 [new]
- 一張表一段:
name、括號裡的label,new: true的表後面加[new]。接著一個欄位一行:欄位名、型別、有ref時→ 表名、#和它的note,new: true的欄位後面加[new]。沒有columns的表寫(no columns)。呼叫有帶note時,第一行下面多一行Changed in this version: …。 - 這些固定字不管帳號是什麼語言,一律是英文;你寫的字照原樣出現,emoji 也一樣。
designId(dsg_開頭)是這份設計,這個topic的每一版都一樣,reviewId(dsn_開頭)是這一版的 id,version照帳號、種類、topic各自編號(不管傳哪個箱子或沒傳),appUrl是 app 裡這份設計的頁面/design/<designId>(拿到連結就打得開,只有帳號本人能回報),boxId是你傳的id那個箱子(沒傳就沒有)。warnings會說ref指到這份 Schema 沒有的表,例如table "bookings" column "member_id": ref "members" is not one of the tables in this diagram。
人在 app 看到的
人在 app 裡這份設計的頁面和「設計」清單看到它,有傳箱子 id 時也在那個箱子頁的「Schema」那一格。在 app 裡也是文字,不是圖:一張表一張卡片,上面是表名、label,新的表旁邊有「新增」小標籤;底下一個欄位一列:欄位名、型別、→ 表名、註解。不是新表裡的新欄位或改過的欄位,欄位名旁邊有「新增」小標籤;整張表都是新的就只在表名標一次,不再一個欄位一個欄位標。手機上一列是欄位名和型別一行、註解在下一行;寬的畫面排成「欄位|型別|說明」三欄。app 自己的字(「新增」、欄名)跟 app 的語言。
每張表的卡片底下有一個「回報這裡」:拿那張表的卡片截一張圖,人在上面圈線、講話、打字,然後送出。一次回報可以包含好幾張表的回報。
人的回報與下一版
- 人對某一版的回報會回到寫那一版的對話,照驗收回饋的方式回來,有沒有箱子都一樣:叫醒設定好接收 app 回饋的對話,或在這個對話下一次呼叫設計工具或對任何箱子呼叫工具時以
fromHuman回來,或經由 複製給 AI 的訊息;用sandbox_report只帶reportId讀,不帶id。訊息會寫Feedback on schema "<topic>" v<n>: …。每張圈過的圖都是人回報的那張表的卡片,那則回報的source.label寫著哪一版、哪張表,例如Schema「預約資料」v1 · bookings(跟 app 的語言)。先看圈過的圖、語音逐字稿和人在圖上打的字(notes[].labels)。訊息最後是Change it as asked, then call sandbox_schema again with the same topic for the next version.。要自動回到這個對話,轉接器要 0.6.0 以上(有傳箱子id時 0.5.4 以上),Claude Code 與 Gemini CLI 的回饋 hook 也要是 0.5.4 以上裝的(自動回到對話的條件)。細節見sandbox_draft。 - 沒有核准或定案的步驟。照人說的改,再用同一個
topic(傳過id就帶同一個id)呼叫一次sandbox_schema,規格整份重給(每個欄位都有note),加一句note說跟上一版比改了什麼:這就是下一版(v2、v3…),不管從哪個箱子或沒有箱子。
錯誤
呼叫失敗時訊息會指出是哪個欄位;什麼都不會存,也不算使用任何箱子:box not found (it must be one of yours; see sandbox_status or GET /v1/boxes)(你傳的 id 不是這個帳號的箱子;不帶 id 就能不用箱子寫)、topic is required: …、topic is at most 80 characters、note is at most 300 characters、spec is at most 64 KB,或 Schema 檢查不接受的東西,格式是 <路徑>: <問題>,路徑就是你呼叫裡的參數:
tables[0].columns[2].note: required (a short comment on every field):那個欄位沒有note或是空的。每個欄位都給note;工具的 input schema 也把它標成必填。tables[1].name: duplicate table name "bookings" (also used by spec.tables[0]):括號裡的spec.tables[0]就是你呼叫裡的tables[0]tables[0].columns[2]: unknown field "comment" (allowed: name, type, note, ref, new)spec: unknown field "table" (allowed: tables):多給或拼錯的最上層參數(除了id、topic、note以外的參數都會交給 Schema 檢查,它把這些合稱spec)
改好那個欄位再呼叫一次。
所有工具與主題都列在工具參考。