stitch-sdk-domain-design について
Stitch SDKパイプラインの第2段階向け設計スキルです。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 ドメイン設計
このスキルでは、生成パイプラインの Stage 2 の実行方法、つまりツールスキーマを読み取り、コード生成を駆動する中間表現 domain-map.json を作成する方法を説明します。
入力
tools-manifest.json— サーバーから取得した MCP ツールスキーマの生データ(outputSchemaを含みます)ir-schema.ts— 有効な domain-map 構造を定義する Zod スキーマ(正式な契約です)- 既存の
domain-map.json— 現在の IR(新規作成ではなく拡張する場合) stitch-sdk-developmentスキル — パイプラインの背景を理解するために使用します
出力
classes と bindings の 2 つのセクションを含む、有効な domain-map.json。ir-schema.ts によって検証されます。
重要
出力はコード生成によって 2 回 検証されます。構造検証(Zod IR スキーマ)と意味検証(ツールマニフェストの
outputSchemaに対する projection ステップの検証)です。
クラスの設計
各クラスはドメインエンティティを表します。「ユーザーが操作する名詞は何か?」と考えてください。
{
"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 スキーマは厳密です。 不明なキーは無視されず、検証エラーになります。以前のドキュメントに記載されていたフィールドごとのソースマッピングヘルパーは実装されたことがなく、存在しません。アイデンティティフィールドは、
reference.keys、リソースのnameの解析、およびidのフォールバックから EntityManager によって設定されます。存在すると思っていたキーが検証で拒否された場合、その機能は存在しません。回避策を試さないでください。
バインディングの設計
各バインディングは、1 つの MCP ツールを 1 つのクラスメソッドに対応付けます。「このアクションを所有するのは誰か?」と考えてください。
引数のルーティング
| 型 | 意味 | 生成されるコード |
|---|---|---|
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 でない最初の要素を取得します。
ヒント
射影内のすべての
propは、コード生成時にツールのoutputSchemaに照らして検証されます。プロパティ名を誤入力すると、使用可能なプロパティの一覧を示す診断メッセージとともにコード生成が失敗します。index/each/findを指定せずに配列の先へ進むこともコード生成エラーになります。生成されるチェーンは実行時にundefinedになるためです。
警告
切り捨ての lint: 上限のない配列に
indexまたはfindを使うと警告が出ます。ほかの要素はすべて暗黙に破棄されます(このパターンが原因で、project.generate()が複数の画面のうち1つだけを返したことがあります)。"each": trueと"array": trueの組み合わせを推奨します。結果が意味的に単一の場合に限り、そのステップに"acknowledgeSingle": trueを指定して明示してください。
戻り値のクラスによるラップ
returns.class が設定されている場合、抽出されたデータはドメインクラスのコンストラクターでラップされます。
{
"returns": {
"class": "Screen",
"projection": [{ "prop": "screens" }],
"array": true
}
}
子クラスで parentField が宣言されている場合、コード生成では自動的に parentField がデータに展開されます。
キャッシュを考慮したメソッド
構造化された 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 のパスを含む cache フィールドを追加します。
検証
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