关于 stitch-sdk-domain-design
用于 Stitch SDK 流程第二阶段的设计技能,指导贡献者将 MCP 工具模式映射为经过验证的中间表示 `domain-map.json`。
主要功能
- 01领域类设计指引
- 02MCP 工具绑定与参数路由
- 03响应投影设计
- 04支持缓存的方法映射
- 05模式与投影验证
适用场景
- 01将新的 MCP 工具映射到领域方法
- 02选择参数来源和返回结构
- 03添加缓存响应路径
- 04诊断无效的 domain-map 投影
MCP 相关
使用保存在 `tools-manifest.json` 中的 MCP 工具模式,并定义如何将其映射到 `domain-map.json` 的领域类和方法。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ google-labs-code/ stitch-sdk --skill stitch-sdk-domain-design安装后在 Claude Code 中输入/stitch-sdk-domain-design确认已加载。
详细介绍
Stitch SDK 领域设计
本技能将教你如何完成生成流水线的第 2 阶段:读取工具架构,并生成 domain-map.json——驱动代码生成的中间表示。
输入
tools-manifest.json——从服务端捕获的原始 MCP 工具架构(包含outputSchema)ir-schema.ts——定义有效领域映射结构的 Zod 架构(规范契约)- 现有的
domain-map.json——当前 IR(用于扩展,而非从头开始) stitch-sdk-development技能——用于了解流水线上下文
输出
有效的 domain-map.json,包含两个部分:classes 和 bindings,并通过 ir-schema.ts 验证。
重要
代码生成会对输出进行两次验证:结构验证(Zod IR 架构)和语义验证(根据工具清单中的
outputSchema验证投影步骤)。
设计类
每个类表示一个领域实体。请思考:“用户会与哪个名词交互?”
{
"Stitch": {
"description": "Main entry point. Manages projects.",
"constructorParams": [],
"isRoot": true,
"factories": [
{
"method": "project",
"returns": "Project",
"description": "Create a Project handle from an ID."
}
]
}
}
关键决策:
| 字段 | 用途 | 示例 |
|---|---|---|
constructorParams | 存储在实例上的字段 | ["projectId", "screenId"] |
reference | 身份映射键(.id 是最后一个键的别名) | { "keys": ["projectId", "screenId"] } |
parentField | 从父类注入的参数 | "projectId" |
factories | 本地工厂方法(不调用 API) | [{ "method": "project", "returns": "Project" }] |
sideEffects | 手写扩展方法(已声明,不会生成) | [{ "method": "upload", "reason": "private_rest", "specPath": "src/spec/upload.ts" }] |
extensionPath | 重新导出带有手写方法的类的模块 | "../../src/project-ext.js" |
重要
**IR 架构是严格的。**未知键会导致验证失败,不会被静默忽略。旧文档中描述的按字段进行的源映射辅助工具从未实现,也不存在——身份字段由 EntityManager 根据
reference.keys、资源name解析结果和id回退值填充。如果验证拒绝了你认为应该存在的键,该功能就不存在——不要试图绕过限制。
设计绑定
每个绑定都将一个 MCP 工具映射到一个类方法。请思考:“这个操作归谁所有?”
参数路由
| 类型 | 含义 | 生成的代码 |
|---|---|---|
self | 来自 this.field | projectId: this.projectId |
param | 来自方法参数 | prompt: prompt |
computed | template 插值("template": "projects/{projectId}") | name: \projects/${this.projectId}/screens/${screenId}`` |
selfArray | 将 self 字段包装为数组 | selectedScreenIds: [this.screenId] |
可选参数使用 "optional": true。重命名参数使用 "rename": "newName"。
调用方省略值时,会发送 "default": "VALUE"(需要 optional: true):
deviceType: options?.deviceType ?? "DESKTOP"。
重要
方法签名形式(D12): 必需参数按 IR 顺序作为位置参数;所有可选参数都会放入单个尾随的
options?: { ... }对象中。使用generate(prompt, options?),而非generate(prompt, deviceType?)。
响应投影
returns.projection 数组会告知代码生成器如何遍历 API 响应。每个步骤都是一个 ProjectionStep:
{ prop: string; index?: number; each?: boolean; find?: string; acknowledgeSingle?: boolean }
| 投影 | 生成的代码 | 使用场景 |
|---|---|---|
[](空) | raw | 直接返回(整个响应) |
[{ "prop": "projects" }] | raw.projects | 对象内的数组 |
[{ "prop": "outputComponents", "index": 0 }, { "prop": "design" }, { "prop": "screens", "index": 0 }] | raw.outputComponents[0].design.screens[0] | 深层嵌套的单个项 |
[{ "prop": "outputComponents", "each": true }, { "prop": "design" }, { "prop": "screens", "each": true }] | flatMap 链 | 收集数组中的所有项 |
[{ "prop": "screenshot" }, { "prop": "downloadUrl" }] | raw.screenshot.downloadUrl | 遍历嵌套属性 |
判断方式:提取单个项时使用 "index": 0。收集所有项(数组结果)时使用 "each": true。同一步骤中不能同时使用两者。"find": "a.b" 会扫描数组,查找嵌套路径非 null 的第一个元素。
提示
代码生成时,会针对工具的
outputSchema验证投影中的每个prop。如果属性名拼写错误,代码生成将失败,并给出列出可用属性的诊断信息。遍历数组时未使用index、each或find也会导致代码生成错误——生成的链在运行时将得到undefined。
警告
截断检查: 对无界数组使用
index或find会发出警告——其他所有元素都会被静默丢弃(project.generate()就曾因这种写法只返回了多个屏幕中的一个)。 优先使用"each": true和"array": true。只有在结果在语义上确实是单个项时,才在该步骤中使用"acknowledgeSingle": true明确表示这一点。
返回类封装
设置 returns.class 时,提取的数据会封装在一个领域类构造函数中:
{
"returns": {
"class": "Screen",
"projection": [{ "prop": "screens" }],
"array": true
}
}
如果子类声明了 parentField,codegen 会自动将其展开到数据中。
缓存感知方法
添加一个带有结构化 projection 的 cache 字段,以便在调用 API 前检查 this.data:
{
"cache": {
"projection": [{ "prop": "htmlCode" }, { "prop": "downloadUrl" }],
"description": "Use cached HTML download URL from generation response if available"
}
}
当缓存属性是嵌套对象(例如带有 downloadUrl 的 File)时,请使用多个 projection 步骤逐层深入。
生成的代码:
if (this.data?.htmlCode?.downloadUrl) return this.data?.htmlCode?.downloadUrl;
// ... else call API
决策框架
映射新工具时,请回答以下问题:
-
属于哪个类? 查看工具需要哪些字段。如果它需要来自
self的projectId,则应归属Project或Screen。如果它不需要来自 self 的任何内容,则应归属Stitch。 -
方法叫什么? 使用工具名称中的动词,并简化命名。
generate_screen_from_text→generate。edit_screens→edit。 -
参数来自 self 还是 param? 如果调用方已有该数据(因为它是在调用自身的方法),则使用
self。如果需要调用方提供,则使用param。 -
返回值嵌套多深? 查看
tools-manifest.json中工具的outputSchema。逐步构建projection数组,以导航到有用的数据。 -
是否应该缓存? 如果数据可从先前的响应中获取(例如生成操作的响应),则添加一个带有 projection 路径的缓存字段。
验证
编辑 domain-map.json 后:
bun scripts/generate-sdk.ts # Validates IR + projections, then generates
npx tsc --noEmit # Type check
npx vitest run # Unit tests
bun scripts/e2e-test.ts # E2E tests
bun scripts/validate-generated.ts # Lock integrity
如果 projection 无效,你会看到:
❌ Binding "Project.generate" projection step 2:
property "screenz" not found in outputSchema.
Available properties: screens, components, metadata