mcpField
S
stitch-sdk-domain-design

stitch-sdk-domain-design

github.com/google-labs-code开发1.8k185 次安装4 周前更新
代码仓库

关于 stitch-sdk-domain-design

用于 Stitch SDK 流程第二阶段的设计技能,指导贡献者将 MCP 工具模式映射为经过验证的中间表示 `domain-map.json`。

主要功能

  1. 01领域类设计指引
  2. 02MCP 工具绑定与参数路由
  3. 03响应投影设计
  4. 04支持缓存的方法映射
  5. 05模式与投影验证

适用场景

  1. 01将新的 MCP 工具映射到领域方法
  2. 02选择参数来源和返回结构
  3. 03添加缓存响应路径
  4. 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——驱动代码生成的中间表示。


输入

  1. tools-manifest.json——从服务端捕获的原始 MCP 工具架构(包含 outputSchema)
  2. ir-schema.ts——定义有效领域映射结构的 Zod 架构(规范契约)
  3. 现有的 domain-map.json——当前 IR(用于扩展,而非从头开始)
  4. 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.fieldprojectId: this.projectId
param来自方法参数prompt: prompt
computedtemplate 插值("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

决策框架

映射新工具时,请回答以下问题:

  1. 属于哪个类? 查看工具需要哪些字段。如果它需要来自 self 的 projectId,则应归属 Project 或 Screen。如果它不需要来自 self 的任何内容,则应归属 Stitch。

  2. 方法叫什么? 使用工具名称中的动词,并简化命名。generate_screen_from_text → generate。edit_screens → edit。

  3. 参数来自 self 还是 param? 如果调用方已有该数据(因为它是在调用自身的方法),则使用 self。如果需要调用方提供,则使用 param。

  4. 返回值嵌套多深? 查看 tools-manifest.json 中工具的 outputSchema。逐步构建 projection 数组,以导航到有用的数据。

  5. 是否应该缓存? 如果数据可从先前的响应中获取(例如生成操作的响应),则添加一个带有 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

评论

支持 Markdown