@appscreenshotstudio/mcp 0.5.3 → 0.5.5

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.5',
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,26 +425,34 @@ 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;
391
440
  const creditsRemaining = res.data.credits_remaining;
441
+ // A device switch ("make this for iPad") clones the project. All further
442
+ // edits, uploads, and renders must target the NEW project id, not the old one.
443
+ const activeProjectId = data.duplicated_project_id || project_id;
392
444
  return {
393
445
  content: [{
394
446
  type: 'text',
395
447
  text: [
396
448
  data.message,
397
449
  '',
450
+ data.duplicated_project_id
451
+ ? `NEW PROJECT CREATED at the target device size: ${data.duplicated_project_id}\nUse this project_id for all further edits, screenshot uploads, and rendering. The original project (${project_id}) is unchanged.`
452
+ : '',
398
453
  `Cards: ${data.canvas_state?.cards?.length || 0}`,
399
454
  `Credits remaining: ${creditsRemaining}`,
400
- `Project URL: ${API_BASE}/builder/${project_id}`,
455
+ `Project URL: ${API_BASE}/builder/${activeProjectId}`,
401
456
  '',
402
457
  data.suggestions?.length
403
458
  ? `Suggestions: ${data.suggestions.join(', ')}`
package/package.json CHANGED
@@ -1,48 +1,48 @@
1
- {
2
- "name": "@appscreenshotstudio/mcp",
3
- "version": "0.5.3",
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
- }
1
+ {
2
+ "name": "@appscreenshotstudio/mcp",
3
+ "version": "0.5.5",
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)