stitch-sdk-development について
`@google/stitch-sdk` の開発者向けスキルです。SDK の生成パイプライン、アーキテクチャ上の境界、実装手順を解説します。
主な機能
- 013段階のSDK生成パイプライン
- 02MCPツールとドメインのバインディング指針
- 03生成コードの整合性チェック
- 04副作用を伴う機能の実装パターン
- 05レッド・グリーン・イエローのテスト手順
活用シーン
- 01Stitchの新しいツールをSDKに追加
- 02ドメインクラスとメソッドの設計
- 03ファイルシステムやネットワーク拡張の実装
- 04SDK変更のテストと検証
MCP 対応
Stitch MCP サーバーからツールスキーマを取得し、生成SDKクラスへ対応付ける方法を説明します。`StitchToolClient`の通信機能と、`StitchProxy` MCPプロキシサーバーも扱います。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ google-labs-code/ stitch-sdk --skill stitch-sdk-developmentインストール後、Claude Code で/stitch-sdk-developmentを入力して読み込まれたか確認します。
詳細
Stitch SDK の開発
このスキルでは、@google/stitch-sdk の開発に必要な専門知識、つまり中核となるシステム、パターン、設計思想を説明します。すべてのメソッドを列挙するものではありません(信頼できる情報源はコードベースです)。システムの考え方を学ぶためのものです。
生成パイプライン
ドメインレイヤーはすべて生成されます。手書きのドメインクラスはありません。パイプラインは3つのステージで構成されます。
Stage 1: Capture Stage 2: Domain Design Stage 3: Generate
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐
│ capture-tools.ts │──────▶│ domain-map.json │───────▶│ generate-sdk.ts │
│ │ │ (the IR) │ │ │
│ Connects to MCP │ │ Classes, bindings│ │ Deterministic │
│ server, calls │ │ arg routing, │ │ codegen into │
│ tools/list │ │ cache, extraction│ │ packages/sdk/generated/ │
└──────────────────┘ └──────────────────┘ └──────────────────────────┘
│ │ │
▼ ▼ ▼
tools-manifest.json domain-map.json packages/sdk/generated/src/*.ts
(raw MCP tool schemas) (tool→class mapping) (Stitch, Project, Screen)
ステージ1(bun scripts/capture-tools.ts):稼働中の Stitch MCP サーバーに接続してtools/listを呼び出し、tools-manifest.jsonに書き込みます。どのツールが存在するかを示す信頼できる情報源です。
ステージ2(エージェント/人間):マニフェストを読み込み、中間表現であるdomain-map.jsonを作成します。ここで判断が必要になります。どのツールをどのクラスに対応付けるか、どの引数をself、param、computedから取得するか、戻り値をどう抽出するか、どのデータをキャッシュするかを決めます。
ステージ3(bun scripts/generate-sdk.ts):決定論的なコード生成を行います。マニフェストと domain-map を読み込み、packages/sdk/generated/src/に TypeScript クラスを出力します。LLM は関与せず、テンプレート展開のみで生成されます。
整合性:stitch-sdk.lockには、すべての入力と出力の SHA-256 ハッシュが記録されます。bun scripts/validate-generated.tsで整合性を検証します。古いコードの公開を防ぐため、CI で実行してください。
新しいツールへの対応
Stitch MCP サーバーに新しいツールが追加された場合:
- ステージ1を実行し、更新されたマニフェストを取得します
- ステージ2を実行し、新しいツールのバインディングを
domain-map.jsonに追加します - ステージ3を実行し、SDK クラスを再生成します
validate-generated.tsを実行して、整合性を確認します- 必要に応じてテストを更新します
Domain Map IR
domain-map.jsonでは、次の2つを表現します。
クラス:存在するドメインオブジェクトとその構築方法。
{
"Screen": {
"constructorParams": ["projectId", "screenId"],
"parentField": "projectId",
"reference": { "keys": ["projectId", "screenId"] }
}
}
バインディング:MCP ツールをクラスメソッドに対応付ける方法。
{
"tool": "generate_screen_from_text",
"class": "Project",
"method": "generate",
"args": {
"projectId": { "from": "self" },
"prompt": { "from": "param" },
"name": {
"from": "computed",
"template": "projects/{projectId}/screens/{screenId}"
}
},
"returns": {
"class": "Screen",
"projection": [
{ "prop": "outputComponents", "index": 0 },
{ "prop": "design" },
{ "prop": "screens", "index": 0 }
]
}
}
引数の振り分け:self=thisから注入、param=呼び出し元から渡される、computed=呼び出し時にテンプレートから構築、selfArray=[this.field]を配列でラップ。
レスポンスの射影:outputSchemaに照らして検証される、構造化されたProjectionStep[]配列です。単一の項目にはindex、配列にはeachを使用します。空の[]は直接返すことを意味します。
キャッシュ:メソッドでは、API 呼び出し前にthis.dataを確認するため、構造化されたprojectionを含むcacheを指定できます。
{
"cache": {
"projection": [{ "prop": "htmlCode" }, { "prop": "downloadUrl" }],
"description": "Use cached download URL from generation response"
}
}
2つの利用形態
SDK は、ニーズの異なる2種類の利用者に対応します。
エージェント向け — StitchToolClient
AI エージェントやオーケストレーションスクリプト向けです。ツールとのやり取りをそのまま行います。エージェントはツールスキーマを受け取り、JSON を構築して送信し、JSON を受け取ります。ドメイン知識は必要ありません。
クイックスタート(シングルトン)
stitchシングルトンは、Proxyを介してドメインメソッドとツールメソッドの両方を提供します。インスタンス化は不要です。初めてアクセスした際に、環境変数からStitchToolClientを遅延生成します。
import { stitch } from "@google/stitch-sdk";
// Discover available tools
const { tools } = await stitch.listTools();
// Call any tool by name with a JSON payload
const result = await stitch.callTool("generate_screen_from_text", {
projectId: "123",
prompt: "A login page",
});
// Clean up when done
await stitch.close();
シングルトンは、環境変数からSTITCH_API_KEY(またはSTITCH_ACCESS_TOKENとGOOGLE_CLOUD_PROJECT)を読み込みます。サーバー URL を上書きするには、STITCH_BASE_URLを設定します。(STITCH_HOSTは非推奨の別名で、2.0で削除されます。)
直接インスタンス化
明示的な制御(複数のクライアント、カスタム設定、テスト)が必要な場合は、StitchToolClientを直接インスタンス化します。
import { StitchToolClient } from "@google/stitch-sdk";
const client = new StitchToolClient({ apiKey: "my-key" });
const tools = await client.listTools();
const result = await client.callTool("create_project", { title: "My App" });
await client.close();
設定の解決順序:コンストラクターのパラメーター → 環境変数 → デフォルト値。認証には、apiKeyまたはaccessTokenとprojectIdの組み合わせが必要です(インスタンス生成時に Zod で検証されます)。
接続:callToolとlistToolsは、初回呼び出し時に自動接続します。並行する呼び出しは、Promise ベースのロックを介して安全に接続を共有します。
AI SDK アダプター — stitchTools()
Vercel AI SDK で構築されたエージェント向けです。MCP ツールスキーマを AI SDK 互換のツール定義に変換し、generateText() とのプラグアンドプレイを可能にします。
import { stitchTools } from "@google/stitch-sdk/ai";
import { generateText } from "ai";
import { google } from "@ai-sdk/google";
const result = await generateText({
model: google("gemini-2.0-flash"),
tools: stitchTools(), // all tools
// or: stitchTools({ include: ["create_project"] }) // filtered
prompt: "Create a project called My App",
});
stitchTools() は ai 依存をオプションに保つため、/ai サブパスからエクスポートされています。内部的には同じ共有 StitchToolClient シングルトンを使用します。
/tools サブパスは、事前パース済みパラメータによる O(1) のツールルックアップを提供します — 静的、認証不要、ネットワーク呼び出しなし:
import { toolMap } from "@google/stitch-sdk/tools";
const tool = toolMap.get("create_project");
tool.params; // ToolParam[] — flat, pre-parsed
tool.params.filter((p) => p.required); // required params only
tool.inputSchema; // raw ToolInputSchema still available
toolDefinitions と toolMap はどちらも @google/stitch-sdk/tools サブパスからエクスポートされています (ルートエントリには含めず、スリムに保たれています)。
SDK モダリティ — 生成されたドメインクラス
正確でプログラム的なスクリプトを書く人間向けです。callTool 上の生成されたドメインファサードです。型付きパラメータ、ドメインオブジェクトが返され、失敗時には StitchError がスローされます。
const project = await stitch.createProject({ title: "My App" });
const generation = await project.generate("A login page");
const html = await generation.first.getHtml();
両方のモダリティは、内部的に StitchToolClient を共有しています。ドメインクラスは callTool 上の型付きレイヤーです。
エラーハンドリング — 境界でスローする
生成されたすべてのメソッドは、エラーハンドリングに throw StitchError を使用します。Result<T> パターンはありません。
// Generated method pattern (inside each method):
try {
const raw = await this.client.callTool<any>("tool_name", args);
return /* extracted result */;
} catch (error) {
throw StitchError.fromUnknown(error);
}
StitchError.fromUnknown() は、すべてのエラーがコード、メッセージ、回復可能性のヒントを持つ StitchError に正規化されることを保証します。
インフラストラクチャ (手書き)
これらのコンポーネントは基盤となる配管を提供するため、手書きのままです:
StitchToolClient— MCP トランスポート、認証、ツール呼び出しStitchError— コード、メッセージ、回復のヒントを持つ型付きエラークラスStitchProxy— Stitch を他のエージェントに再公開するための MCP プロキシサーバーsingleton.ts— 環境変数設定を伴うstitchエクスポート用の遅延プロキシ
副作用の膜
SDK には、正式な境界によって分離された 2 つの半球があります:
- 生成された半球 — すべての MCP ツール呼び出し、レスポンス投影、引数ルーティング、クラスコンストラクタ。JSON を送信して JSON を受け取る場合、それは生成されなければなりません (MUST)。
- 手書きの半球 — 実世界に触れる操作: ファイルシステム I/O、バイナリストリーム、非 MCP REST エンドポイント。ディスクに触れる、またはプライベート API を使用する場合、それは手書きでなければなりません (MUST)。
膜は、extensionPath を持つ任意のクラスの sideEffects を介して domain-map.json で宣言されます:
{
"Project": {
"extensionPath": "../../src/project-ext.js",
"sideEffects": [
{
"method": "upload",
"reason": "private_rest",
"specPath": "src/spec/upload.ts"
},
{
"method": "downloadAssets",
"reason": "filesystem_io",
"specPath": "src/spec/download.ts"
}
]
}
}
有効な reason の値: filesystem_io、binary_data、private_rest、complex_orchestration。
ジェネレーターはステージ 3 で検証します:
- どの
sideEffect.methodも、生成されたバインディングメソッド名と衝突しないこと - 各
specPathが既存のファイルを指していること
新しい副作用メソッドの追加
Spec → Handler → Extension パターンに従ってください:
-
Spec を作成する (
src/spec/my-operation.ts): 入力スキーマ (Zod)、エラーコード、Result 型、インターフェースを定義します。Handler はこのインターフェースを実装し、決してスローしてはなりません。 -
Handler を作成する (
src/my-operation-handler.ts): Spec インターフェースを実装します。すべての失敗はResult<T>を返し、決してthrowしません。ここに副作用ロジック (ディスク、ネットワーク、バイナリ) が置かれます。 -
Extension に追加する (
src/project-ext.ts): 入力をパースし、Handler を呼び出し、失敗時に Result をthrow StitchErrorにマッピングする薄いアダプター (メソッド本体ごとに 15 行未満)。 -
domain-map.json で宣言する:
method、reason、specPathを持つsideEffectエントリを追加します。 -
再生成する:
bun scripts/generate-sdk.tsを実行します — ジェネレーターが宣言を検証します。
ルール
| ルール | 根拠 |
|---|---|
| Extension メソッドは生成されたメソッドをオーバーライドしてはならない | 暗黙のシャドーイングを防ぐ |
| Extension メソッドは Handler に委譲しなければならない | インラインビジネスロジックを防ぐ |
| Handler は Spec インターフェースを実装しなければならない | 型付きサービスコントラクト |
| Handler は Result を返し、決してスローしてはならない | 一貫したエラーサーフェス |
Extension は singleton.ts からインポートしてはならない | 循環依存を防ぐ |
トラフィックライト方式(赤 → 緑 → 黄)
新機能を実装するときやバグを修正するときは、トラフィックライト方式に従ってください。
🔴 赤 — 失敗するテストを書く
まずテストを書きます。テストは失敗する必要があります。これにより、実装に着手する前に契約を定義します。
# Unit tests for generated classes
npx vitest run test/unit/sdk.test.ts
# → FAIL (new method doesn't exist yet)
# E2E test for the public API
bun scripts/e2e-test.ts
# → FAIL (method doesn't exist yet)
🟢 緑 — 実装する
domain-map.jsonにバインディングを追加します(ステージ 2)bun scripts/generate-sdk.tsを実行します(ステージ 3)- 正しい動作を検証するようにテストを更新します
npx vitest run # All tests pass
bun scripts/e2e-test.ts # E2E passes
🟡 黄 — リファクタリング/改善/見直し
テストが合格する状態を安全網として、次の作業を行います。
- 明瞭さを高めるためにリファクタリングします(ヘルパーの抽出、引数の簡素化など)
- エッジケースのテストを追加します
npx tscを実行して型安全性を検証しますbun scripts/validate-generated.tsを実行してパイプラインの整合性を検証します
コードベースの把握
コードベースを直接読んで、現在の状態を確認してください。主なエントリーポイントは次のとおりです。
- 公開インターフェース:
packages/sdk/src/index.tsから始めます。すべての公開エクスポートが一覧表示されています - 生成されたクラス:
packages/sdk/generated/src/— Stitch、Project、Screen、DesignSystem - パイプラインの生成物:
packages/sdk/generated/domain-map.json、packages/sdk/generated/tools-manifest.json - インフラストラクチャ:
packages/sdk/src/client.ts、packages/sdk/src/spec/errors.ts、packages/sdk/src/singleton.ts - テスト構成:ユニットテストは
packages/sdk/test/unit/、ライブテストはpackages/sdk/test/integration/にあります - 利用可能なコマンド:
package.jsonのscriptsフィールドを確認してください
ファイルやディレクトリツリーについて、キャッシュされた説明に頼らないでください。ソースを読んでください。
インポート規約
ESM との互換性のため、.js 拡張子を使用します。
import { StitchError } from "../../src/spec/errors.js"; // ✓
import { StitchError } from "../../src/spec/errors"; // ✗