@appscreenshotstudio/mcp 0.5.3 → 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 +59 -10
- package/package.json +48 -48
- package/skills/appscreenshotstudio/SKILL.md +3 -2
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
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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;
|
package/package.json
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
|
|
2
|
-
"name": "@appscreenshotstudio/mcp",
|
|
3
|
-
"version": "0.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
|
-
}
|
|
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
|
|
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
|
|
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)
|