@kvasar/google-stitch 0.1.8 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "openclaw-google-stitch",
3
3
  "name": "Google Stitch MCP",
4
- "version": "0.1.8",
4
+ "version": "0.1.10",
5
5
  "description": "Integrates Google Stitch MCP services into OpenClaw",
6
6
  "skills": ["skills"],
7
7
  "configSchema": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kvasar/google-stitch",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "OpenClaw plugin for Google Stitch UI generation, screen design, variants, and design systems",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
package/skills/SKILL.md CHANGED
@@ -86,14 +86,40 @@ Always extract:
86
86
 
87
87
  - projectId
88
88
  - prompt
89
- - deviceType
90
- - preferred model
89
+ - deviceType (optional)
90
+ - modelId (optional)
91
91
 
92
- Preferred model:
92
+ ## Supported device types:
93
93
 
94
- - `GEMINI_3_1_PRO`
94
+ - MOBILE
95
+ - DESKTOP
96
+ - TABLET
97
+ - AGNOSTIC
95
98
 
96
- ---
99
+ ### Preferred default:
100
+
101
+ - DESKTOP
102
+
103
+ #### Supported models:
104
+
105
+ - GEMINI_3_PRO
106
+ - GEMINI_3_FLASH
107
+ - GEMINI_3_1_PRO
108
+
109
+ #### Preferred default:
110
+
111
+ - GEMINI_3_1_PRO
112
+
113
+ Always structure the tool call as:
114
+
115
+ {
116
+ "projectId": "project-id",
117
+ "prompt": "Generate a modern SaaS dashboard with KPI cards and sidebar",
118
+ "deviceType": "DESKTOP",
119
+ "modelId": "GEMINI_3_1_PRO"
120
+ }
121
+
122
+ Never call the tool with only a string prompt.
97
123
 
98
124
  ### Screen editing
99
125
 
@@ -135,17 +161,44 @@ Recommended defaults:
135
161
 
136
162
  For brand consistency and theming:
137
163
 
164
+
165
+
138
166
  - `create_design_system`
167
+
168
+ Examples:
169
+
170
+ - Create a dark mode design system with Inter font and round corners
171
+ - Set up a design system with blue as the primary color
172
+ - Create a brand identity with Geist font and minimal roundness
173
+
139
174
  - `update_design_system`
175
+
176
+ Examples:
177
+
178
+ - Update the design system to use dark mode
179
+ - Change the font to Geist in our design system
180
+ - Update the roundness to fully rounded
181
+
182
+
140
183
  - `list_design_systems`
184
+
185
+ Examples:
186
+
187
+ - List all design systems in project 12345
188
+ - Show me the available design systems
189
+ - What design systems do I have?
190
+
191
+
141
192
  - `apply_design_system`
142
193
 
194
+ Applies a design system to one or more screens, modifying their appearance to match the system’s tokens (colors, fonts, shapes).
195
+
143
196
  Examples:
144
197
 
145
- - create dark theme
146
- - use Inter font
147
- - rounded buttons
148
- - apply brand colors
198
+ - Apply the blue design system to all screens in project 12345
199
+ - Restyle these screens with the brand identity
200
+ - Use design system abc to update my screens
201
+
149
202
 
150
203
  ---
151
204
 
@@ -1,6 +1,19 @@
1
1
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
2
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
3
3
 
4
+ export type DeviceType =
5
+ | "DEVICE_TYPE_UNSPECIFIED"
6
+ | "MOBILE"
7
+ | "DESKTOP"
8
+ | "TABLET"
9
+ | "AGNOSTIC";
10
+
11
+ export type ModelId =
12
+ | "MODEL_ID_UNSPECIFIED"
13
+ | "GEMINI_3_PRO"
14
+ | "GEMINI_3_FLASH"
15
+ | "GEMINI_3_1_PRO";
16
+
4
17
  export class StitchMCPClient {
5
18
  private client: Client;
6
19
  private transport: StreamableHTTPClientTransport;
@@ -41,12 +54,22 @@ export class StitchMCPClient {
41
54
  });
42
55
  }
43
56
 
44
- async generateScreen(prompt: string) {
45
- await this.connect();
57
+ async generateScreen(params: {
58
+ projectId: string;
59
+ prompt: string;
60
+ deviceType?: DeviceType;
61
+ modelId?: ModelId;
62
+ }) {
63
+ await this.connect();
46
64
 
47
- return this.client.callTool({
48
- name: "generate_screen_from_text",
49
- arguments: { prompt },
50
- });
51
- }
65
+ return this.client.callTool({
66
+ name: "generate_screen_from_text",
67
+ arguments: {
68
+ projectId: params.projectId,
69
+ prompt: params.prompt,
70
+ deviceType: params.deviceType,
71
+ modelId: params.modelId,
72
+ },
73
+ });
74
+ }
52
75
  }
@@ -1,7 +1,61 @@
1
- export const applyDesignSystemTool = (client:any) => ({
1
+ type SelectedScreenInstance = {
2
+ id: string;
3
+ sourceScreen: string;
4
+ };
5
+
6
+ type ApplyDesignSystemParams = {
7
+ projectId: string;
8
+ selectedScreenInstances: SelectedScreenInstance[];
9
+ assetId: string;
10
+ };
11
+
12
+ export const applyDesignSystemTool = (client: any) => ({
2
13
  name: "apply_design_system",
3
- description: "apply design system",
4
- async execute(_: string, params: any) {
5
- return await client.request?.("apply_design_system", params);
14
+ description:
15
+ "Applies a design system asset to one or more screens, updating colors, fonts, and shapes",
16
+
17
+ parameters: {
18
+ type: "object",
19
+ properties: {
20
+ projectId: {
21
+ type: "string",
22
+ description: "Required. Project ID without prefix."
23
+ },
24
+ selectedScreenInstances: {
25
+ type: "array",
26
+ description:
27
+ "Required. Screen instances to update from get_project.",
28
+ items: {
29
+ type: "object",
30
+ properties: {
31
+ id: {
32
+ type: "string",
33
+ description:
34
+ "Required. Screen instance ID (not source screen ID)."
35
+ },
36
+ sourceScreen: {
37
+ type: "string",
38
+ description:
39
+ "Required. Resource name format: projects/{project}/screens/{screen}"
40
+ }
41
+ },
42
+ required: ["id", "sourceScreen"]
43
+ }
44
+ },
45
+ assetId: {
46
+ type: "string",
47
+ description:
48
+ "Required. Design system asset ID without assets/ prefix."
49
+ }
50
+ },
51
+ required: [
52
+ "projectId",
53
+ "selectedScreenInstances",
54
+ "assetId"
55
+ ]
6
56
  },
7
- });
57
+
58
+ async execute(_: string, params: ApplyDesignSystemParams) {
59
+ return await client.request("apply_design_system", params);
60
+ }
61
+ });
@@ -1,7 +1,142 @@
1
- export const createDesignSystemTool = (client:any) => ({
1
+ type ColorMode =
2
+ | "COLOR_MODE_UNSPECIFIED"
3
+ | "LIGHT"
4
+ | "DARK";
5
+
6
+ type FontType =
7
+ | "FONT_UNSPECIFIED"
8
+ | "INTER"
9
+ | "DM_SANS"
10
+ | "GEIST";
11
+
12
+ type Roundness =
13
+ | "ROUNDNESS_UNSPECIFIED"
14
+ | "ROUND_FOUR"
15
+ | "ROUND_EIGHT"
16
+ | "ROUND_TWELVE"
17
+ | "ROUND_FULL";
18
+
19
+ type ColorVariant =
20
+ | "COLOR_VARIANT_UNSPECIFIED"
21
+ | "MONOCHROME"
22
+ | "NEUTRAL"
23
+ | "TONAL_SPOT"
24
+ | "VIBRANT"
25
+ | "EXPRESSIVE"
26
+ | "FIDELITY"
27
+ | "CONTENT"
28
+ | "RAINBOW"
29
+ | "FRUIT_SALAD";
30
+
31
+ type DesignTheme = {
32
+ colorMode: ColorMode;
33
+ headlineFont: FontType;
34
+ bodyFont: FontType;
35
+ labelFont?: FontType;
36
+ roundness: Roundness;
37
+ customColor: string;
38
+ colorVariant?: ColorVariant;
39
+ overridePrimaryColor?: string;
40
+ overrideSecondaryColor?: string;
41
+ overrideTertiaryColor?: string;
42
+ overrideNeutralColor?: string;
43
+ designMd?: string;
44
+ };
45
+
46
+ type DesignSystem = {
47
+ displayName: string;
48
+ theme: DesignTheme;
49
+ };
50
+
51
+ type CreateDesignSystemParams = {
52
+ designSystem: DesignSystem;
53
+ projectId?: string;
54
+ };
55
+
56
+ export const createDesignSystemTool = (client: any) => ({
2
57
  name: "create_design_system",
3
- description: "create design system",
4
- async execute(_: string, params: any) {
5
- return await client.request?.("create_design_system", params);
58
+ description:
59
+ "Creates a new design system with colors, typography, roundness, and theme tokens for a Stitch project",
60
+
61
+ parameters: {
62
+ type: "object",
63
+ properties: {
64
+ projectId: {
65
+ type: "string",
66
+ description:
67
+ "Optional. Project ID. If omitted, creates a global design asset."
68
+ },
69
+ designSystem: {
70
+ type: "object",
71
+ description: "Required. Design system definition.",
72
+ properties: {
73
+ displayName: {
74
+ type: "string",
75
+ description: "Required. Display name."
76
+ },
77
+ theme: {
78
+ type: "object",
79
+ properties: {
80
+ colorMode: {
81
+ type: "string",
82
+ enum: ["LIGHT", "DARK"]
83
+ },
84
+ headlineFont: {
85
+ type: "string",
86
+ enum: ["INTER", "DM_SANS", "GEIST"]
87
+ },
88
+ bodyFont: {
89
+ type: "string",
90
+ enum: ["INTER", "DM_SANS", "GEIST"]
91
+ },
92
+ labelFont: {
93
+ type: "string",
94
+ enum: ["INTER", "DM_SANS", "GEIST"]
95
+ },
96
+ roundness: {
97
+ type: "string",
98
+ enum: [
99
+ "ROUND_FOUR",
100
+ "ROUND_EIGHT",
101
+ "ROUND_TWELVE",
102
+ "ROUND_FULL"
103
+ ]
104
+ },
105
+ customColor: {
106
+ type: "string",
107
+ description: "Hex seed color"
108
+ },
109
+ colorVariant: {
110
+ type: "string",
111
+ enum: [
112
+ "MONOCHROME",
113
+ "NEUTRAL",
114
+ "TONAL_SPOT",
115
+ "VIBRANT",
116
+ "EXPRESSIVE"
117
+ ]
118
+ },
119
+ designMd: {
120
+ type: "string",
121
+ description: "Optional markdown design instructions"
122
+ }
123
+ },
124
+ required: [
125
+ "colorMode",
126
+ "headlineFont",
127
+ "bodyFont",
128
+ "roundness",
129
+ "customColor"
130
+ ]
131
+ }
132
+ },
133
+ required: ["displayName", "theme"]
134
+ }
135
+ },
136
+ required: ["designSystem"]
6
137
  },
7
- });
138
+
139
+ async execute(_: string, params: CreateDesignSystemParams) {
140
+ return await client.request("create_design_system", params);
141
+ }
142
+ });
@@ -1,28 +1,43 @@
1
- import { StitchMCPClient } from "../services/stitch-mcp-client.js";
1
+ import {
2
+ DeviceType,
3
+ ModelId,
4
+ StitchMCPClient
5
+ } from "../services/stitch-mcp-client.js";
2
6
  import fs from "node:fs";
3
7
  import path from "node:path";
4
8
  import os from "node:os";
5
9
 
6
- type StitchGenerateResponse = {
7
- html?: string;
8
- code?: string;
9
- jsx?: string;
10
- previewHtml?: string;
11
- result?: {
12
- html?: string;
13
- code?: string;
14
- jsx?: string;
15
- };
16
- imageUrl?: string;
17
- screenshotUrl?: string;
18
- previewUrl?: string;
19
- data?: {
20
- imageUrl?: string;
21
- screenshotUrl?: string;
22
- previewUrl?: string;
23
- base64Image?: string;
24
- rawBytes?: string;
10
+ type StitchResponse = {
11
+ screen?: {
12
+ name?: string;
13
+ title?: string;
14
+ prompt?: string;
15
+ screenshot?: {
16
+ url?: string;
17
+ bytes?: string;
18
+ };
19
+ htmlCode?: {
20
+ content?: string;
21
+ url?: string;
22
+ };
23
+ deviceType?: DeviceType;
24
+ width?: string;
25
+ height?: string;
25
26
  };
27
+ output_components?: Array<{
28
+ text?: string;
29
+ suggestion?: string;
30
+ design?: {
31
+ screenshot?: {
32
+ url?: string;
33
+ bytes?: string;
34
+ };
35
+ htmlCode?: {
36
+ content?: string;
37
+ url?: string;
38
+ };
39
+ };
40
+ }>;
26
41
  };
27
42
 
28
43
  export function generateScreenFromTextTool(client: StitchMCPClient) {
@@ -33,91 +48,100 @@ export function generateScreenFromTextTool(client: StitchMCPClient) {
33
48
  parameters: {
34
49
  type: "object",
35
50
  properties: {
51
+ projectId: {
52
+ type: "string",
53
+ description: "Required. Project ID without prefix."
54
+ },
36
55
  prompt: {
37
56
  type: "string",
38
57
  description: "Describe the UI screen to generate"
58
+ },
59
+ deviceType: {
60
+ type: "string",
61
+ enum: [
62
+ "DEVICE_TYPE_UNSPECIFIED",
63
+ "MOBILE",
64
+ "DESKTOP",
65
+ "TABLET",
66
+ "AGNOSTIC"
67
+ ]
68
+ },
69
+ modelId: {
70
+ type: "string",
71
+ enum: [
72
+ "MODEL_ID_UNSPECIFIED",
73
+ "GEMINI_3_PRO",
74
+ "GEMINI_3_FLASH",
75
+ "GEMINI_3_1_PRO"
76
+ ]
39
77
  }
40
78
  },
41
- required: ["prompt"]
79
+ required: ["projectId", "prompt"]
42
80
  },
43
81
 
44
- async execute(_id: string, params: { prompt: string }) {
45
- const result =
46
- (await client.generateScreen(
47
- params.prompt
48
- )) as StitchGenerateResponse;
82
+ async execute(
83
+ _id: string,
84
+ params: {
85
+ projectId: string;
86
+ prompt: string;
87
+ deviceType?: DeviceType;
88
+ modelId?: ModelId;
89
+ }
90
+ ) {
91
+ const result = (await client.generateScreen(params)) as StitchResponse;
92
+
93
+ const screen = result.screen;
94
+ const firstComponent = result.output_components?.[0];
49
95
 
50
- // Check for image URLs or base64 data
51
- const imageUrl = result.imageUrl || result.screenshotUrl || result.previewUrl ||
52
- result.data?.imageUrl || result.data?.screenshotUrl || result.data?.previewUrl;
53
- const base64Image = result.data?.base64Image;
54
- const rawBytes = result.data?.rawBytes;
96
+ const imageUrl =
97
+ screen?.screenshot?.url ||
98
+ firstComponent?.design?.screenshot?.url;
55
99
 
56
100
  if (imageUrl) {
57
- // If we have an image URL, return it as an image block
58
101
  return {
59
102
  content: [
60
103
  {
61
104
  type: "image",
62
105
  url: imageUrl,
63
- caption: "Generated by Google Stitch"
106
+ caption: screen?.title || "Generated by Google Stitch"
107
+ }
108
+ ]
109
+ };
110
+ }
111
+
112
+ const imageBytes =
113
+ screen?.screenshot?.bytes ||
114
+ firstComponent?.design?.screenshot?.bytes;
115
+
116
+ if (imageBytes) {
117
+ const tempFilePath = path.join(
118
+ os.tmpdir(),
119
+ `stitch-screen-${Date.now()}.png`
120
+ );
121
+
122
+ fs.writeFileSync(
123
+ tempFilePath,
124
+ Buffer.from(imageBytes, "base64")
125
+ );
126
+
127
+ return {
128
+ content: [
129
+ {
130
+ type: "image",
131
+ path: tempFilePath,
132
+ caption: screen?.title || "Generated by Google Stitch"
64
133
  }
65
134
  ]
66
135
  };
67
- } else if (base64Image || rawBytes) {
68
- // Decode and save base64 or raw bytes to a temporary file
69
- const tempDir = os.tmpdir();
70
- const tempFilePath = path.join(tempDir, `stitch-screen-${Date.now()}.png`);
71
-
72
- try {
73
- let imageData: Buffer;
74
- if (base64Image) {
75
- // Remove data URL prefix if present
76
- const cleanBase64 = base64Image.replace(/^data:image\/\w+;base64,/, '');
77
- imageData = Buffer.from(cleanBase64, 'base64');
78
- } else if (rawBytes) {
79
- // Assuming rawBytes is base64 encoded
80
- imageData = Buffer.from(rawBytes, 'base64');
81
- } else {
82
- throw new Error("No image data found");
83
- }
84
-
85
- fs.writeFileSync(tempFilePath, imageData);
86
-
87
- return {
88
- content: [
89
- {
90
- type: "image",
91
- path: tempFilePath,
92
- caption: "Generated by Google Stitch"
93
- }
94
- ]
95
- };
96
- } catch (error) {
97
- console.error("Failed to save image:", error);
98
- // Fall back to HTML if image processing fails
99
- }
100
136
  }
101
137
 
102
- // Fallback to HTML if no image data is available
103
138
  const html =
104
- result.html ??
105
- result.previewHtml ??
106
- result.result?.html ??
107
- (result.code
108
- ? `<pre style="white-space:pre-wrap;padding:16px">${escapeHtml(
109
- result.code
110
- )}</pre>`
111
- : result.jsx
112
- ? `<pre style="white-space:pre-wrap;padding:16px">${escapeHtml(
113
- result.jsx
114
- )}</pre>`
115
- : `<div style="padding:16px">
116
- <h3>Screen generated</h3>
117
- <pre>${escapeHtml(
118
- JSON.stringify(result, null, 2)
119
- )}</pre>
120
- </div>`);
139
+ screen?.htmlCode?.content ||
140
+ firstComponent?.design?.htmlCode?.content ||
141
+ `<div style="padding:16px">
142
+ <h3>${escapeHtml(screen?.title || "Generated screen")}</h3>
143
+ <p>${escapeHtml(firstComponent?.text || "")}</p>
144
+ </div>`;
121
145
 
122
146
  return {
123
147
  content: [