Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 62 additions & 56 deletions packages/sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import { stitch } from "@google/stitch-sdk";

// STITCH_API_KEY must be set in the environment
const project = await stitch.createProject("My App");
const screen = await project.generate("A login page with email and password fields");
const screen = await project.generate(
"A login page with email and password fields",
);
const html = await screen.getHtml();
const imageUrl = await screen.getImage();
```
Expand Down Expand Up @@ -79,11 +81,11 @@ for (const variant of variants) {

`variantOptions` fields:

| Field | Type | Default | Description |
|---|---|---|---|
| `variantCount` | `number` | 3 | Number of variants (1–5) |
| `creativeRange` | `string` | `"EXPLORE"` | `"REFINE"`, `"EXPLORE"`, or `"REIMAGINE"` |
| `aspects` | `string[]` | all | `"LAYOUT"`, `"COLOR_SCHEME"`, `"IMAGES"`, `"TEXT_FONT"`, `"TEXT_CONTENT"` |
| Field | Type | Default | Description |
| --------------- | ---------- | ----------- | ------------------------------------------------------------------------- |
| `variantCount` | `number` | 3 | Number of variants (1–5) |
| `creativeRange` | `string` | `"EXPLORE"` | `"REFINE"`, `"EXPLORE"`, or `"REIMAGINE"` |
| `aspects` | `string[]` | all | `"LAYOUT"`, `"COLOR_SCHEME"`, `"IMAGES"`, `"TEXT_FONT"`, `"TEXT_CONTENT"` |

## Tool Client (Agent Usage)

Expand Down Expand Up @@ -116,45 +118,45 @@ The client auto-connects on the first `callTool` or `listTools` call. No explici

The root class. Manages projects.

| Method | Parameters | Returns | Description |
|---|---|---|---|
| `createProject(title)` | `title: string` | `Promise<Project>` | Create a new project |
| `projects()` | — | `Promise<Project[]>` | List all accessible projects |
| `project(id)` | `id: string` | `Project` | Reference a project by ID (no API call) |
| Method | Parameters | Returns | Description |
| ---------------------- | --------------- | -------------------- | --------------------------------------- |
| `createProject(title)` | `title: string` | `Promise<Project>` | Create a new project |
| `projects()` | — | `Promise<Project[]>` | List all accessible projects |
| `project(id)` | `id: string` | `Project` | Reference a project by ID (no API call) |

### `Project`

A Stitch project containing screens.

| Property | Type | Description |
|---|---|---|
| `id` | `string` | Alias for `projectId` |
| Property | Type | Description |
| ----------- | -------- | --------------------------------------- |
| `id` | `string` | Alias for `projectId` |
| `projectId` | `string` | Bare project ID (no `projects/` prefix) |

| Method | Parameters | Returns | Description |
|---|---|---|---|
| `generate(prompt, deviceType?)` | `prompt: string`, `deviceType?: DeviceType` | `Promise<Screen>` | Generate a screen from a text prompt |
| `screens()` | — | `Promise<Screen[]>` | List all screens in the project |
| `getScreen(screenId)` | `screenId: string` | `Promise<Screen>` | Retrieve a specific screen by ID |
| Method | Parameters | Returns | Description |
| ------------------------------- | ------------------------------------------- | ------------------- | ------------------------------------ |
| `generate(prompt, deviceType?)` | `prompt: string`, `deviceType?: DeviceType` | `Promise<Screen>` | Generate a screen from a text prompt |
| `screens()` | — | `Promise<Screen[]>` | List all screens in the project |
| `getScreen(screenId)` | `screenId: string` | `Promise<Screen>` | Retrieve a specific screen by ID |

`DeviceType`: `"MOBILE"` \| `"DESKTOP"` \| `"TABLET"` \| `"AGNOSTIC"`

### `Screen`

A generated UI screen. Provides access to HTML and screenshots.

| Property | Type | Description |
|---|---|---|
| `id` | `string` | Alias for `screenId` |
| `screenId` | `string` | Bare screen ID |
| `projectId` | `string` | Parent project ID |
| Property | Type | Description |
| ----------- | -------- | -------------------- |
| `id` | `string` | Alias for `screenId` |
| `screenId` | `string` | Bare screen ID |
| `projectId` | `string` | Parent project ID |

| Method | Parameters | Returns | Description |
|---|---|---|---|
| `edit(prompt, deviceType?, modelId?)` | `prompt: string` | `Promise<Screen>` | Edit the screen with a text prompt |
| `variants(prompt, variantOptions, deviceType?, modelId?)` | `prompt: string`, `variantOptions: object` | `Promise<Screen[]>` | Generate design variants |
| `getHtml()` | — | `Promise<string>` | Get the screen's HTML download URL |
| `getImage()` | — | `Promise<string>` | Get the screen's screenshot download URL |
| Method | Parameters | Returns | Description |
| --------------------------------------------------------- | ------------------------------------------ | ------------------- | ---------------------------------------- |
| `edit(prompt, deviceType?, modelId?)` | `prompt: string` | `Promise<Screen>` | Edit the screen with a text prompt |
| `variants(prompt, variantOptions, deviceType?, modelId?)` | `prompt: string`, `variantOptions: object` | `Promise<Screen[]>` | Generate design variants |
| `getHtml()` | — | `Promise<string>` | Get the screen's HTML download URL |
| `getImage()` | — | `Promise<string>` | Get the screen's screenshot download URL |

`getHtml()` and `getImage()` use cached data from the generation response when available. If the screen was loaded from `screens()` or `getScreen()`, they call the `get_screen` API automatically.

Expand All @@ -170,12 +172,12 @@ const result = await client.callTool<any>("tool_name", { arg: "value" });
await client.close();
```

| Method | Parameters | Returns | Description |
|---|---|---|---|
| `callTool<T>(name, args)` | `name: string`, `args: Record<string, any>` | `Promise<T>` | Call an MCP tool |
| `listTools()` | — | `Promise<{ tools }>` | List available tools |
| `connect()` | — | `Promise<void>` | Explicitly connect (auto-called by `callTool`) |
| `close()` | — | `Promise<void>` | Close the connection |
| Method | Parameters | Returns | Description |
| ------------------------- | ------------------------------------------- | -------------------- | ---------------------------------------------- |
| `callTool<T>(name, args)` | `name: string`, `args: Record<string, any>` | `Promise<T>` | Call an MCP tool |
| `listTools()` | — | `Promise<{ tools }>` | List available tools |
| `connect()` | — | `Promise<void>` | Explicitly connect (auto-called by `callTool`) |
| `close()` | — | `Promise<void>` | Close the connection |

### `StitchProxy`

Expand Down Expand Up @@ -212,10 +214,10 @@ import { stitch } from "@google/stitch-sdk";
const tool = stitch.toolMap.get("generate_screen_from_text");
if (tool) {
// Pre-parsed params — no JSON Schema parsing needed
const required = tool.params.filter(p => p.required);
const optional = tool.params.filter(p => !p.required);
console.log(required.map(p => p.name)); // ["projectId", "prompt"]
console.log(optional.map(p => p.name)); // ["deviceType", "modelId"]
const required = tool.params.filter((p) => p.required);
const optional = tool.params.filter((p) => !p.required);
console.log(required.map((p) => p.name)); // ["projectId", "prompt"]
console.log(optional.map((p) => p.name)); // ["deviceType", "modelId"]
}

// Iterate all tools
Expand All @@ -234,12 +236,12 @@ The raw `inputSchema` (`ToolInputSchema`) is also available on each entry. Stand

### Environment Variables

| Variable | Required | Description |
|---|---|---|
| `STITCH_API_KEY` | Yes (or use OAuth) | API key for authentication |
| `STITCH_ACCESS_TOKEN` | No | OAuth access token (alternative to API key) |
| `GOOGLE_CLOUD_PROJECT` | With OAuth | Google Cloud project ID |
| `STITCH_HOST` | No | Override the MCP server URL |
| Variable | Required | Description |
| ---------------------- | ------------------ | ------------------------------------------- |
| `STITCH_API_KEY` | Yes (or use OAuth) | API key for authentication |
| `STITCH_ACCESS_TOKEN` | No | OAuth access token (alternative to API key) |
| `GOOGLE_CLOUD_PROJECT` | With OAuth | Google Cloud project ID |
| `STITCH_HOST` | No | Override the MCP server URL |

### Explicit Configuration

Expand All @@ -256,13 +258,13 @@ const sdk = new Stitch(client);
const projects = await sdk.projects();
```

| Option | Type | Default | Description |
|---|---|---|---|
| `apiKey` | `string` | `STITCH_API_KEY` | API key |
| `accessToken` | `string` | `STITCH_ACCESS_TOKEN` | OAuth token |
| `projectId` | `string` | `GOOGLE_CLOUD_PROJECT` | Cloud project ID |
| `baseUrl` | `string` | `https://stitch.googleapis.com/mcp` | MCP server URL |
| `timeout` | `number` | `300000` | Request timeout (ms) |
| Option | Type | Default | Description |
| ------------- | -------- | ----------------------------------- | -------------------- |
| `apiKey` | `string` | `STITCH_API_KEY` | API key |
| `accessToken` | `string` | `STITCH_ACCESS_TOKEN` | OAuth token |
| `projectId` | `string` | `GOOGLE_CLOUD_PROJECT` | Cloud project ID |
| `baseUrl` | `string` | `https://stitch.googleapis.com/mcp` | MCP server URL |
| `timeout` | `number` | `300000` | Request timeout (ms) |

Authentication requires either `apiKey` or both `accessToken` and `projectId`.

Expand All @@ -278,8 +280,8 @@ try {
await project.screens();
} catch (error) {
if (error instanceof StitchError) {
console.error(error.code); // "UNKNOWN_ERROR"
console.error(error.message); // Human-readable description
console.error(error.code); // "UNKNOWN_ERROR"
console.error(error.message); // Human-readable description
console.error(error.recoverable); // false
}
}
Expand All @@ -289,6 +291,10 @@ Error codes: `AUTH_FAILED`, `NOT_FOUND`, `PERMISSION_DENIED`, `RATE_LIMITED`, `N

---

## Examples

- [Batch Design Generation](./examples/batch-generation/README.md) - Generate multiple designs in parallel from a JSON list of prompts.

## Disclaimer

This is not an officially supported Google product. This project is not
Expand All @@ -297,4 +303,4 @@ Program](https://bughunters.google.com/open-source-security).

## License

Apache 2.0 — see [LICENSE](LICENSE) for details.
Apache 2.0 — see [LICENSE](LICENSE) for details.
24 changes: 24 additions & 0 deletions packages/sdk/examples/batch-generation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Batch Design Generation

This script demonstrates how to read a list of prompts from a JSON file and generate Stitch UI screens in parallel using `Promise.allSettled`.

This is a Tier 1 (Script) example because it is deterministic and requires no agentic decision-making.

## Prerequisites

1. Set your `STITCH_API_KEY` environment variable.
2. Build the SDK first (from the root directory):
```bash
npm run build
```

## Running the Example

Run this script directly from this directory:

```bash
cd packages/sdk/examples/batch-generation
STITCH_API_KEY=your_key bun index.ts
```

The script will read prompts from `prompts.json`, create a single project, generate all designs in parallel, and output a summary report with the generated screen IDs and image URLs.
60 changes: 60 additions & 0 deletions packages/sdk/examples/batch-generation/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { stitch } from "@google/stitch-sdk";
import fs from "fs/promises";
import path from "path";
import "../_require-key.js";

console.log("Reading prompts from prompts.json...");
const promptsPath = path.join(process.cwd(), "prompts.json");
let promptsFile;
try {
promptsFile = await fs.readFile(promptsPath, "utf-8");
} catch (error) {
console.error(
"Could not read prompts.json. Ensure you run this from the batch-generation directory.",
);
process.exit(1);
}

const prompts: string[] = JSON.parse(promptsFile);
console.log(
`Found ${prompts.length} prompts. Generating designs in parallel...`,
);

const project = await stitch.createProject("Batch Generation Example");
console.log(`Created project ${project.id}`);

const results = await Promise.allSettled(
prompts.map(async (prompt, index) => {
console.log(
`[${index + 1}/${prompts.length}] Starting: "${prompt.slice(0, 30)}..."`,
);
const screen = await project.generate(prompt);
const htmlUrl = await screen.getHtml();
const imageUrl = await screen.getImage();
return { prompt, screenId: screen.id, htmlUrl, imageUrl };
}),
);

console.log("\n=== Generation Results ===");
const successful = results
.filter((r) => r.status === "fulfilled")
.map((r) => (r as PromiseFulfilledResult<any>).value);
const failed = results
.filter((r) => r.status === "rejected")
.map((r) => (r as PromiseRejectedResult).reason);

console.log(`✅ Successfully generated: ${successful.length}`);
console.log(`❌ Failed: ${failed.length}`);

for (const success of successful) {
console.log(`\n- Prompt: "${success.prompt}"`);
console.log(` Screen ID: ${success.screenId}`);
console.log(` Image URL: ${success.imageUrl}`);
}

if (failed.length > 0) {
console.log("\nErrors:");
for (const error of failed) {
console.error(" ", error);
}
}
5 changes: 5 additions & 0 deletions packages/sdk/examples/batch-generation/prompts.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[
"A modern landing page for a SaaS startup with a hero section and pricing table",
"A user profile settings dashboard with form fields for email, password, and notifications",
"A mobile app home screen for a fitness tracker showing daily steps and heart rate"
]
Loading