{
  "name": "screenmocks",
  "title": "ScreenMocks",
  "description": "A remote Model Context Protocol server for App Store screenshots: create projects, lay out screens and layers, write and translate captions across 48 App Store locales, bring in real screenshots, preview what was built and export every device size.",
  "documentation": "https://www.screenmocks.com/mcp",
  "provider": {
    "name": "ScreenMocks",
    "url": "https://www.screenmocks.com",
    "contact": "support@screenmocks.com"
  },
  "endpoint": {
    "url": "https://www.screenmocks.com/api/mcp",
    "transport": "streamable-http"
  },
  "authentication": {
    "type": "bearer",
    "header": "Authorization",
    "scheme": "Bearer",
    "tokenPrefix": "sf_pat_",
    "oauth": false,
    "obtainTokenAt": "https://www.screenmocks.com/settings?tab=mcp"
  },
  "access": {
    "plans": [
      "pro",
      "launch"
    ],
    "free": false,
    "maxTokensPerAccount": 5,
    "pricing": "https://www.screenmocks.com/pricing"
  },
  "capabilities": {
    "tools": 44,
    "resources": 0,
    "prompts": 0
  },
  "tools": [
    {
      "name": "list_catalog",
      "title": "List a ScreenMocks catalogue",
      "description": "Read one of ScreenMocks's fixed ID spaces. Call this before creating or editing layers: deviceId, finishId, presetId, sizeId, locale codes, font families, templateId, graphicId and background preset ids all come from here, and anything else is rejected. Kinds: presets (the canvas a project is AUTHORED at), sizes (the canvases it SHIPS on, for set_sizes -- Apple, Play and custom), devices (iPhone/iPad bezels, with their per-device finishes), mockups (devices rendered at an angle, screenshot mapped in perspective), locales (App Store localizations), fonts, templates, backgrounds, graphics, blobs (multi-tone organic shapes), icon_sets (the families search_icons searches), shapes, review_elements, widget_glyphs (the marks a rating is counted in and a badge leads with).",
      "readOnly": true
    },
    {
      "name": "search_icons",
      "title": "Search icons",
      "description": "Search the two icon libraries the editor's Icons tab offers. source \"vector\" (the default) searches roughly 90k line/solid icons across permissively-licensed sets (MIT/Apache-2.0/ISC -- see list_catalog kind=icon_sets) and returns ids (\"prefix:name\") to pass to add_layers as type: \"icon\", which inserts them as RECOLOURABLE vector shapes. source \"glossy\" searches the 1,285 Fluent Emoji 3D icons, which are pictures rather than vectors: they are not recolourable, and each hit carries an importUrl -- feed that to import_asset_from_url, then place the assetId as an image layer. That two-step is deliberate; image bytes never travel through a tool call.",
      "readOnly": true
    },
    {
      "name": "search_stock_photos",
      "title": "Search stock photography",
      "description": "Search the licensed Pexels library the editor's Stock tab searches, free to use commercially with no attribution required. An empty query returns the curated feed. Each hit carries a sourceUrl -- hand that to import_asset_from_url to bring the photo in as an ordinary asset, then place it as an image layer or set it as a screen background with update_screen. CREDIT IS A REQUIREMENT, not decoration: Pexels asks for the photographer wherever the photo is shown and a visible link back, so `photographer` and `pageUrl` travel with every hit and the asset keeps the credit in its name. Searching spends a shared monthly allowance, so search once and pick, rather than paging through the library.",
      "readOnly": true
    },
    {
      "name": "capture_app_screenshots",
      "title": "How to capture real app screenshots",
      "description": "Call this FIRST when asked to make, capture or update App Store screenshots of a real app. It captures nothing itself: it returns the step-by-step playbook you run on your own machine (boot a simulator, force a clean 9:41 status bar, shoot pixel-exact screens of the seeded app, choose the screens a store visitor would want), ending with the uploads and placements that land them in a ScreenMocks project via create_upload_url and set_screenshot.",
      "readOnly": true
    },
    {
      "name": "list_projects",
      "title": "List projects",
      "description": "List the ScreenMocks projects in the caller's account, most recently updated first.",
      "readOnly": true
    },
    {
      "name": "get_project",
      "title": "Get a project",
      "description": "Read a project. Returns a summary: the canvas it is authored at, every size it ships on, its languages, one line per screen, the board, and its settings (description, App Store link, guides). Pass screenId to also get that screen's full layer list, which is the only way to learn layer ids. Pass lang to see the text and screenshots as that locale resolves them (locales inherit from the main language until overridden); layers that diverge from main are marked with divergedIn. Pass layout to also measure every layer on that screen: its box, whether it runs off the canvas, and which layers it sits on top of. Use it after writing coordinates, and after translating -- a locale whose wording is longer overflows invisibly otherwise, which is the usual way a set breaks.",
      "readOnly": true
    },
    {
      "name": "create_project",
      "title": "Create a project",
      "description": "Create a screenshot project. A template seeds five ready screens (call list_catalog kind=templates); pass templateId=null for one blank screen. The project starts in a single language, which becomes the main language every other locale inherits from -- add more with set_languages.",
      "readOnly": false
    },
    {
      "name": "update_project",
      "title": "Rename a project",
      "description": "Rename a project. This is the ScreenMocks project's own label, not the app name shown on any screenshot -- headlines are text layers, edited with set_translations.",
      "readOnly": false
    },
    {
      "name": "update_project_settings",
      "title": "Set a project's settings",
      "description": "The three things that belong to a project but to no layer, from the editor's Settings tab. \"description\" is what the app is, in the author's words: a working note, never rendered on a canvas and never exported -- NOT the App Store listing description, which is per-locale and lives on the App Store version. \"appStore\" is which listing this design ships to; only the public identifiers live here, because an App Store Connect API key signs for a whole developer account and never touches a project. \"board\" is the backdrop the frames are laid out on: editor chrome, never drawn into an export, and its dot/grid ink is derived from the colour rather than chosen, so a dark board gets light ink on its own. LEAVE THE BOARD ALONE unless the person asked for a different one: every project starts on the default white board with the dot lattice, that is the workspace they know, and since it is never exported a dark board matched to a dark design changes nothing about the screenshots and only changes the room they work in. Nothing here creates, prices, edits or submits anything on the App Store.",
      "readOnly": false
    },
    {
      "name": "set_guides",
      "title": "Set the rulers and guides",
      "description": "Replace a project's ruler guides. Positions are BASE-SIZE canvas coordinates and are frame-local: one entry draws on every screen at once, which is the point on a board showing a whole set side by side. \"v\" is an x inside a frame, \"h\" a y. Guides are a drawing aid -- they are never exported. Pass rulers: false to hide the rulers, and with them the guides.",
      "readOnly": false
    },
    {
      "name": "delete_project",
      "title": "Delete a project",
      "description": "Permanently delete a project and everything in it. This cannot be undone.",
      "readOnly": false
    },
    {
      "name": "set_languages",
      "title": "Set project languages",
      "description": "Set the exact locale list for a project, and optionally which one is main. Adding a locale costs nothing: it inherits every headline, screenshot and position from main until you override it with set_translations, set_screenshot or update_layer. This REPLACES the list -- any locale you leave out is removed, and its translations, screenshots and layout overrides are permanently deleted. The main language is always kept.",
      "readOnly": false
    },
    {
      "name": "get_divergence",
      "title": "Show how locales differ from main",
      "description": "Report which locales have diverged from the main language and where. Use it to see translation coverage, or to find layout an agent or the user overrode per locale (typically because a translation ran long).",
      "readOnly": true
    },
    {
      "name": "reset_overrides",
      "title": "Reset a locale back to main",
      "description": "Drop layout overrides so a locale re-inherits main's design. Scope: \"locale\" resets every layer in that locale project-wide; \"layer\" resets one layer; \"properties\" resets only the named properties of one layer. This never touches translations or screenshots -- use set_translations / set_screenshot with null for those. Overridable properties: x, y, width, height, rotation, fontSize, lineHeight, letterSpacing, hidden, deviceId, finishId, fit, fitX, fitY, fitZoom, statusBar.",
      "readOnly": false
    },
    {
      "name": "set_sizes",
      "title": "Set where a project ships",
      "description": "Set the exact list of canvases a project ships on: iPhone, iPad, Android, Android tablet, or a custom size. Adding one costs nothing and copies nothing, because every size is derived from the base size. This REPLACES the list; any size you leave out is removed along with anything pinned to it. The base size is always kept. See list_catalog kind=sizes for the ids.",
      "readOnly": false
    },
    {
      "name": "set_size_languages",
      "title": "Limit a size to some languages",
      "description": "Ship one canvas in fewer languages than the rest of the project, e.g. iPhone everywhere but iPad in English only. Pass languages: null to go back to shipping every language on that size. Only affects export; the design and its translations are untouched.",
      "readOnly": false
    },
    {
      "name": "set_size_screens",
      "title": "Limit a size to some screens",
      "description": "Ship one canvas with fewer screens than the rest of the project, e.g. six screenshots on iPhone and three on iPad, which is what App Store listings routinely do. Pass screenIds: null to go back to shipping every screen on that size. The screens, their layers and their overrides are untouched; only what this canvas EXPORTS changes. Every size must keep at least one screen. Order always follows the project, so this cannot give one canvas an order of its own -- use reorder_screens for that.",
      "readOnly": false
    },
    {
      "name": "get_size_divergence",
      "title": "Show how sizes differ from the design",
      "description": "Report which canvases have been adjusted away from the base design and on how many screens. Use it to find work an agent or the user pinned to one size, typically because a taller or wider canvas needed the layout moved.",
      "readOnly": true
    },
    {
      "name": "reset_size",
      "title": "Reattach a size to the design",
      "description": "Drop every layout adjustment pinned to one canvas, so it follows the base design again. Wording and screenshots pinned to that size are kept, the same way resetting a locale keeps its translations.",
      "readOnly": false
    },
    {
      "name": "apply_template",
      "title": "Apply a template",
      "description": "Replace ALL screens in a project with a template's screens. Destructive: existing layers, translations and per-locale overrides are discarded. Templates build in the main language. See list_catalog kind=templates.",
      "readOnly": false
    },
    {
      "name": "add_template_screens",
      "title": "Add screens from a template",
      "description": "Append a template's screens to a project WITHOUT replacing anything, which is what the editor's Templates panel does: take one screen from one template and the next from another. Pass `screens` to choose which ones by index (0-based, in the order you name them); omit it for all of them. The screens are re-projected onto the project's own canvas and re-keyed to its main language, and the imagery they reference comes with them. Use apply_template instead only when you mean to discard everything the project already has. See list_catalog kind=templates.",
      "readOnly": false
    },
    {
      "name": "add_screen",
      "title": "Add a screen",
      "description": "Add a screenshot slot. Apple shows up to 10 per localization, in order. Pass copyOfScreenId to duplicate an existing screen (layers, background and every locale override come with it), otherwise you get a blank one. A new screen ships on every size unless you name sizeIds; a DUPLICATE ships wherever its source ships, and sizeIds is refused there rather than quietly overriding that. See list_catalog kind=sizes.",
      "readOnly": false
    },
    {
      "name": "update_screen",
      "title": "Rename a screen or set its background",
      "description": "Rename a screen and/or set its background. Background is one of: a preset id (list_catalog kind=backgrounds), a solid hex colour, a gradient (linear or radial, two ends or a multi-stop ramp), or an uploaded image (assetId, cover-fitted), which can also carry a blur and a dim -- the two things that turn a photo into a backdrop copy can be read against. Backgrounds are part of the design, not the localization axis: every locale and every size shares them. Set applyToAllScreens for a consistent set.",
      "readOnly": false
    },
    {
      "name": "delete_screen",
      "title": "Delete a screen",
      "description": "Delete a screen and its layers. A project must keep at least one screen. Pass sizeId to stop shipping it on ONE canvas instead: the screen, its layers and every override it carries are untouched and the other sizes still draw it, which is how a listing goes out with six iPhone screenshots and three iPad ones. Every size must keep at least one screen. See list_catalog kind=sizes.",
      "readOnly": false
    },
    {
      "name": "reorder_screens",
      "title": "Reorder screens",
      "description": "Set the order screens appear in on the App Store. Pass every screen id exactly once, in the order you want.",
      "readOnly": false
    },
    {
      "name": "add_layers",
      "title": "Add layers to a screen",
      "description": "Add one or more layers to a screen, bottom to top. Batched because a screen is usually a background, a headline and a device together. Omitted geometry gets sensible defaults scaled to the canvas. Text and device screenshots are seeded in the MAIN language, which every other locale then inherits. A \"device\" frame gets a status bar by default (statusBar: \"auto\"), because a capture with no device chrome looks fake inside a bezel; pass statusBar: \"off\" when the screenshot already has one. \"blob\" is one of the multi-tone organic shapes from list_catalog kind=blobs, recoloured by tone rather than by a single fill. \"review_element\" adds ONE social-proof widget (a rating, a laurel badge, a rating pill, a review card, a chart badge) whose score, copy and colours are all editable afterwards. \"icon\" takes an id from search_icons and inserts it as a recolourable vector shape; an icon whose artwork needs rasterizing is rejected with a reason instead -- try a different one.",
      "readOnly": false
    },
    {
      "name": "update_layer",
      "title": "Update a layer",
      "description": "Patch a layer. WHICH LOCALE YOU PASS MATTERS. With lang = the main language you edit the design itself, and every locale that has not diverged follows along. With any other locale, geometry and type size (x, y, width, height, rotation, fontSize, lineHeight, letterSpacing, hidden) diverge for that locale ONLY, leaving the design untouched -- this is how you fix a long translation without disturbing the others. Everything else (colour, font, device, name, lock, flip, spill, and every widget property) always edits the shared design whatever locale you pass. Setting an overridden value back to the design's value re-attaches it. To change wording or screenshots use set_translations / set_screenshot.",
      "readOnly": false
    },
    {
      "name": "delete_layers",
      "title": "Delete layers",
      "description": "Delete layers from a screen, in every locale.",
      "readOnly": false
    },
    {
      "name": "duplicate_layers",
      "title": "Duplicate layers",
      "description": "Copy layers within a screen. Copies land just above their source, offset by 40px, and carry every locale override with them.",
      "readOnly": false
    },
    {
      "name": "reorder_layers",
      "title": "Reorder layers",
      "description": "Set a screen's render order. Pass every layer id on the screen exactly once, bottom first: index 0 renders underneath everything else.",
      "readOnly": false
    },
    {
      "name": "move_layers",
      "title": "Move layers to another screen",
      "description": "Move layers from one screen to another, or on and off the board. This is the drag that carries a layer out of its frame in the editor. The layers keep their translations, screenshots and per-size/per-locale overrides, and land on top of the target. A whole group travels together with its name. The board (\"@board\") is working space: a layer parked there is never exported and never resolves a per-size or per-locale override while it sits there, but its stored overrides are kept untouched, so moving it back into a frame makes them live again exactly as they were.",
      "readOnly": false
    },
    {
      "name": "align_layers",
      "title": "Align layers",
      "description": "Line layers up, the way the editor's Align buttons do. The thing that moves is a UNIT -- one ungrouped layer, or one whole group taking a single delta -- so aligning a group slides it as a block instead of stacking its members on one edge. One unit always aligns to its FRAME (there is nothing else to line it up with); two or more align to each other unless target=\"frame\". Boxes are measured frame-local, so one call can span several screens and put the same headline into one shared column across the whole set. Layers on the board are refused, exactly as the editor greys the buttons for them. The move follows update_layer's scoping: name a sizeId and the alignment is pinned to that canvas alone, leaving the design as it was; omit it to align the design itself. ACCURACY: every kind but text carries its own width and height, so its box is exact. A text layer's box is its LETTERS -- cap height down to the descender, not the line boxes around them, which is the same box the editor draws its handles on -- so both axes are MEASURED: on this server from the font size alone, an estimate within a percent or two of it. Set x or y directly when a headline has to be exact.",
      "readOnly": false
    },
    {
      "name": "group_layers",
      "title": "Group layers",
      "description": "Group 2+ layers on a screen so they move, and are selected, together, like Ctrl+G in the editor. Returns the new groupId; layer summaries already report a layer's groupId. Grouping across screens is refused -- pass ids from one screen.",
      "readOnly": false
    },
    {
      "name": "ungroup_layers",
      "title": "Ungroup layers",
      "description": "Dissolve one or more groups, leaving their members in place as ordinary layers.",
      "readOnly": false
    },
    {
      "name": "rename_group",
      "title": "Rename a group",
      "description": "Rename a layer group. The name is what the layers panel shows for the block, so it is worth setting to what the group IS (\"Phone + caption\") rather than leaving it as \"Group 2\".",
      "readOnly": false
    },
    {
      "name": "set_translations",
      "title": "Set translations",
      "description": "Write text for text layers, in any locale, in one call. Batched because translating a project means writing every headline x every locale, and one call per string does not scale to 10 screens x 12 languages. Writing the MAIN language edits the source copy, and any locale that has not been translated yet follows it. Writing another locale overrides it there only. Pass text: null to drop a translation and re-inherit main. A widget layer holds several strings, one per slot (a review card has \"quote\" and \"author\"); name the one you mean with `slot`, or address it as \"layerId#slot\" the way get_translations reports it.",
      "readOnly": false
    },
    {
      "name": "get_translations",
      "title": "Export every string",
      "description": "Read every text layer x every locale, for translating or reviewing. Shows the main-language source next to each locale's value and whether that value is translated or still inherited. Feed the result back through set_translations.",
      "readOnly": true
    },
    {
      "name": "set_screenshot",
      "title": "Set a device screenshot",
      "description": "Put an uploaded image inside a device frame for one locale. Screenshots are per-locale because a localized app UI looks different in each language. Set the main language first: every other locale shows that image until given its own. Pass assetId: null to drop a locale's screenshot and re-inherit main.",
      "readOnly": false
    },
    {
      "name": "list_assets",
      "title": "List uploaded assets",
      "description": "List the images in the caller's asset library, with the ids and dimensions layers reference.",
      "readOnly": true
    },
    {
      "name": "import_asset_from_url",
      "title": "Import an image from a URL",
      "description": "Fetch an image from a publicly reachable URL into the asset library and return its assetId, for use with set_screenshot or an image layer. Use this when the image is already hosted; use create_upload_url for a local file. PNG, JPEG, WebP or GIF only: rasterize an SVG before importing it. Never send image bytes through a tool call.",
      "readOnly": false
    },
    {
      "name": "create_upload_url",
      "title": "Get an upload URL for a local image",
      "description": "Get a short-lived upload URL for a local image, plus the assetId it will land under. Upload the bytes yourself with an HTTP PUT (for example: curl -X PUT --data-binary @shot.png -H \"Content-Type: image/png\" \"<uploadUrl>\"), then use the assetId with set_screenshot or an image layer. The assetId is reserved immediately, but is only usable once the bytes land. sizeBytes must be the file's exact size (e.g. from `wc -c < shot.png`): the URL refuses an upload of any other length.",
      "readOnly": false
    },
    {
      "name": "delete_asset",
      "title": "Delete an uploaded asset",
      "description": "Remove an image from the asset library and delete its bytes. Layers still pointing at it are left exactly as they are, the same way the editor's own Delete behaves: unpicking a reference would be an edit to someone's design made on the side. Check list_assets and the screens that use it first -- this cannot be undone. Assets count against the account's storage ceiling, which is the reason to bother.",
      "readOnly": false
    },
    {
      "name": "render_preview",
      "title": "See a screen",
      "description": "Render screens to images you can actually LOOK at, returned inline. Use it after laying out a screen, after filling in screenshots, and after translating -- it is the only way to see what you built. It renders the same canvas the export renders, at a smaller pixel size, so what you see is the real design rather than a sketch of it. Pair it with get_project layout: true, which gives the numbers behind what you are looking at (boxes, overflow, overlaps). NOT a submission asset: export_project produces those at full resolution.",
      "readOnly": true
    },
    {
      "name": "export_project",
      "title": "Export screenshots",
      "description": "Render finished screenshots and return a link to a ZIP, foldered per locale and named for App Store upload. Output is exactly the canvas preset's pixel dimensions and fully opaque (Apple rejects transparency). Defaults to every screen in every language. NOT YET AVAILABLE on the hosted endpoint: server-side rendering has not shipped, so this call currently fails with NOT_AVAILABLE. Ask the user to export from the ScreenMocks editor (Export button) instead.",
      "readOnly": false
    }
  ]
}
