stitch-sdk-domain-design
$
npx mdskill add google-labs-code/stitch-sdk/stitch-sdk-domain-designDesigns domain model for Stitch SDK generation pipeline
- Maps MCP tools to domain classes and bindings in codegen
- Depends on tool schemas from manifest and Zod IR schema
- Validates output against structural rules and semantic projections
- Produces valid JSON driving the next stage of SDK development
SKILL.md
.github/skills/stitch-sdk-domain-designView on GitHub ↗
---
name: stitch-sdk-domain-design
description: Design the domain model for the Stitch SDK. Use when mapping MCP tools to domain classes and bindings in domain-map.json. This is Stage 2 of the generation pipeline.
---
# Stitch SDK Domain Design
This skill teaches you how to perform **Stage 2** of the generation pipeline: reading tool schemas and producing `domain-map.json` — the intermediate representation that drives codegen.
---
## Your Inputs
1. **`tools-manifest.json`** — raw MCP tool schemas captured from the server (includes `outputSchema`)
2. **`ir-schema.ts`** — Zod schema defining valid domain-map structure (the canonical contract)
3. **Existing `domain-map.json`** — the current IR (if extending, not starting fresh)
4. **The `stitch-sdk-development` skill** — for understanding the pipeline context
## Your Output
A valid `domain-map.json` with two sections: `classes` and `bindings`, validated by `ir-schema.ts`.
> [!IMPORTANT]
> Your output is validated **twice** by the codegen: structurally (Zod IR schema) and semantically (projection steps verified against `outputSchema` from the tools-manifest).
---
## Designing Classes
Each class represents a domain entity. Ask: "What noun does the user interact with?"
```json
{
"Stitch": {
"description": "Main entry point. Manages projects.",
"constructorParams": [],
"isRoot": true,
"factories": [
{
"method": "project",
"returns": "Project",
"description": "Create a Project handle from an ID."
}
]
}
}
```
### Key decisions:
| Field | Purpose | Example |
| ------------------- | --------------------------------------------------------- | ------------------------------------------------- |
| `constructorParams` | Fields stored on the instance | `["projectId", "screenId"]` |
| `fieldMapping` | Per-field data source mapping with optional `stripPrefix` | See below |
| `parentField` | Which param is injected from a parent class | `"projectId"` |
| `idField` | Which param the `.id` getter aliases | `"screenId"` |
| `factories` | Local factory methods (no API call) | `[{ "method": "project", "returns": "Project" }]` |
### Field Mapping
Use `fieldMapping` when a param needs a different source field, prefix stripping, or a fallback:
```json
{
"constructorParams": ["projectId", "screenId"],
"fieldMapping": {
"projectId": { "from": "name", "stripPrefix": "projects/" },
"screenId": {
"from": "id",
"fallback": { "field": "name", "splitOn": "/screens/" }
}
}
}
```
- **`stripPrefix`**: Removes a resource name prefix from the value
- **`fallback`**: If the primary field is missing, splits an alternate field on a delimiter
---
## Designing Bindings
Each binding maps one MCP tool to one class method. Ask: "Who owns this action?"
### Arg routing
| Type | Meaning | Code generated |
| ----------- | ------------------------ | ---------------------------------------------------------- |
| `self` | From `this.field` | `projectId: this.projectId` |
| `param` | From method parameter | `prompt: prompt` |
| `computed` | Template interpolation | `name: \`projects/${this.projectId}/screens/${screenId}\`` |
| `selfArray` | Wrap self field as array | `selectedScreenIds: [this.screenId]` |
Optional params use `"optional": true`. Renamed params use `"rename": "newName"`.
### Response Projections
The `returns.projection` array tells codegen how to navigate the API response. Each step is a `ProjectionStep`:
```typescript
{ prop: string; index?: number; each?: boolean; fallback?: string }
```
| Projection | Generated code | Use when |
| ----------------------------------------------------------------------------------------------------------- | ------------------------------------------- | ------------------------------- |
| `[]` (empty) | `raw` | Direct return (whole response) |
| `[{ "prop": "projects" }]` | `raw.projects` | Array inside object |
| `[{ "prop": "outputComponents", "index": 0 }, { "prop": "design" }, { "prop": "screens", "index": 0 }]` | `raw.outputComponents[0].design.screens[0]` | Deeply nested single item |
| `[{ "prop": "outputComponents", "each": true }, { "prop": "design" }, { "prop": "screens", "each": true }]` | `flatMap` chain | Collect all items across arrays |
| `[{ "prop": "screenshot" }, { "prop": "downloadUrl" }]` | `raw.screenshot.downloadUrl` | Navigate nested properties |
**Decision**: Use `"index": 0` when extracting a single item. Use `"each": true` when collecting all items (array result). You **cannot** use both on the same step.
> [!TIP]
> Every `prop` in a projection is validated against the tool's `outputSchema` at codegen time. If you typo a property name, codegen will fail with a diagnostic listing the available properties.
### Return class wrapping
When `returns.class` is set, the extracted data is wrapped in a domain class constructor:
```json
{
"returns": {
"class": "Screen",
"projection": [{ "prop": "screens" }],
"array": true
}
}
```
The codegen automatically spreads `parentField` into the data if the child class declares one.
### Cache-aware methods
Add a `cache` field with a structured `projection` to check `this.data` before calling the API:
```json
{
"cache": {
"projection": [{ "prop": "htmlCode" }, { "prop": "downloadUrl" }],
"description": "Use cached HTML download URL from generation response if available"
}
}
```
When the cached property is a nested object (like `File` with a `downloadUrl`), use multiple projection steps to drill into it.
Generated code:
```typescript
if (this.data?.htmlCode?.downloadUrl) return this.data?.htmlCode?.downloadUrl;
// ... else call API
```
---
## Decision Framework
When mapping a new tool, answer these questions:
1. **Which class?** Look at which fields the tool requires. If it needs `projectId` from `self`, it belongs on `Project` or `Screen`. If it needs nothing from self, it belongs on `Stitch`.
2. **Which method name?** Use the verb from the tool name, simplified. `generate_screen_from_text` → `generate`. `edit_screens` → `edit`.
3. **Arguments from self or param?** If the caller already has the data (because they're calling a method on themselves), use `self`. If they need to provide it, use `param`.
4. **How deep is the return?** Check the tool's `outputSchema` in `tools-manifest.json`. Build the `projection` array step-by-step to navigate to the useful data.
5. **Should it cache?** If the data is available from a previous response (like generation), add a cache field with the projection path.
---
## Validation
After editing `domain-map.json`:
```bash
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
```
If a projection is invalid, you'll see:
```
❌ Binding "Project.generate" projection step 2:
property "screenz" not found in outputSchema.
Available properties: screens, components, metadata
```
More from google-labs-code/stitch-sdk
- github-codebase-briefing>-
- stitch-sdk-bug-bashFind bugs in the Stitch SDK using a real API key. Covers standard functional edges and tricky situations.
- stitch-sdk-developmentDevelop the Stitch SDK. Covers the generation pipeline, dual modality (agent vs SDK), error handling, and Traffic Light (Red-Green-Yellow) implementation workflow. Use when adding features, fixing bugs, or understanding the architecture.
- stitch-sdk-pipelineRun the full Stitch SDK generation pipeline. Use when a new tool is added, or the SDK needs to be regenerated end-to-end.
- stitch-sdk-readmeGenerate or update the README for the Stitch SDK. Use the Bookstore Test structure and source the current API from the codebase. Use when the README needs to be written or updated.
- stitch-sdk-usageUse the Stitch SDK to generate, edit, and iterate on UI screens from text prompts, manage projects, and retrieve screen HTML/images. Use when the user wants to consume the SDK in their application.