@appscreenshotstudio/mcp 0.5.2 → 0.5.4

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
@@ -92,7 +92,7 @@ const STORY_FLOW_DESCRIPTIONS = {
92
92
  'hero-intro': 'Hero branding screen first (no device), then features with devices',
93
93
  'social-proof-bookend': 'Hero intro, then features, then social proof ending',
94
94
  };
95
- function buildDesignMessage(input) {
95
+ function buildDesignMessage(input, hasScreenshotImages = false) {
96
96
  const parts = [];
97
97
  parts.push(`Create ${input.count} App Store screenshots for my app.`);
98
98
  parts.push(`\nApp name: ${input.app_name}`);
@@ -147,19 +147,57 @@ function buildDesignMessage(input) {
147
147
  parts.push('--- End App Research Context ---');
148
148
  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.');
149
149
  }
150
- parts.push(`\nAll device mockups should have screenshotImage: null — the developer will upload actual app screenshots later.`);
150
+ if (hasScreenshotImages) {
151
+ parts.push(`\nMy app screenshots are attached — they are placed into the device frames automatically.`);
152
+ }
153
+ else {
154
+ parts.push(`\nAll device mockups should have screenshotImage: null — the developer will upload actual app screenshots later.`);
155
+ }
151
156
  parts.push(`Please include projectMeta with brand colors, mood, appCategory, and a rich globalVisualTheme description.`);
152
157
  return parts.join('\n');
153
158
  }
159
+ /** Read local image files into the chat API's images payload. Same file
160
+ * handling as upload-screenshots; kind rides through to server-side
161
+ * placement (screenshots fill the generated device frames, mascots get
162
+ * placed decoratively around the phones). */
163
+ function readImagesForChat(images) {
164
+ const payload = [];
165
+ const errors = [];
166
+ for (const { file_path, kind } of images ?? []) {
167
+ try {
168
+ if (!existsSync(file_path)) {
169
+ errors.push(`File not found: ${file_path}`);
170
+ continue;
171
+ }
172
+ const buffer = readFileSync(file_path);
173
+ const ext = file_path.toLowerCase().split('.').pop();
174
+ const mimeType = ext === 'jpg' || ext === 'jpeg' ? 'image/jpeg'
175
+ : ext === 'webp' ? 'image/webp'
176
+ : 'image/png';
177
+ payload.push({ dataUrl: `data:${mimeType};base64,${buffer.toString('base64')}`, mediaType: mimeType, kind });
178
+ }
179
+ catch (err) {
180
+ errors.push(`Failed to read ${file_path}: ${err instanceof Error ? err.message : String(err)}`);
181
+ }
182
+ }
183
+ return { payload, errors };
184
+ }
185
+ /** Shared images param for generate-screenshots and edit-screenshots. */
186
+ const chatImagesSchema = z.array(z.object({
187
+ file_path: z.string().describe('Absolute path to an image file on the local filesystem (PNG, JPG, or WEBP)'),
188
+ kind: z.enum(['screenshot', 'mascot']).default('screenshot')
189
+ .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
+ })).max(5).optional()
191
+ .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.');
154
192
  // ─── MCP Server ─────────────────────────────────────────────────────────────────
155
193
  const server = new McpServer({
156
194
  name: 'appscreenshotstudio',
157
- version: '0.5.2',
195
+ version: '0.5.4',
158
196
  });
159
197
  // Tool 1: generate-screenshots
160
198
  server.registerTool('generate-screenshots', {
161
199
  title: 'Generate App Store Screenshots',
162
- description: `Create a complete set of App Store screenshot designs for an app. Returns a project URL where the developer can upload actual app screenshots into the device frames and export final PNGs.
200
+ 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.
163
201
 
164
202
  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.
165
203
 
@@ -234,6 +272,7 @@ Costs 5 credits per generation.`,
234
272
  .describe('The main user journey — e.g. "Sign up → Create project → Invite team → Track progress"'),
235
273
  }).optional()
236
274
  .describe('Context gathered from researching the app codebase. Dramatically improves screenshot quality — the more detail here, the better the output.'),
275
+ images: chatImagesSchema,
237
276
  }),
238
277
  }, async (input) => {
239
278
  // Resolve/validate the device up front so an unknown id fails fast here
@@ -259,11 +298,15 @@ Costs 5 credits per generation.`,
259
298
  }
260
299
  const project = createRes.data.data;
261
300
  const projectId = project.id;
262
- // Step 2: Chat to generate all cards
263
- const message = buildDesignMessage(input);
301
+ // Step 2: Chat to generate all cards (with attached images when provided:
302
+ // screenshots auto-fill the device frames, mascots decorate around them)
303
+ const { payload: chatImages, errors: imageErrors } = readImagesForChat(input.images);
304
+ const hasScreens = chatImages.some((img) => img.kind === 'screenshot');
305
+ const message = buildDesignMessage(input, hasScreens);
264
306
  const chatRes = await apiCall('POST', `/api/v1/projects/${projectId}/chat`, {
265
307
  message,
266
308
  selected_card_indices: [],
309
+ ...(chatImages.length > 0 ? { images: chatImages } : {}),
267
310
  });
268
311
  if (!chatRes.ok) {
269
312
  return {
@@ -286,10 +329,13 @@ Costs 5 credits per generation.`,
286
329
  `Credits remaining: ${creditsRemaining}`,
287
330
  '',
288
331
  'Next steps:',
289
- '1. Use upload-screenshots to add your app screenshots into the device frames',
332
+ hasScreens
333
+ ? '1. Your attached screenshots were placed into the device frames (upload-screenshots can swap any card later)'
334
+ : '1. Use upload-screenshots to add your app screenshots into the device frames',
290
335
  '2. Use render-screenshots to export final PNGs',
291
336
  '3. Or open the project URL in a browser to preview and adjust',
292
337
  '',
338
+ imageErrors.length ? `Image read warnings:\n${imageErrors.join('\n')}` : '',
293
339
  chatData.suggestions?.length
294
340
  ? `Suggestions: ${chatData.suggestions.join(', ')}`
295
341
  : '',
@@ -300,7 +346,7 @@ Costs 5 credits per generation.`,
300
346
  // Tool 2: edit-screenshots
301
347
  server.registerTool('edit-screenshots', {
302
348
  title: 'Edit Screenshot Designs',
303
- description: `Make changes to an existing screenshot project. Use natural language to describe what you want to change. Costs 5 credits per edit.
349
+ 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.
304
350
 
305
351
  What you can change:
306
352
  - Text: headlines, subtitles, badge text, font size, font family (Inter, Poppins, Montserrat, DM Sans, Space Grotesk, etc.)
@@ -357,8 +403,9 @@ Example edit messages:
357
403
  primary_user_flow: z.string().optional(),
358
404
  }).optional()
359
405
  .describe('App context from codebase research. Helps the AI make edits that match the actual app.'),
406
+ images: chatImagesSchema,
360
407
  }),
361
- }, async ({ project_id, message, card_indices, codebase_context }) => {
408
+ }, async ({ project_id, message, card_indices, codebase_context, images }) => {
362
409
  let enrichedMessage = message;
363
410
  if (codebase_context) {
364
411
  const ctxParts = [];
@@ -378,13 +425,15 @@ Example edit messages:
378
425
  enrichedMessage = `[App context: ${ctxParts.join('. ')}]\n\n${message}`;
379
426
  }
380
427
  }
428
+ const { payload: chatImages, errors: imageErrors } = readImagesForChat(images);
381
429
  const res = await apiCall('POST', `/api/v1/projects/${project_id}/chat`, {
382
430
  message: enrichedMessage,
383
431
  selected_card_indices: card_indices || [],
432
+ ...(chatImages.length > 0 ? { images: chatImages } : {}),
384
433
  });
385
434
  if (!res.ok) {
386
435
  return {
387
- content: [{ type: 'text', text: `Edit failed: ${JSON.stringify(res.data)}` }],
436
+ content: [{ type: 'text', text: `Edit failed: ${JSON.stringify(res.data)}${imageErrors.length ? `\n\nImage read warnings:\n${imageErrors.join('\n')}` : ''}` }],
388
437
  };
389
438
  }
390
439
  const data = res.data.data;
@@ -409,7 +458,7 @@ Example edit messages:
409
458
  // Tool 3: render-screenshots
410
459
  server.registerTool('render-screenshots', {
411
460
  title: 'Render Screenshots to PNG',
412
- description: 'Export a screenshot project to high-resolution PNG files at exact App Store dimensions. Returns download URLs for each card. 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.',
461
+ 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.',
413
462
  inputSchema: z.object({
414
463
  project_id: z.string().describe('Project ID to render'),
415
464
  }),
@@ -431,6 +480,9 @@ server.registerTool('render-screenshots', {
431
480
  '',
432
481
  ...images.map((img, i) => `Card ${i + 1}: ${img.url} (${img.width}×${img.height})`),
433
482
  '',
483
+ 'Download URLs are valid for 7 days. Save the PNGs to disk now if you',
484
+ 'need them long-term (re-rendering later is free).',
485
+ '',
434
486
  'Previews are shown below. To compare all cards side by side at full size,',
435
487
  'refine by hand, or export, open the project URL in the builder.',
436
488
  ].join('\n'),
@@ -480,7 +532,7 @@ Returns the canvas state with:
480
532
  - cards[]: each card has an id, elements array, and optional background settings
481
533
  - Each element has: type (text, device-mockup, shape, badge, image, star-rating), position (x, y), size (width, height), zIndex, and type-specific properties
482
534
  - Text elements: fontFamily, fontSize, fontWeight, color, segments (for multi-color text with per-word color, bold, italic, underline, highlightColor)
483
- - Device mockups: perspectiveVariant (flat, left-15, right-15, left-30, right-30, isometric, top-down, landscape-left, landscape-right), screenshotImage (null if no upload)
535
+ - 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)
484
536
  - 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)
485
537
  - projectMeta: globalVisualTheme, brandColors, mood, appCategory`,
486
538
  inputSchema: z.object({
package/package.json CHANGED
@@ -1,45 +1,48 @@
1
- {
2
- "name": "@appscreenshotstudio/mcp",
3
- "version": "0.5.2",
4
- "description": "MCP server for generating App Store screenshots via AppScreenshotStudio",
5
- "type": "module",
6
- "license": "MIT",
7
- "homepage": "https://appscreenshotstudio.com/docs/mcp",
8
- "repository": {
9
- "type": "git",
10
- "url": "https://github.com/TWWorks-org/mcp-server"
11
- },
12
- "keywords": [
13
- "mcp",
14
- "model-context-protocol",
15
- "app-store-screenshots",
16
- "screenshot-generator",
17
- "appscreenshotstudio",
18
- "ai-tools"
19
- ],
20
- "bin": {
21
- "appscreenshotstudio-mcp": "dist/index.js"
22
- },
23
- "scripts": {
24
- "build": "tsc",
25
- "start": "node dist/index.js",
26
- "dev": "tsc --watch",
27
- "prepublishOnly": "npm run build"
28
- },
29
- "dependencies": {
30
- "@modelcontextprotocol/sdk": "^1.27.1",
31
- "zod": "^3.25.67"
32
- },
33
- "devDependencies": {
34
- "@types/node": "^22.0.0",
35
- "typescript": "^5.8.0"
36
- },
37
- "engines": {
38
- "node": ">=18"
39
- },
40
- "files": [
41
- "dist",
42
- "skills",
43
- "README.md"
44
- ]
45
- }
1
+ {
2
+ "name": "@appscreenshotstudio/mcp",
3
+ "version": "0.5.4",
4
+ "description": "MCP server for generating App Store screenshots via AppScreenshotStudio",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "homepage": "https://appscreenshotstudio.com/docs/mcp",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/TWWorks-org/mcp-server"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/TWWorks-org/mcp-server/issues"
14
+ },
15
+ "keywords": [
16
+ "mcp",
17
+ "model-context-protocol",
18
+ "app-store-screenshots",
19
+ "screenshot-generator",
20
+ "appscreenshotstudio",
21
+ "ai-tools"
22
+ ],
23
+ "bin": {
24
+ "appscreenshotstudio-mcp": "dist/index.js"
25
+ },
26
+ "scripts": {
27
+ "build": "tsc",
28
+ "start": "node dist/index.js",
29
+ "dev": "tsc --watch",
30
+ "prepublishOnly": "npm run build"
31
+ },
32
+ "dependencies": {
33
+ "@modelcontextprotocol/sdk": "^1.27.1",
34
+ "zod": "^3.25.67"
35
+ },
36
+ "devDependencies": {
37
+ "@types/node": "^22.0.0",
38
+ "typescript": "^5.8.0"
39
+ },
40
+ "engines": {
41
+ "node": ">=18"
42
+ },
43
+ "files": [
44
+ "dist",
45
+ "skills",
46
+ "README.md"
47
+ ]
48
+ }
@@ -68,12 +68,13 @@ Call the `generate-screenshots` MCP tool with:
68
68
  - `count`: number of cards (3-10)
69
69
  - `story_flow`: `"auto"` (default), `"hero-intro"`, `"problem-solution"`, `"benefit-first"`, etc.
70
70
  - `codebase_context`: the full context object from Step 1
71
+ - `images`: local file paths of real app screenshots (placed inside the device frames automatically, in order) and/or the app's mascot with `kind: "mascot"` (placed peeking from behind the hook card's phone and on the closing card; transparent PNG). If screenshots exist in the repo (fastlane/screenshots, store assets, README images), attach them here so the set comes back with real UI in the phones instead of empty frames.
71
72
 
72
73
  ### Step 4: Upload App Screenshots
73
74
 
74
- If the user has actual app screenshots (from Simulator, emulator, or screen captures), upload them into the device frames using `upload-screenshots`:
75
+ If screenshots were attached in Step 3 they are already in the frames this step covers swaps and late additions. `upload-screenshots`:
75
76
  - Takes local file paths and maps them to card indices
76
- - Fills the empty device mockups with real app UI
77
+ - Fills or replaces the device mockups' app UI per card
77
78
  - Free — no credit cost
78
79
 
79
80
  ### Step 5: Show and Iterate (the core loop)
@@ -91,7 +92,7 @@ Hand off to the builder only when it genuinely helps: share the project URL and
91
92
 
92
93
  ### Step 6: Export
93
94
 
94
- `render-screenshots` is also the export: it returns download URLs for the final PNGs at exact App Store dimensions, and it is free. Give the user those URLs. The builder project URL is the alternative when they want to compare the full set visually or hand-tweak before downloading.
95
+ `render-screenshots` is also the export: it returns download URLs for the final PNGs at exact App Store dimensions, and it is free. Give the user those URLs and note they stay valid for 7 days, so download promptly (re-rendering later is free if a URL has expired). The builder project URL is the alternative when they want to compare the full set visually or hand-tweak before downloading.
95
96
 
96
97
  ## Headline Rules
97
98