@appscreenshotstudio/mcp 0.7.0 → 0.8.1

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.
package/dist/index.js CHANGED
@@ -38,6 +38,7 @@ const DEVICES = [
38
38
  { id: 'ipad-13', name: 'iPad Pro 13"', width: 2064, height: 2752, category: 'ipad', required: true },
39
39
  { id: 'android-phone', name: 'Android Phone', width: 1080, height: 2340, category: 'android-phone', required: true },
40
40
  { id: 'pixel-11-pro', name: 'Google Pixel 11 Pro', width: 1280, height: 2856, category: 'android-phone', required: false },
41
+ { id: 'galaxy-s26-ultra', name: 'Samsung Galaxy S26 Ultra', width: 1440, height: 3120, category: 'android-phone', required: false },
41
42
  { id: 'android-tablet-10', name: 'Android Tablet 7"', width: 1200, height: 1920, category: 'android-tablet', required: true },
42
43
  { id: 'android-tablet-large', name: 'Android Tablet 10"', width: 1600, height: 2560, category: 'android-tablet', required: false },
43
44
  { id: 'apple-watch-ultra', name: 'Apple Watch Ultra 2', width: 410, height: 502, category: 'apple-watch', required: true },
@@ -45,7 +46,7 @@ const DEVICES = [
45
46
  const VALID_DEVICE_IDS = DEVICES.map(d => d.id);
46
47
  // Aliases callers commonly pass instead of the canonical id: display-name
47
48
  // derivations ("iPhone 16 Pro Max" -> iphone-16-pro-max) and legacy slugs.
48
- // Mirrors MARKETING_TO_SPEC in lib/device-specs.ts keep the two in sync.
49
+ // Mirrors MARKETING_TO_SPEC in lib/device-specs.ts. Keep the two in sync.
49
50
  const DEVICE_ALIASES = {
50
51
  'iphone-16-pro': 'iphone-6.9',
51
52
  'iphone-16-pro-max': 'iphone-6.9',
@@ -153,18 +154,14 @@ function buildDesignMessage(input, hasScreenshotImages = false) {
153
154
  parts.push('\nUse the research context above to create screenshots that accurately represent this specific app. Headlines, features, and visual style should reflect what the app actually does and looks like.');
154
155
  }
155
156
  if (hasScreenshotImages) {
156
- parts.push(`\nMy app screenshots are attached they are placed into the device frames automatically.`);
157
+ parts.push(`\nMy app screenshots are attached, and they are placed into the device frames automatically.`);
157
158
  }
158
159
  else {
159
- parts.push(`\nAll device mockups should have screenshotImage: null the developer will upload actual app screenshots later.`);
160
+ parts.push(`\nAll device mockups should have screenshotImage: null. The developer will upload actual app screenshots later.`);
160
161
  }
161
162
  parts.push(`Please include projectMeta with brand colors, mood, appCategory, and a rich globalVisualTheme description.`);
162
163
  return parts.join('\n');
163
164
  }
164
- /** Read local image files into the chat API's images payload. Same file
165
- * handling as upload-screenshots; kind rides through to server-side
166
- * placement (screenshots fill the generated device frames, mascots get
167
- * placed decoratively around the phones). */
168
165
  function readImagesForChat(images) {
169
166
  const payload = [];
170
167
  const errors = [];
@@ -190,54 +187,63 @@ function readImagesForChat(images) {
190
187
  /** Shared images param for generate-screenshots and edit-screenshots. */
191
188
  const chatImagesSchema = z.array(z.object({
192
189
  file_path: z.string().describe('Absolute path to an image file on the local filesystem (PNG, JPG, or WEBP)'),
193
- kind: z.enum(['screenshot', 'mascot']).default('screenshot')
194
- .describe("'screenshot' = real app UI, automatically placed inside the generated device frames (in attachment order). 'mascot' = the app's character/mascot, placed decoratively around the phones (peeking from behind the hook card's phone, beside or in a corner on the closing card). Use a transparent PNG for mascots."),
190
+ kind: z.enum(['screenshot', 'mascot', 'reference']).default('screenshot')
191
+ .describe("'screenshot' = real app UI, automatically placed inside the generated device frames (in attachment order). 'mascot' = the app's character/mascot, placed decoratively around the phones (peeking from behind the hook card's phone, beside or in a corner on the closing card); use a transparent PNG. 'reference' = a look to match rather than content: a competitor's App Store listing, a design you want the style of, any image that already shows a phone with a headline above it. A reference is read for taste and is NEVER placed inside a frame, which is the point: wrapping a finished marketing card in a device frame puts a phone inside a phone."),
195
192
  })).max(5).optional()
196
- .describe('Images to attach to this generation. App screenshots land inside the device frames automatically; a mascot gets placed around the phones. Both survive later edits and regenerations.');
193
+ .describe('Images to attach to this generation. App screenshots land inside the device frames automatically; a mascot gets placed around the phones; a reference only informs the design. All survive later edits and regenerations.');
197
194
  // ─── MCP Server ─────────────────────────────────────────────────────────────────
195
+ // Read from package.json rather than a literal. This is the version a client
196
+ // sees in the MCP handshake, and a hand-synced literal silently sat at 0.6.1
197
+ // through the 0.6.2 release, then at 0.6.3 through 0.7.0 and 0.8.0. A comment
198
+ // asking the next person to remember has now failed twice, so stop asking.
199
+ const PKG_VERSION = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json'), 'utf-8')).version;
198
200
  const server = new McpServer({
199
201
  name: 'appscreenshotstudio',
200
- // Keep in step with package.json: this is the version a client sees in the
201
- // MCP handshake, and it silently sat at 0.6.1 through the 0.6.2 release.
202
- version: '0.6.3',
202
+ version: PKG_VERSION,
203
203
  });
204
204
  // Tool 1: generate-screenshots
205
205
  server.registerTool('generate-screenshots', {
206
206
  title: 'Generate App Store Screenshots',
207
- description: `Create a complete set of App Store screenshot designs for an app. Attach real app screenshots via the images param and they are placed inside the device frames automatically; attach a mascot/character image (kind: "mascot", transparent PNG) and it gets placed around the phones. Returns a project URL where the developer can preview, refine, and export final PNGs.
208
-
209
- IMPORTANT: Before calling this tool, research the user's codebase to populate the codebase_context parameter. Search for: package.json/README (app name & description), theme/color config files (brand colors), route definitions (key screens), marketing copy (value proposition), and App Store metadata. The more context you provide, the better the screenshots will be. Call prepare-screenshot-brief first if you need a research checklist.
210
-
211
- The AI picks a narrative arc that fits the app's sell angle (trust-first for finance/health, visual-first for creative/lifestyle, problem-first for pain-relief apps, numbers-led for SaaS/analytics, community-first for social, feature-forward for multi-mode apps) and assigns a job to each card: HOOK → EDUCATE → PROVE → CONVERT.
212
-
213
- 10 layout types available per card:
214
- - text-top-device-bottom: headline top, flat device bottom (feature/educate)
215
- - text-top-device-tilted: headline left, tilted device right (or mirrored)
216
- - device-hero: headline + large centered device, product-forward
217
- - social-proof: stars + quote + laurel stat, no device (testimonial-led)
218
- - review-clip: rating-statement headline + stars + quote + bottom-clipped device
219
- - screen-hero: top-clipped device + centered headline + optional trust stat
220
- - lifestyle-hero: full-bleed photo background + text overlay (no device)
221
- - stats-hero: giant centered stat(s), no device, numbers-led
222
- - metric-badge: centered device + chunky "achievement card" floating over its screen
223
- - annotated-feature: tilted device + side callout chip linked by a connector line
224
-
225
- Each card can carry auto-positioned compound fields: statRow, laurelStat, pressBanner, starRating, quote, credential ("FDIC insured"-style trust pills), guarantee ("Cancel anytime"-style risk-reversal pills). Trust signals are opt-in: supply the real number, quote or press name, or the card ships clean rather than inventing one.
226
-
227
- Panoramic backgrounds slice one wide AI-generated or Pexels image across multiple cards for cohesion. Panoramic element spans stretch a foreground image or device across adjacent cards. Note: if the design comes back with a panoramic background, the chat only tags the cards; call generate-panoramic-background afterwards to actually create and slice the wide image.
228
-
229
- Design features:
230
- - Rich text with per-word color, weight, italic, underline, highlight pills, gradient fills, stroke outlines, and emoji
231
- - Device perspective variants: flat, left-15/30, right-15/30, isometric, top-down, landscape
232
- - 77 decorative shapes across 13 categories (nature, weather, celebration, social, tech, health, food, travel, abstract, finance, education, pets, emoji), plus 17 core geometric shapes
233
- - Laurel stats: wing-left + wing-right shapes flanking a hero number
234
-
235
- App Store 60/40 rule enforced: minimum 60% of cards must show a device mockup, maximum 40% can be marketing-only.
236
-
207
+ annotations: {
208
+ // Creates a new project; never overwrites an existing one.
209
+ readOnlyHint: false,
210
+ destructiveHint: false,
211
+ openWorldHint: false,
212
+ },
213
+ description: `Create a complete set of App Store screenshot designs for an app. Attach real app screenshots via the images param and they are placed inside the device frames automatically; attach a mascot/character image (kind: "mascot", transparent PNG) and it gets placed around the phones. Returns a project URL where the developer can preview, refine, and export final PNGs.
214
+
215
+ IMPORTANT: Before calling this tool, research the user's codebase to populate the codebase_context parameter. Search for: package.json/README (app name & description), theme/color config files (brand colors), route definitions (key screens), marketing copy (value proposition), and App Store metadata. The more context you provide, the better the screenshots will be. Call prepare-screenshot-brief first if you need a research checklist.
216
+
217
+ The AI picks a narrative arc that fits the app's sell angle (trust-first for finance/health, visual-first for creative/lifestyle, problem-first for pain-relief apps, numbers-led for SaaS/analytics, community-first for social, feature-forward for multi-mode apps) and assigns a job to each card: HOOK → EDUCATE → PROVE → CONVERT.
218
+
219
+ 10 layout types available per card:
220
+ - text-top-device-bottom: headline top, flat device bottom (feature/educate)
221
+ - text-top-device-tilted: headline left, tilted device right (or mirrored)
222
+ - device-hero: headline + large centered device, product-forward
223
+ - social-proof: stars + quote + laurel stat, no device (testimonial-led)
224
+ - review-clip: rating-statement headline + stars + quote + bottom-clipped device
225
+ - screen-hero: top-clipped device + centered headline + optional trust stat
226
+ - lifestyle-hero: full-bleed photo background + text overlay (no device)
227
+ - stats-hero: giant centered stat(s), no device, numbers-led
228
+ - metric-badge: centered device + chunky "achievement card" floating over its screen
229
+ - annotated-feature: tilted device + side callout chip linked by a connector line
230
+
231
+ Each card can carry auto-positioned compound fields: statRow, laurelStat, pressBanner, starRating, quote, credential ("FDIC insured"-style trust pills), guarantee ("Cancel anytime"-style risk-reversal pills). Trust signals are opt-in: supply the real number, quote or press name, or the card ships clean rather than inventing one.
232
+
233
+ Panoramic backgrounds slice one wide AI-generated or Pexels image across multiple cards for cohesion. Panoramic element spans stretch a foreground image or device across adjacent cards. Note: if the design comes back with a panoramic background, the chat only tags the cards; call generate-panoramic-background afterwards to actually create and slice the wide image.
234
+
235
+ Design features:
236
+ - Rich text with per-word color, weight, italic, underline, highlight pills, gradient fills, stroke outlines, and emoji
237
+ - Device perspective variants: flat, left-15/30, right-15/30, isometric, top-down, landscape
238
+ - 77 decorative shapes across 13 categories (nature, weather, celebration, social, tech, health, food, travel, abstract, finance, education, pets, emoji), plus 17 core geometric shapes
239
+ - Laurel stats: wing-left + wing-right shapes flanking a hero number
240
+
241
+ App Store 60/40 rule enforced: minimum 60% of cards must show a device mockup, maximum 40% can be marketing-only.
242
+
237
243
  Costs 5 credits per generation.`,
238
244
  inputSchema: z.object({
239
245
  app_name: z.string().describe('Name of the app'),
240
- app_description: z.string().describe('What the app does 1-3 sentences'),
246
+ app_description: z.string().describe('What the app does, in 1-3 sentences'),
241
247
  features: z.array(z.string()).max(10).optional()
242
248
  .describe('Key features in order of importance. The first 2-3 will be highlighted most prominently.'),
243
249
  brand_colors: z.object({
@@ -246,7 +252,7 @@ Costs 5 credits per generation.`,
246
252
  accent: z.string().optional().describe('Accent color as hex'),
247
253
  }).optional().describe('Brand colors to use in the design'),
248
254
  mood: z.string().optional()
249
- .describe('Design mood e.g. "energetic", "calm", "minimal", "bold", "professional", "playful"'),
255
+ .describe('Design mood, e.g. "energetic", "calm", "minimal", "bold", "professional", "playful"'),
250
256
  device_id: z.string().default('iphone-6.9')
251
257
  .describe(`Target device. Options: ${VALID_DEVICE_IDS.join(', ')}. Default: iphone-6.9 (iPhone 16 Pro Max)`),
252
258
  count: z.number().min(3).max(10).default(5)
@@ -255,27 +261,27 @@ Costs 5 credits per generation.`,
255
261
  .describe('Narrative structure for the screenshots. "auto" lets the AI choose the best flow.'),
256
262
  codebase_context: z.object({
257
263
  readme_summary: z.string().optional()
258
- .describe('Summary of the app from README or docs what does it do and why?'),
264
+ .describe('Summary of the app from README or docs: what does it do and why?'),
259
265
  key_screens: z.array(z.string()).max(15).optional()
260
- .describe('Main screens/views in the app e.g. "Dashboard with activity feed", "Settings with theme toggle"'),
266
+ .describe('Main screens/views in the app, e.g. "Dashboard with activity feed", "Settings with theme toggle"'),
261
267
  color_tokens: z.record(z.string()).optional()
262
- .describe('Brand/theme colors found in code e.g. {"primary": "#7C3AED", "background": "#0F172A"}'),
268
+ .describe('Brand/theme colors found in code, e.g. {"primary": "#7C3AED", "background": "#0F172A"}'),
263
269
  target_audience: z.string().optional()
264
- .describe('Who the app is for e.g. "busy professionals who want to track habits"'),
270
+ .describe('Who the app is for, e.g. "busy professionals who want to track habits"'),
265
271
  app_category: z.string().optional()
266
- .describe('App category e.g. fitness, finance, social, productivity, food, travel, health, education'),
272
+ .describe('App category, e.g. fitness, finance, social, productivity, food, travel, health, education'),
267
273
  competitive_edge: z.string().optional()
268
274
  .describe('What makes this app unique vs competitors'),
269
275
  app_store_description: z.string().optional()
270
276
  .describe('Existing App Store/Play Store description if found in the codebase'),
271
277
  tech_stack: z.string().optional()
272
- .describe('Tech stack e.g. "React Native", "SwiftUI", "Flutter". Useful for developer-tool apps.'),
278
+ .describe('Tech stack, e.g. "React Native", "SwiftUI", "Flutter". Useful for developer-tool apps.'),
273
279
  ui_style: z.string().optional()
274
- .describe('UI style observations e.g. "dark mode with neon accents", "clean minimal with lots of whitespace"'),
280
+ .describe('UI style observations, e.g. "dark mode with neon accents", "clean minimal with lots of whitespace"'),
275
281
  primary_user_flow: z.string().optional()
276
- .describe('The main user journey e.g. "Sign up → Create project → Invite team → Track progress"'),
282
+ .describe('The main user journey, e.g. "Sign up → Create project → Invite team → Track progress"'),
277
283
  }).optional()
278
- .describe('Context gathered from researching the app codebase. Dramatically improves screenshot quality the more detail here, the better the output.'),
284
+ .describe('Context gathered from researching the app codebase. Dramatically improves screenshot quality. The more detail here, the better the output.'),
279
285
  images: chatImagesSchema,
280
286
  }),
281
287
  }, async (input) => {
@@ -350,48 +356,54 @@ Costs 5 credits per generation.`,
350
356
  // Tool 2: edit-screenshots
351
357
  server.registerTool('edit-screenshots', {
352
358
  title: 'Edit Screenshot Designs',
353
- description: `Make changes to an existing screenshot project. Use natural language to describe what you want to change. Costs 5 credits per edit. You can also attach images: app screenshots fill the device frames of regenerated cards, a mascot (kind: "mascot") gets placed around the phones.
354
-
355
- What you can change:
356
- - Text: headlines, subtitles, badge text, font size, font family (Inter, Poppins, Montserrat, DM Sans, Space Grotesk, etc.)
357
- - Text styling: per-word color, bold, italic, underline, highlight pills (colored background behind words), gradient text, text stroke outlines
358
- - Colors: brand palette, gradient backgrounds, accent colors, text colors. A set can rotate MULTIPLE accents card by card ("pink, then coral, then brick") and each card keeps its own highlight colour.
359
- - Background textures (ask for one explicitly, they are never added on their own): diagonal-stripe, crosshatch, checkerboard, zigzag, hairline-grid, dot-grid, waves, grain, radial-rays, concentric-circles. Kept subtle by design so they cannot affect headline contrast. Tiled textures run continuously across the whole set; radial-rays and concentric-circles can be centred on the middle of the set so the pattern fans out across every card.
360
- - Layouts (10 types): text-top-device-bottom, text-top-device-tilted, device-hero, social-proof, review-clip, screen-hero, lifestyle-hero, stats-hero, metric-badge, annotated-feature. The AI picks a narrative arc (HOOK → EDUCATE → PROVE → CONVERT) across the set.
361
- - Layout params: deviceScale (small/medium/large), deviceSide (left/right), textPosition (above/below), textAlign (left/center)
362
- - Device mockups: perspective variants (flat, left-15, right-15, left-30, right-30, isometric, top-down, landscape-left, landscape-right), 2D rotation, resize, reposition
363
- - Frame color: recolor the device frame — "natural" (default), "black", "white", "gold". Examples: "make the iPhone gold", "black titanium finish", "white iPhone". Requires Growth plan or higher.
364
- - Switch devices: "make this for Apple Watch" / "duplicate for Android tablet" clones the project at the target device's canvas size
365
- - Add/remove cards: add a social proof card, remove card 3, add a marketing title card
366
- - Compound fields (auto-positioned): statRow, laurelStat, pressBanner, starRating, quote, credential, guarantee
367
- - Panoramic backgrounds: one wide AI or Pexels image sliced across multiple cards ("pano the background across cards 1-3"). The edit only tags the cards; call generate-panoramic-background afterwards to create and slice the wide image.
368
- - Panoramic element spans: stretch a foreground image, device-mockup, or shape across adjacent cards ("pano the device across cards 1-2")
369
- - Floating elements: add/edit badges, star ratings
370
- - Shapes: glow orbs, waves, blobs, rounded rectangles, circles, custom SVG paths
371
- - Decorative shapes: 77 library shapes leaf, flower, cloud, sparkle, heart, rocket, trophy, crown, coffee-cup, airplane, dollar-sign, paw-print, wing-left/wing-right (for laurels), and many more
372
- - Backgrounds: solid, subtle-gradient, rich-gradient, photo (Pexels), or ai-generated; change gradient colors/angle; set a backgroundPrompt for AI-generated
373
- - Style: shadows, opacity, border radius, rotation, blur
374
-
375
- What isn't supported (the AI will flag these in unsupportedAsks):
376
- - Multiple devices side-by-side in a single card (e.g. iPhone + Watch in one scene) — each card renders one device. Use the duplicate-for-device workflow instead, or split across cards.
377
- - Uploading a specific user-supplied screenshot into a mockup use upload-screenshots tool first, then reference the uploaded project.
378
-
379
- Example edit messages:
380
- - "Make the headlines larger and use Bebas Neue font"
381
- - "Change the color scheme to blue (#2563EB) across all cards"
382
- - "Add a social proof card with 5 stars and a testimonial quote"
383
- - "Tilt the phone on card 2 to the left"
384
- - "Pano the background photo across cards 1-3"
385
- - "Stretch the device on card 1 across into card 2"
386
- - "Make this project for Apple Watch"
387
- - "Recolor the iPhone frame to gold across all cards"
388
- - "Replace card 3 with a CTA card saying Download Free"
389
- - "Switch card 2 to device-hero layout with statRow showing our three key metrics"
390
- - "Add decorative leaf and sparkle shapes scattered in the background"
359
+ annotations: {
360
+ // Rewrites cards in place, so a bad edit costs the previous design.
361
+ readOnlyHint: false,
362
+ destructiveHint: true,
363
+ openWorldHint: false,
364
+ },
365
+ description: `Make changes to an existing screenshot project. Use natural language to describe what you want to change. Costs 5 credits per edit. You can also attach images: app screenshots fill the device frames of regenerated cards, a mascot (kind: "mascot") gets placed around the phones.
366
+
367
+ What you can change:
368
+ - Text: headlines, subtitles, badge text, font size, font family (Inter, Poppins, Montserrat, DM Sans, Space Grotesk, etc.)
369
+ - Text styling: per-word color, bold, italic, underline, highlight pills (colored background behind words), gradient text, text stroke outlines
370
+ - Colors: brand palette, gradient backgrounds, accent colors, text colors. A set can rotate MULTIPLE accents card by card ("pink, then coral, then brick") and each card keeps its own highlight colour.
371
+ - Background textures (ask for one explicitly, they are never added on their own): diagonal-stripe, crosshatch, checkerboard, zigzag, hairline-grid, dot-grid, waves, grain, radial-rays, concentric-circles. Kept subtle by design so they cannot affect headline contrast. Tiled textures run continuously across the whole set; radial-rays and concentric-circles can be centred on the middle of the set so the pattern fans out across every card.
372
+ - Layouts (10 types): text-top-device-bottom, text-top-device-tilted, device-hero, social-proof, review-clip, screen-hero, lifestyle-hero, stats-hero, metric-badge, annotated-feature. The AI picks a narrative arc (HOOK → EDUCATE → PROVE → CONVERT) across the set.
373
+ - Layout params: deviceScale (small/medium/large), deviceSide (left/right), textPosition (above/below), textAlign (left/center)
374
+ - Device mockups: perspective variants (flat, left-15, right-15, left-30, right-30, isometric, top-down, landscape-left, landscape-right), 2D rotation, resize, reposition
375
+ - Frame color: recolor the device frame: "natural" (default), "black", "white", "gold". Examples: "make the iPhone gold", "black titanium finish", "white iPhone". Requires Growth plan or higher.
376
+ - Switch devices: "make this for Apple Watch" / "duplicate for Android tablet" clones the project at the target device's canvas size
377
+ - Add/remove cards: add a social proof card, remove card 3, add a marketing title card
378
+ - Compound fields (auto-positioned): statRow, laurelStat, pressBanner, starRating, quote, credential, guarantee
379
+ - Panoramic backgrounds: one wide AI or Pexels image sliced across multiple cards ("pano the background across cards 1-3"). The edit only tags the cards; call generate-panoramic-background afterwards to create and slice the wide image.
380
+ - Panoramic element spans: stretch a foreground image, device-mockup, or shape across adjacent cards ("pano the device across cards 1-2")
381
+ - Floating elements: add/edit badges, star ratings
382
+ - Shapes: glow orbs, waves, blobs, rounded rectangles, circles, custom SVG paths
383
+ - Decorative shapes: 77 library shapes: leaf, flower, cloud, sparkle, heart, rocket, trophy, crown, coffee-cup, airplane, dollar-sign, paw-print, wing-left/wing-right (for laurels), and many more
384
+ - Backgrounds: solid, subtle-gradient, rich-gradient, photo (Pexels), or ai-generated; change gradient colors/angle; set a backgroundPrompt for AI-generated
385
+ - Style: shadows, opacity, border radius, rotation, blur
386
+
387
+ What isn't supported (the AI will flag these in unsupportedAsks):
388
+ - Multiple devices side-by-side in a single card (e.g. iPhone + Watch in one scene). Each card renders one device. Use the duplicate-for-device workflow instead, or split across cards.
389
+ - Uploading a specific user-supplied screenshot into a mockup: use upload-screenshots tool first, then reference the uploaded project.
390
+
391
+ Example edit messages:
392
+ - "Make the headlines larger and use Bebas Neue font"
393
+ - "Change the color scheme to blue (#2563EB) across all cards"
394
+ - "Add a social proof card with 5 stars and a testimonial quote"
395
+ - "Tilt the phone on card 2 to the left"
396
+ - "Pano the background photo across cards 1-3"
397
+ - "Stretch the device on card 1 across into card 2"
398
+ - "Make this project for Apple Watch"
399
+ - "Recolor the iPhone frame to gold across all cards"
400
+ - "Replace card 3 with a CTA card saying Download Free"
401
+ - "Switch card 2 to device-hero layout with statRow showing our three key metrics"
402
+ - "Add decorative leaf and sparkle shapes scattered in the background"
391
403
  - "Make 'Every' underlined and italic in the headline"`,
392
404
  inputSchema: z.object({
393
405
  project_id: z.string().describe('Project ID from a previous generate-screenshots call'),
394
- message: z.string().describe('What to change use natural language'),
406
+ message: z.string().describe('What to change, in natural language'),
395
407
  card_indices: z.array(z.number().int().min(0)).max(10).optional()
396
408
  .describe('Target specific cards by index (0-based). e.g. [0] for card 1, [2,3] for cards 3-4. Max 10 indices. Omit to apply changes to all cards.'),
397
409
  codebase_context: z.object({
@@ -468,6 +480,13 @@ Example edit messages:
468
480
  // Tool 3: render-screenshots
469
481
  server.registerTool('render-screenshots', {
470
482
  title: 'Render Screenshots to PNG',
483
+ annotations: {
484
+ // Re-rendering the same project state yields the same PNGs.
485
+ readOnlyHint: false,
486
+ destructiveHint: false,
487
+ idempotentHint: true,
488
+ openWorldHint: false,
489
+ },
471
490
  description: 'Export a screenshot project to high-resolution PNG files at exact App Store dimensions. Returns download URLs for each card; URLs stay valid for 7 days, so save the PNGs to disk promptly (re-rendering is free if a URL has expired). Free (no credit cost). Rendering blocks until the PNGs are ready: expect roughly 20-40s for iPhone sets and 60-120s for iPad (the larger canvas renders slower), so allow up to ~2 minutes before treating it as failed. Note: device mockups will show empty frames unless app screenshots have been uploaded via upload-screenshots first.',
472
491
  inputSchema: z.object({
473
492
  project_id: z.string().describe('Project ID to render'),
@@ -515,10 +534,15 @@ server.registerTool('render-screenshots', {
515
534
  // Tool 4: list-devices
516
535
  server.registerTool('list-devices', {
517
536
  title: 'List Supported Devices',
537
+ annotations: {
538
+ // Reads a local constant; makes no API call at all.
539
+ readOnlyHint: true,
540
+ openWorldHint: false,
541
+ },
518
542
  description: 'Show all supported device specs for App Store and Play Store screenshots. Use the device ID when generating screenshots.',
519
543
  inputSchema: z.object({}),
520
544
  }, async () => {
521
- const lines = DEVICES.map(d => `${d.id} ${d.name} (${d.width}×${d.height}, ${d.category}${d.required ? ', required for store submission' : ''})`);
545
+ const lines = DEVICES.map(d => `${d.id}: ${d.name} (${d.width}×${d.height}, ${d.category}${d.required ? ', required for store submission' : ''})`);
522
546
  return {
523
547
  content: [{
524
548
  type: 'text',
@@ -536,14 +560,18 @@ server.registerTool('list-devices', {
536
560
  // Tool 5: get-project
537
561
  server.registerTool('get-project', {
538
562
  title: 'Get Project Details',
539
- description: `Retrieve the full state of a screenshot project, including all cards and their elements. Use this to inspect what was generated before making edits. Free — no credit cost.
540
-
541
- Returns the canvas state with:
542
- - cards[]: each card has an id, elements array, and optional background settings
543
- - Each element has: type (text, device-mockup, shape, badge, image, star-rating), position (x, y), size (width, height), zIndex, and type-specific properties
544
- - Text elements: fontFamily, fontSize, fontWeight, color, segments (for multi-color text with per-word color, bold, italic, underline, highlightColor)
545
- - Device mockups: perspectiveVariant (flat, left-15, right-15, left-30, right-30, isometric, top-down, landscape-left, landscape-right), screenshotImage (null if no upload), frameStyle (realistic | none), showIsland (false hides the Dynamic Island pill; Apple accepts screenshots either way)
546
- - Shapes: 94 shape types (17 core + 77 decorative across 13 categories) — core shapes (circle, rectangle, rounded-rect, blob, wave, triangle, diamond, hexagon, ring, star, wing-left, wing-right, etc.) plus decorative library shapes (leaf, flower, cloud, sparkle, heart, rocket, trophy, crown, coffee-cup, airplane, dollar-sign, paw-print, and many more)
563
+ annotations: {
564
+ readOnlyHint: true,
565
+ openWorldHint: false,
566
+ },
567
+ description: `Retrieve the full state of a screenshot project, including all cards and their elements. Use this to inspect what was generated before making edits. Free: no credit cost.
568
+
569
+ Returns the canvas state with:
570
+ - cards[]: each card has an id, elements array, and optional background settings
571
+ - Each element has: type (text, device-mockup, shape, badge, image, star-rating), position (x, y), size (width, height), zIndex, and type-specific properties
572
+ - Text elements: fontFamily, fontSize, fontWeight, color, segments (for multi-color text with per-word color, bold, italic, underline, highlightColor)
573
+ - Device mockups: perspectiveVariant (flat, left-15, right-15, left-30, right-30, isometric, top-down, landscape-left, landscape-right), screenshotImage (null if no upload), frameStyle (realistic | none), showIsland (false hides the Dynamic Island pill; Apple accepts screenshots either way)
574
+ - Shapes: 94 shape types (17 core + 77 decorative across 13 categories): core shapes (circle, rectangle, rounded-rect, blob, wave, triangle, diamond, hexagon, ring, star, wing-left, wing-right, etc.) plus decorative library shapes (leaf, flower, cloud, sparkle, heart, rocket, trophy, crown, coffee-cup, airplane, dollar-sign, paw-print, and many more)
547
575
  - projectMeta: globalVisualTheme, brandColors, mood, appCategory`,
548
576
  inputSchema: z.object({
549
577
  project_id: z.string().describe('Project ID to inspect'),
@@ -564,7 +592,7 @@ Returns the canvas state with:
564
592
  typeCounts[t] = (typeCounts[t] || 0) + 1;
565
593
  }
566
594
  const typeStr = Object.entries(typeCounts).map(([t, c]) => `${c} ${t}`).join(', ');
567
- return ` Card ${i} (${card.id}): ${card.elements.length} elements ${typeStr}`;
595
+ return ` Card ${i} (${card.id}): ${card.elements.length} elements: ${typeStr}`;
568
596
  }) || [];
569
597
  return {
570
598
  content: [{
@@ -588,19 +616,25 @@ Returns the canvas state with:
588
616
  // Tool 6: generate-background
589
617
  server.registerTool('generate-background', {
590
618
  title: 'Generate AI Background',
591
- description: `Generate an AI background image for a specific card using Gemini. The background is generated based on a text prompt and applied directly to the card. Costs 6 credits.
592
-
593
- Good prompts describe mood, lighting, and color — not objects or text:
594
- - "Deep purple nebula with soft pink and blue light rays"
595
- - "Warm sunset gradient with golden bokeh particles"
596
- - "Dark moody atmosphere with teal and emerald glow"
597
- - "Clean minimal white-to-light-gray gradient with subtle noise texture"
598
-
619
+ annotations: {
620
+ // Replaces the card's existing background.
621
+ readOnlyHint: false,
622
+ destructiveHint: true,
623
+ openWorldHint: false,
624
+ },
625
+ description: `Generate an AI background image for a specific card using Gemini. The background is generated based on a text prompt and applied directly to the card. Costs 6 credits.
626
+
627
+ Good prompts describe mood, lighting, and color, not objects or text:
628
+ - "Deep purple nebula with soft pink and blue light rays"
629
+ - "Warm sunset gradient with golden bokeh particles"
630
+ - "Dark moody atmosphere with teal and emerald glow"
631
+ - "Clean minimal white-to-light-gray gradient with subtle noise texture"
632
+
599
633
  The generated image is cropped to exact device dimensions and set as the card's background.`,
600
634
  inputSchema: z.object({
601
635
  project_id: z.string().describe('Project ID'),
602
636
  card_index: z.number().describe('Card index (0-based) to apply the background to'),
603
- prompt: z.string().describe('Background description describe mood, lighting, colors, textures. Do NOT include text, devices, or UI elements.'),
637
+ prompt: z.string().describe('Background description: describe mood, lighting, colors, textures. Do NOT include text, devices, or UI elements.'),
604
638
  }),
605
639
  }, async ({ project_id, card_index, prompt }) => {
606
640
  const res = await apiCall('POST', `/api/v1/projects/${project_id}/generate-background`, {
@@ -627,17 +661,23 @@ The generated image is cropped to exact device dimensions and set as the card's
627
661
  // Tool 7: generate-panoramic-background
628
662
  server.registerTool('generate-panoramic-background', {
629
663
  title: 'Generate Panoramic Background',
630
- description: `Generate one wide background image and slice it across multiple cards so they read as a continuous scene when the App Store gallery scrolls. The store gallery gap is accounted for, so slices line up after Apple's gutter.
631
-
632
- Two image sources:
633
- - prompt: AI-generated via Gemini. Costs 6 credits.
634
- - pexels_query: stock landscape photo from Pexels. Free.
635
-
636
- If you omit both, the tool fulfills a panoramic the chat already set up: when generate-screenshots or edit-screenshots returns a design with a panoramic background (e.g. after "pano the background across cards 1-3"), the cards are tagged but the wide image isn't created yet. Calling this tool with just the project_id picks up that pending panoramic and generates it.
637
-
638
- Good prompts describe a continuous scene, not objects or text:
639
- - "Misty mountain range at dawn, soft pink-to-blue gradient sky"
640
- - "Calm ocean horizon at golden hour with gentle waves"
664
+ annotations: {
665
+ // Replaces the background on every card in the span.
666
+ readOnlyHint: false,
667
+ destructiveHint: true,
668
+ openWorldHint: false,
669
+ },
670
+ description: `Generate one wide background image and slice it across multiple cards so they read as a continuous scene when the App Store gallery scrolls. The store gallery gap is accounted for, so slices line up after Apple's gutter.
671
+
672
+ Two image sources:
673
+ - prompt: AI-generated via Gemini. Costs 6 credits.
674
+ - pexels_query: stock landscape photo from Pexels. Free.
675
+
676
+ If you omit both, the tool fulfills a panoramic the chat already set up: when generate-screenshots or edit-screenshots returns a design with a panoramic background (e.g. after "pano the background across cards 1-3"), the cards are tagged but the wide image isn't created yet. Calling this tool with just the project_id picks up that pending panoramic and generates it.
677
+
678
+ Good prompts describe a continuous scene, not objects or text:
679
+ - "Misty mountain range at dawn, soft pink-to-blue gradient sky"
680
+ - "Calm ocean horizon at golden hour with gentle waves"
641
681
  - "Abstract flowing emerald and teal silk waves"`,
642
682
  inputSchema: z.object({
643
683
  project_id: z.string().describe('Project ID'),
@@ -675,16 +715,20 @@ Good prompts describe a continuous scene, not objects or text:
675
715
  // Tool 8: prepare-screenshot-brief
676
716
  server.registerTool('prepare-screenshot-brief', {
677
717
  title: 'Prepare Screenshot Brief',
678
- description: `Get a category playbook and repo-research checklist to prepare for screenshot generation. Call this BEFORE generate-screenshots. Free (no credits).
679
-
680
- Pass app_category to get category-specific guidance pulled live from AppScreenshotStudio: the frame-1 hook playbook (default layout, what to avoid, caption patterns), the recommended story_flow and mood, and the exact facts to dig out of THIS app's repo for its category (e.g. security and compliance signals for finance, real gameplay art for games, the outcome for fitness).
681
-
682
- Returns:
683
- - A category playbook (frame-1 hooks + layout + arc + mood) for the app_category
684
- - A category-specific repo research focus (what to grep this app for)
685
- - A generic codebase research checklist (file patterns per tech stack)
686
- - Headline tips + the codebase_context schema to fill in
687
-
718
+ annotations: {
719
+ readOnlyHint: true,
720
+ openWorldHint: false,
721
+ },
722
+ description: `Get a category playbook and repo-research checklist to prepare for screenshot generation. Call this BEFORE generate-screenshots. Free (no credits).
723
+
724
+ Pass app_category to get category-specific guidance pulled live from AppScreenshotStudio: the frame-1 hook playbook (default layout, what to avoid, caption patterns), the recommended story_flow and mood, and the exact facts to dig out of THIS app's repo for its category (e.g. security and compliance signals for finance, real gameplay art for games, the outcome for fitness).
725
+
726
+ Returns:
727
+ - A category playbook (frame-1 hooks + layout + arc + mood) for the app_category
728
+ - A category-specific repo research focus (what to grep this app for)
729
+ - A generic codebase research checklist (file patterns per tech stack)
730
+ - Headline tips + the codebase_context schema to fill in
731
+
688
732
  The more you gather here, the better generate-screenshots does on the first try.`,
689
733
  inputSchema: z.object({
690
734
  app_category: z.string().optional()
@@ -694,7 +738,7 @@ The more you gather here, the better generate-screenshots does on the first try.
694
738
  }),
695
739
  }, async ({ app_category, platform }) => {
696
740
  const checklist = [
697
- '# Screenshot Brief Research Checklist',
741
+ '# Screenshot Brief: Research Checklist',
698
742
  '',
699
743
  'Search the codebase for each of these before calling generate-screenshots.',
700
744
  'The more you find, the better the screenshots will be.',
@@ -800,7 +844,7 @@ The more you gather here, the better generate-screenshots does on the first try.
800
844
  '- Lead with USER BENEFIT, not feature name: "Never forget a task" > "Task Management"',
801
845
  '- Use power words: Track, Save, Build, Discover, Master, Simplify, Automate',
802
846
  '- Include numbers when possible: "3x faster", "10K+ recipes", "Save 2hrs/week"',
803
- '- First 3 screenshots matter most App Store shows them in search results',
847
+ '- First 3 screenshots matter most: App Store shows them in search results',
804
848
  '- Hero card headline = your one-sentence pitch. Make it count.',
805
849
  '- Keep headlines under 6 words. Subtitle can add detail.',
806
850
  ];
@@ -814,15 +858,15 @@ The more you gather here, the better generate-screenshots does on the first try.
814
858
  ];
815
859
  if (platform === 'ios' || platform === 'both') {
816
860
  deviceTips.push('**iOS (required for App Store):**');
817
- deviceTips.push('- iPhone 16 Pro Max (iphone-6.9): 1260×2736 REQUIRED');
818
- deviceTips.push('- iPad Pro 13" (ipad-13): 2064×2752 REQUIRED');
861
+ deviceTips.push('- iPhone 16 Pro Max (iphone-6.9): 1260×2736: REQUIRED');
862
+ deviceTips.push('- iPad Pro 13" (ipad-13): 2064×2752: REQUIRED');
819
863
  deviceTips.push('');
820
864
  }
821
865
  if (platform === 'android' || platform === 'both') {
822
866
  deviceTips.push('**Android (required for Play Store):**');
823
- deviceTips.push('- Android Phone (android-phone): 1080×2340 REQUIRED');
824
- deviceTips.push('- Android Tablet 7" (android-tablet-10): 1200×1920 REQUIRED');
825
- deviceTips.push('- Android Tablet 10" (android-tablet-large): 1600×2560 second large-screen slot');
867
+ deviceTips.push('- Android Phone (android-phone): 1080×2340: REQUIRED');
868
+ deviceTips.push('- Android Tablet 7" (android-tablet-10): 1200×1920: REQUIRED');
869
+ deviceTips.push('- Android Tablet 10" (android-tablet-large): 1600×2560: second large-screen slot');
826
870
  deviceTips.push('');
827
871
  }
828
872
  // Schema reminder
@@ -865,21 +909,27 @@ The more you gather here, the better generate-screenshots does on the first try.
865
909
  // Tool 9: upload-screenshots
866
910
  server.registerTool('upload-screenshots', {
867
911
  title: 'Upload App Screenshots',
868
- description: `Upload local app screenshots (from Simulator, emulator, or screen captures) into the device mockups of an existing project. This lets you add real app UI into the device frames without leaving the terminal.
869
-
870
- Reads files from your local filesystem, converts them to base64, and sets them on the device mockup elements in the specified cards.
871
-
872
- Free — no credit cost. The screenshots are placed into the device frames that were created by generate-screenshots.
873
-
874
- Workflow:
875
- 1. generate-screenshots → creates project with empty device frames
876
- 2. upload-screenshots fills the device frames with your actual app UI
877
- 3. render-screenshots → exports final PNGs at App Store dimensions
878
-
879
- Tips:
880
- - Take screenshots from the iOS Simulator (Cmd+S) or Android emulator
881
- - Use PNG format for best quality
882
- - Screenshots are automatically fitted into the device frame
912
+ annotations: {
913
+ // Overwrites whatever image the device frames already held.
914
+ readOnlyHint: false,
915
+ destructiveHint: true,
916
+ openWorldHint: false,
917
+ },
918
+ description: `Upload local app screenshots (from Simulator, emulator, or screen captures) into the device mockups of an existing project. This lets you add real app UI into the device frames without leaving the terminal.
919
+
920
+ Reads files from your local filesystem, converts them to base64, and sets them on the device mockup elements in the specified cards.
921
+
922
+ Free: no credit cost. The screenshots are placed into the device frames that were created by generate-screenshots.
923
+
924
+ Workflow:
925
+ 1. generate-screenshots creates project with empty device frames
926
+ 2. upload-screenshots fills the device frames with your actual app UI
927
+ 3. render-screenshots → exports final PNGs at App Store dimensions
928
+
929
+ Tips:
930
+ - Take screenshots from the iOS Simulator (Cmd+S) or Android emulator
931
+ - Use PNG format for best quality
932
+ - Screenshots are automatically fitted into the device frame
883
933
  - You can upload different screenshots to different cards`,
884
934
  inputSchema: z.object({
885
935
  project_id: z.string().describe('Project ID from a previous generate-screenshots call'),
@@ -963,7 +1013,7 @@ function autoInstallSkill() {
963
1013
  copyFileSync(src, dest);
964
1014
  }
965
1015
  catch {
966
- // Silent fail skill install is optional
1016
+ // Silent fail: skill install is optional
967
1017
  }
968
1018
  }
969
1019
  async function main() {