Skip to main content
Glama

shotforge

CI License: MIT Python

App Store screenshots from your real app screens — decided and written by your own Claude. Give shotforge your screenshots; Claude looks at them and proposes the design (main colour, text/accent colour, number of pages, 2D or 3D template) and writes the headline copy in every language you want. You can fix any of those yourself, and whatever you fix is kept exactly. The output is one separate, upload-ready image per page per language (1320×2868 for iPhone 6.9″) — never one combined image.

Türkçe okumak için: README.tr.md

2D and 3D templates

Features

  • Claude does the thinking, locally rendered. Colours, page count, template and copy are decided by Claude from the screenshots; rendering is pure Python (Pillow + numpy). No design service, no account, no telemetry.

  • Three ways to bring your own Claude: the claude CLI (Claude Code login), an Anthropic API key, or MCP (Claude Desktop, Claude Code, Cursor, …) where your chat Claude is the designer.

  • You stay in control. Pass any of: main background colour, text colour, accent colour, page count, your own headlines (full or partial), 2D/3D, or a template. Everything you give is kept verbatim; Claude (or the built-in rules) fills the rest.

  • 7 templates, 2D and 3D. 2D = phone flat or rotated only in the plane; 3D = phone tilted in perspective with visible thickness. Automatic selection follows documented rules (shotforge rules) and never picks a template whose theme clashes with your colour.

  • Asks before it starts. The slash command, MCP tools and terminal share one question list; anything left blank gets a default, with colours taken from your screenshots.

  • App Store and Google Play from one run: phone and tablet sizes, App Store Header and Search Results images, and the Play feature graphic.

  • Multi-language in one run (--langs en,tr,de): one folder per language plus a zip, optional per-language screenshots, App Preview video converter.

  • Brand file: shotforge.toml keeps colours, font, logo and tone, so every run and every app looks the same.

  • Maestro capture: shotforge capture runs your flows per language on a booted device and writes the screenshots shotforge reads.

  • Store tests: --variants 2 writes complete sets that differ by copy, design or both, ready for Custom Product Pages and Play experiments.

  • fastlane folders: --layout fastlane writes the trees deliver and supply upload. shotforge itself uploads nothing.

  • ASO copy: --keywords tells the AI which search terms to work into the headlines.

  • Readable by construction: unreadable text colours are corrected per slide (contrast ≥ 3:1 for headlines) and reported.

Related MCP server: MagicScreenshots

Install

Requires Python 3.10+.

# CLI
pipx install "git+https://github.com/ibrahimatmaca/shotforge"
# CLI + MCP server
pipx install "shotforge[mcp] @ git+https://github.com/ibrahimatmaca/shotforge"
# or run without installing (needs uv)
uvx --from "shotforge[mcp] @ git+https://github.com/ibrahimatmaca/shotforge" shotforge-mcp

shotforge doctor     # checks Python deps, ffmpeg (optional), claude CLI / API keys

Quick start

shotforge

Run it on its own in a terminal. It asks where your screenshots are, then everything else, including who writes the headlines (AI, you, or a file). Press Enter to accept a default.

In Claude Code, install the slash command once with shotforge install-claude --global, then type /store-shots. The questions appear in the conversation.

AI-written headlines need claude auth login or ANTHROPIC_API_KEY. Without either, shotforge tells you at the start and you write the headlines yourself.

With flags

# 1) Let Claude decide everything (uses your logged-in `claude` CLI, or ANTHROPIC_API_KEY)
shotforge create ./screens --app "Habita" --about "Habit tracker with weekly stats" --langs en,tr --no-ask -o out

#    Not using Claude? Add: --ai openai  (needs OPENAI_API_KEY and `pip install openai`)

# 2) Fix the choices you care about; Claude fills the rest
shotforge create ./screens --app "Habita" --dimension 3d --bg-color "#0B1026" --accent "#F472B6" --count 6 -o out

# 3) Write your own copy (partial is fine) — see "Your own text" below
shotforge create ./screens --app "Habita" --texts my-texts.txt -o out

# 4) Answer questions in the terminal (Enter = let Claude decide)
shotforge create ./screens --app "Habita" --ask

Result, as separate files per language:

out/
  en/
    app-store/
      iphone-6.9/01_en.png …
      header-search/5244x2950.png, 3840x2560.png, 1920x1280.png
      previews/iphone-6.9.mp4          # only if you pass --preview
    google-play/
      phone/01_en.png …
      feature-graphic/1024x500.png
  tr/ …
  manifest.json                        # every file, its size, the decisions and warnings
  habita.zip                           # en/, tr/, manifest.json
  shotforge.copy.json, _review/        # not in the zip

Without -o, output goes to ./shotforge-out/<app>/. Add --sheet for a single side-by-side _review/overview_<lang>.jpg (a review aid, not a deliverable) and --brief for a copy-paste prompt to recreate the set in Claude Design.

What it asks, and the defaults

Question

Flag

If left blank

Screenshots

shotforge create <folder or files>

required, asked again until given

Store

--store appstore|playstore|both

both

Header and Search Results images (App Store)

--header-sizes 5244x2950,3840x2560,1920x1280|no

all three

Languages

--langs en,tr,de

en, tr

2D, 3D or a template

--dimension, --template

chosen by the rules

Device sizes

--devices iphone-6.9,ipad-13,android-phone,android-tablet-7,android-tablet-10

iphone-6.9, android-phone

Pages

--count

6–7

Headlines and tone

--texts, --tone

written by Claude

Background, text, accent colour

--bg-color, --text-color, --accent

from the screenshots; template default if they are neutral

App icon for the wide images

--icon

none

App Preview recording, shared or per language

--preview rec.mov, --preview tr=rec_tr.mov

skipped

Output

-o, --no-zip, --zip-only

./shotforge-out/<app>/ and a zip

--no-ask skips the questions and uses these defaults. Passing --device draws that one device with the old out/<lang>/ layout, no store folders or zip.

Separate screenshots per language or platform: put them in <folder>/tr/, <folder>/android/, <folder>/android/tr/, <folder>/ipad/ or <folder>/android-tablet/, with the same file names. shotforge warns when it has to fall back, for example to an English UI on a Turkish page.

Store notes:

  • Google Play takes up to 8 images per device type and at least 4 for tablets; extra pages are dropped with a warning.

  • Google's guidance recommends against device imagery, taglines over 20% of the image, and words like "Best" or "Free". It is a recommendation, not a requirement for phones and tablets, but it can affect where the listing is featured, so shotforge warns on every Play run.

  • Per third-party guides, 5244x2950 works for both the App Store header and search results (PNG only), while 3:2 sizes work for search results only. Check the crop in App Store Connect's preview; shotforge keeps text near the centre.

  • App Preview needs ffmpeg and is App Store only; Google Play takes a YouTube link.

Who decides what

Decision

You fix it with

If you don't

Main background colour

--bg-color #HEX

Claude picks one from the app's look (local colour analysis as a hint)

Text colour

--text-color #HEX

automatic, readable on every slide

Accent colour

--accent #HEX

Claude / derived from the background

Number of pages

--count N (1–10) or the number of slides in --texts

Claude picks 6 or 7

Copy

--texts file (full or partial)

Claude writes it, looking at each screenshot

2D / 3D

--dimension 2d|3d

rules / Claude decide

Template

--template name

rules / Claude decide

If you fix everything, Claude is not called at all. If you fix a background colour, Claude's template choice is overridden when its theme clashes with that colour (the reason is printed). Results are written to shotforge.copy.json; edit it and run shotforge render to redraw without calling Claude again.

Your own text

# slide 1
shot: 01_home.png                         (optional)
en: Build habits that *stick* | Your whole day on one calm screen
tr: Kalıcı *alışkanlıklar* edin | Tüm günün tek sakin ekranda

# slide 2
tr: Gününü *planla*                       (missing 'en' -> Claude writes it)

*word* = accent colour · | … = subtitle · \n = line break. JSON works too. Example: examples/texts.example.txt.

Templates: 2D vs 3D

Template

Theme

Suits

2D

bubble

colorful

education, lifestyle, productivity, health

2D

heartbeat

dark

fitness, health, finance, utilities

2D

halo

dark

health, fitness, productivity, utilities

2D

slant

dark

social, entertainment, productivity

3D

classic

colorful

entertainment, lifestyle, travel, shopping, social, photo

3D

prism3d

light

lifestyle, shopping, travel, photo, education, social

3D

neon3d

dark

games, entertainment, photo, fitness, social

Automatic choice (--template auto, see shotforge rules; try shotforge analyze ./screens --category health): 3D score = +2 for games/photo/travel/shopping/entertainment/lifestyle, −2 for finance/productivity/utilities/health (−1 fitness/education), +1 for ≤ 3 distinct screenshots, −2 for dense small-text UIs, +1 for a premium/playful/creative tone → 3D when ≥ 1, else 2D. Inside the family: +3 theme match (dark colour → dark template, very light → light, vivid mid-tone → colorful), +3 category match. --template disables the auto choice; --dimension restricts it.

Per-slide layouts, colours and decorations come from shotforge/templates/*.json (palette, bg_mode, layouts, defaults with $bg/$accent/$text placeholders). Copy one, edit it, and pass --template my.json.

Use it from your own Claude via MCP

shotforge-mcp is a stdio MCP server. In MCP mode your chat Claude is the designer: it looks at the screenshots, asks you what you want to fix, writes the copy, and calls the tools; shotforge renders and returns an overview image so Claude can check its own work. No API key is needed.

Run shotforge mcp to see the setup steps, the tools and whether the server can start.

# Claude Code
claude mcp add shotforge -- shotforge-mcp
#   (without installing:  claude mcp add shotforge -- uvx --from "shotforge[mcp] @ git+https://github.com/ibrahimatmaca/shotforge" shotforge-mcp)

Claude Desktop / Cursor / any MCP client (claude_desktop_config.json or the client's MCP settings):

{
  "mcpServers": {
    "shotforge": {
      "command": "uvx",
      "args": ["--from", "shotforge[mcp] @ git+https://github.com/ibrahimatmaca/shotforge", "shotforge-mcp"]
    }
  }
}

(If you installed with pipx, use "command": "shotforge-mcp" and no args.)

Tool

Purpose

analyze_screenshots

local colour/theme/density analysis + rule-based 2D/3D recommendation + rules and template catalogue

list_templates

templates with dimension, theme and suited categories

create_screenshots

takes Claude's/your decisions (slides, colours, template/dimension, langs, count, only, layout, …) and writes separate images; returns paths + overview image

render_screenshots

redraw from shotforge.copy.json after edits, optionally with another template/colours

convert_app_preview

screen recording → App Store app-preview spec (ffmpeg)

A prompt named store_screenshots walks Claude through the whole flow. Then just say: "Make App Store screenshots from ~/Desktop/habita-screens for my app Habita, English and Turkish, 3D." Output goes to ~/shotforge-out by default.

Claude Code slash command

shotforge install-claude --global      # writes ~/.claude/commands/store-shots.md

Type /store-shots in Claude Code, in the terminal or the desktop app. It asks for your screenshots, app name and the questions above in the conversation, runs shotforge create, opens the overview images and fixes problems it finds. You can also give them up front: /store-shots ./screens "Habita" "habit tracker".

Brand file

Put shotforge.toml in your project (see examples/shotforge.toml). create, plan and render find it in the current folder or a parent, up to the repository root; --brand FILE picks another one and --no-brand ignores it. Flags win over the file, and the file wins over the AI.

[brand]
tone = "calm, direct, benefit-first"
template = "bubble"
langs = ["en", "tr"]
font = "assets/Brand-Bold.otf"     # relative to this file, must stay inside its folder
logo = "assets/logo.png"           # small, in the free band beside the headline; header icon unless --icon is given

[brand.colors]
background = "#6D28D9"
text = "#FFFFFF"
accent = "#FDE047"

render keeps the design in the copy file and takes only font and logo from the brand file. To restyle hand-edited colours, use the render flags.

Keywords

shotforge create ./screens --app Habita --keywords "habit tracker,daily routine"

The AI works the terms in where they read naturally and within the length limits, and shotforge lists any not found word for word (a localized term can be the reason). Offline mode writes placeholder copy and can't use them. Apple doesn't document screenshot text as a search ranking factor, so keep filling the App Store keyword field; treat this as matching your headlines to what people searched for. Google Play's guidelines advise against words like "Best", "#1" and "Free" in images, and shotforge warns about them.

Store test variants

shotforge create ./screens --app Habita --variants 3 --variant-by both -o out
# out/variant-a/, out/variant-b/, out/variant-c/  — each with its own copy file, manifest, zip and images

--variant-by copy keeps the design of variant A and gives each variant another messaging angle. design keeps A's text and colours, uses another template, and tries the other of 2D/3D for B unless you set --dimension; no further AI call is made. both (default) changes both. --template sets variant A only. Different copy needs an AI backend: offline runs warn that the headlines are the same. Design questions are not asked with --variants, so pass choices as flags. Upload each folder as its own Custom Product Page or Play experiment.

Maestro capture

shotforge doesn't know your app's screens. Your Maestro flow decides which pages are captured: every takeScreenshot is one page, and the flow taps its way there. shotforge runs that flow per language and collects the PNGs. It doesn't boot a device, install the app or change the device language.

1. Install Maestro and your app. Install Maestro, then install a build of your app on a simulator or emulator. shotforge doctor shows whether Maestro and a booted device are found.

2. Choose the pages. Pick the 6–8 screens that sell the app, in the order you want to show them. Google Play takes at most 8 images per device type, and the App Store at most 10.

3. Find what to tap. Use Maestro Studio (right-click an element on the device) or maestro hierarchy in a terminal. Prefer id over visible text, because text changes with the language. An id may need adding to your app, such as accessibilityIdentifier on iOS.

4. Write one flow in its own folder, so your normal test runs don't pick it up:

# .maestro/shots/store.yaml
appId: com.example.habita
---
- launchApp:
    clearState: true
    permissions:
      all: allow
- runFlow: login.yaml            # if the screens need an account; use a test account
- extendedWaitUntil:
    visible:
      id: "home_list"
    timeout: 15000
- takeScreenshot: 01_home

- tapOn:
    id: "tab_stats"
- waitForAnimationToEnd
- takeScreenshot: 02_stats

- tapOn:
    id: "tab_goals"
- waitForAnimationToEnd
- takeScreenshot: 03_goals
  • Number the names: the number sets the page order. Use the same names in every language, because shotforge matches screens by file name.

  • Keep the pages in one file. Maestro runs the flows in a folder in no fixed order and ignores subfolders.

  • clearState signs the app out, so the flow has to log in again, or remove clearState.

  • Use test data with realistic content. The screens are published as they are.

  • For iPhone and iPad, or for iOS and Android, write separate flows if the navigation differs, and capture with --platform ios, ipad, android or android-tablet.

5. Capture in each language. Maestro sets the language on the device, not in the flow, so use one of these:

A. One device per language (works without changing your app). Start each device, install the app on it if it's new, and capture that language. Keep one device running at a time, or pass --device <id>:

maestro start-device --platform ios --device-locale en_US
shotforge capture --flows .maestro/shots/store.yaml --langs en --platform ios -o shots

maestro start-device --platform ios --device-locale tr_TR
shotforge capture --flows .maestro/shots/store.yaml --langs tr --platform ios -o shots

B. Your app reads the language at launch (one device, one command). shotforge passes each language to the flow as ${LANG}. Send it as a launch argument, and have a debug build of your app read it (UserDefaults on iOS, intent extras on Android) and switch language:

- launchApp:
    arguments:
      appLanguage: ${LANG}
shotforge capture --flows .maestro/shots/store.yaml --langs en,tr --platform ios -o shots

6. Check the screenshots. Open shots/ios/tr/ and confirm the UI really is in Turkish: a language that didn't switch still produces images, with no error. shotforge warns about missing names (within one run), about shapes that would be cropped, and about older PNGs in the folder, which it leaves in place and which would become slides.

7. Create the images:

shotforge create shots --app Habita --langs en,tr

With option B you can do steps 5 and 7 in one command: shotforge create shots --maestro .maestro/shots/store.yaml --capture-platform ios --app Habita --langs en,tr.

fastlane

shotforge create ./screens --app Habita --layout fastlane -o store
fastlane deliver --screenshots_path store/fastlane/screenshots --skip_binary_upload --skip_metadata --overwrite_screenshots
fastlane supply --metadata_path store/fastlane/metadata/android --skip_upload_apk --skip_upload_aab --skip_upload_metadata --skip_upload_changelogs
store/fastlane/screenshots/en-US/APP_IPHONE_67_iphone-6.9_01_en.png        # deliver finds the device from the image size
store/fastlane/metadata/android/en-US/images/phoneScreenshots/01_en.png    # also sevenInchScreenshots, tenInchScreenshots
store/fastlane/metadata/android/en-US/images/featureGraphic.png
store/app-store-extra/en/header-search/5244x2950.png                         # deliver doesn't upload these
store/en/app-store/previews/iphone-6.9.mp4                                   # App Previews, if any: upload these yourself

Language codes become store locales (en → en-US, tr → tr on Apple and tr-TR on Play). Check a new device size against your fastlane version; both tools change with the stores.

App Store Connect

shotforge create ./screens --app Habita --store appstore --header-sizes no --layout asc -o media
# media/en-US/APP_IPHONE_67/01_en.png, media/tr/APP_IPHONE_67/…  — works with folder-based uploaders such as
#   asc apps media upload com.example.MyApp --folder media/ --replace      (keremerkan/asc-cli)
shotforge preview recording.mov -o preview.mp4      # 886x1920, 30 fps, H.264/AAC, ≤ 30 s (needs ffmpeg)

--layout asc folder names: iphone-6.9 → APP_IPHONE_67, iphone-6.5 → APP_IPHONE_65, ipad-13 → APP_IPAD_PRO_3GEN_129; en → en-US. Check them against your uploader's docs — Apple changes these identifiers over time. shotforge sizes prints the sizes. App previews are plain screen recordings (no frame added).

Backends, privacy and cost

--ai

What runs

auto

claude CLI if found, else ANTHROPIC_API_KEY (needs pip install anthropic), else offline

claude

claude -p --output-format json --tools Read --allowedTools Read --strict-mcp-config --add-dir <screens> (prompt on stdin) — your Claude Code login

api

Anthropic API with your key (--model to choose)

openai

OpenAI Responses API with OPENAI_API_KEY (needs pip install openai). Default model gpt-6.1-sol, --model to choose. Only runs when you ask for it: auto never picks it

offline

no AI: local rules decide design, placeholder copy

Screenshots leave your machine only toward the AI you configured (your Claude Code account, your Anthropic or OpenAI API key, or your MCP host) — shotforge itself has no server and no telemetry. The OpenAI backend sends store: false, so responses are not kept for later retrieval; your OpenAI account's data terms still apply. Usage counts against your plan or API billing. The model's answer is validated (page count, file names, template name, hex colours, empty titles) and retried once; remaining problems are repaired by the rules.

Status and known limitations

Beta (v0.5). CI runs the test-suite on Linux with Python 3.10–3.14 on every push and PR, and weekly on macOS with Python 3.13.

  • The claude -p path is covered by tests against a fake claude executable that mimics Claude Code's JSON envelope; please report anything that differs with your real installation. The MCP server was exercised with the official MCP client on mcp 1.30 and 2.3.

  • Linux/macOS are tested; Windows is untested.

  • Device frames are generic modern-phone drawings, not Apple's official bezels. ~5–6 s per image.

  • Headlines: ≤ 3 lines, subtitles ≤ 2 lines; long text shrinks automatically, and text that still overflows is reported. The bundled Inter font has no Japanese, Korean or Arabic glyphs: shotforge warns, and --font selects another font (Arabic also needs Pillow with raqm). Screenshot analysis is a simple heuristic (a hint, not a verdict).

  • Tablet output crops a phone screenshot to the tablet ratio; supply real tablet screens. Android output reuses iOS screens unless you supply android/.

  • Arabic and Hebrew are not laid out right to left. The App Store header-only 3840x1646 size is not generated.

Development

git clone https://github.com/ibrahimatmaca/shotforge && cd shotforge
python -m venv .venv && . .venv/bin/activate
pip install -e ".[dev]"
pytest -q

See CONTRIBUTING.md.

Credits and disclaimer

  • Bundled font: Inter (SIL Open Font License 1.1, see shotforge/fonts/LICENSE-Inter.txt).

  • Template art direction was inspired by publicly viewable template galleries such as AppScreens'. shotforge is an independent project and is not affiliated with or endorsed by AppScreens, Apple or Anthropic. All templates are original code; no third-party assets are included. "App Store" and "iPhone" are trademarks of Apple Inc.; "Claude" is a trademark of Anthropic.

License

MIT

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables AI agents to search a daily-updated library of real App Store screenshots, preview videos and listing history from thousands of top apps by category, chart, look or app name. It also generates and localizes the user's own App Store screenshots at App Store resolution, restyled after any app in the library.
      MIT
    • A
      license
      A
      quality
      A
      maintenance
      Connects Claude to Apple's App Store Connect API, letting you manage apps, builds, TestFlight beta groups and testers, customer reviews and replies, sales and finance reports, and team users through natural language. Write actions such as invites, beta review submissions, and review responses require confirmation before anything is sent.
      22
      447 npm
      1
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables agents to design App Store and Google Play screenshot sets from templates or simulator captures, translate captions into 41 languages with automatic text fitting, render the pixels, and publish straight to App Store Connect or Google Play.
      1
      MIT