# PhotoStudioGen MCP Server > Connect any AI agent to a PhotoStudioGen account through the Model Context Protocol. 14 tools: images, video, Recast, Copy Pose, try-ons, headshots, product shots, upscaling, cut-outs, stitching, and deleting. Every call uses the account's normal credit balance. Human-readable version: https://photostudiogen.com/mcp ## Quick start 1. Create an API key at https://photostudiogen.com/account#api-keys. It starts with `psg_` and is shown once. 2. Add the server to your MCP client with that key (below). 3. Ask your agent something like "make a 2:3 portrait of a lighthouse at dusk". It finds the tools on its own. ## Connect Endpoint: `https://photostudiogen.com/api/mcp`. Streamable HTTP, stateless; no session to open or keep alive. Cursor (`.cursor/mcp.json`), Claude Desktop, Windsurf: ```json { "mcpServers": { "photostudiogen": { "url": "https://photostudiogen.com/api/mcp", "headers": { "Authorization": "Bearer psg_your_api_key_here" } } } } ``` Claude Code: ```sh claude mcp add --transport http photostudiogen https://photostudiogen.com/api/mcp \ --header "Authorization: Bearer psg_your_api_key_here" ``` Test a key with curl (swap `tools/call` for `tools/list` to get every input schema): ```sh curl -X POST https://photostudiogen.com/api/mcp \ -H "Authorization: Bearer psg_your_api_key_here" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"check_credits","arguments":{}}}' ``` ## Authentication Send `Authorization: Bearer psg_…` on every request. A missing or revoked key gets HTTP 401 before any tool runs. Calls are billed to the account that owns the key. Anyone with the key can spend its credits; revoke a leaked key in Account → API keys. ## How a call works Each tool replies with MCP `content` blocks of type `text` holding JSON. Image tools reply with `{ "images": [{ "id", "url" }] }`; video, stitch, and delete tools reply with their own small objects. Keep the ids: `get_result`, `stitch_videos`, and `delete_media` take them. Errors come back as a normal result with `isError: true` and a message starting with `Error:`, written to be shown to a person as-is. Image calls wait for the render, usually 10–40 seconds. Use an HTTP timeout of at least 2 minutes. ## Sending photos - Every photo is a public `https://` URL a server can download without logging in. Signed links work until they expire. - JPG, PNG, and WebP work best. A link to a video, or one that returns 404/410, is refused before you're charged. - URLs returned by any tool can go straight back in as inputs, so tools chain. - Clear, well-lit photos with the face visible give the best likeness. | Tool | Photos | | --- | --- | | generate_image | 0–5 | | generate_video | 0–10. One photo is the opening frame; 2+ build a new shot of the person. | | recast | 1 person + 1 reference + 0–2 partners | | copy_pose | 1 | | outfit_try_on | 1 person + 1 outfit | | change_scene | 1 person + 1 scene | | headshots | 1 | | product_photo | 1 | | upscale | 1 | | remove_background | 1 | ## Video jobs Video renders in the background. Credits are taken when the job starts. 1. `generate_video` returns `{ "id", "status": "processing" }` straight away. 2. Call `get_result` with that id every 5–10 seconds until the status is `completed` (with a `url`) or `failed`. Most clips take 1–3 minutes. 3. Optionally pass several finished ids to `stitch_videos` to join them. Never re-run `generate_video` because your side timed out: the job may already be running and billed. Poll `get_result` instead. `sound` is off by default (silent clip); `true` adds sound generated to match the scene, at the same price. Length is any whole number of seconds from 3 to 30. Resolutions: 480p (7 credits/sec), 720p (14 credits/sec), 1080p (28 credits/sec). A 5-second 480p clip is 35 credits. SFW-only accounts are capped at 12 seconds and 720p, and are charged for the capped clip. ## Tool reference ### generate_image Make an image from a prompt, or edit up to 5 photos. **5–14 credits.** With no photos it creates a new image from your prompt. With photos it edits them: change the background, fix lighting, merge people from several shots, restyle. Price: Text only: a flat 5 credits. With photos: the quality tier (standard 5 · ultra 10 · max_effort 14). | Parameter | Type | Required | Default | Details | | --- | --- | --- | --- | --- | | prompt | string | yes | — | What to make, or what to change in the photos. | | image_urls | string (https URL)[] | no | — | 0–5 photos to edit. Leave out for a brand-new image. | | aspect_ratio | "1:1" \| "2:3" \| "3:2" \| "3:4" \| "4:3" \| "9:16" \| "16:9" | no | "2:3", or the photo's shape when editing | Output shape. | | quality | "standard" \| "ultra" \| "max_effort" | no | "max_effort" | Only used when editing photos. standard 5 · ultra 10 · max_effort 14 credits. | Returns: { "images": [{ "id": "…", "url": "…" }] }. Keep the id to delete it later. Example call: ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "generate_image", "arguments": { "prompt": "Golden-hour portrait on a rooftop, soft film grain", "aspect_ratio": "2:3" } } } ``` ### generate_video Make a 3–30s clip from a prompt or from up to 10 photos. **21–840 credits.** With one photo, that photo becomes the opening frame. With 2–10 photos it builds a new shot of the same person from all of them. With none, it's text-to-video. Video renders in the background: this call returns a job id straight away, and you collect the clip with get_result. Price: Credits per second × length. 480p 7/sec · 720p 14/sec · 1080p 28/sec. A 5s 480p clip is 35 credits. Sound doesn't change the price. | Parameter | Type | Required | Default | Details | | --- | --- | --- | --- | --- | | prompt | string | yes | — | The motion, scene, or story. | | image_urls | string (https URL)[] | no | — | 0–10 photos. Must be photos, not videos. | | duration_seconds | integer 3–30 | no | 5 | Clip length in whole seconds. | | resolution | "480p" \| "720p" \| "1080p" | no | "480p" | 480p 7 credits/sec · 720p 14 credits/sec · 1080p 28 credits/sec. | | aspect_ratio | "16:9" \| "9:16" \| "1:1" \| "4:3" \| "3:4" | no | "9:16", or the photo's shape | Output shape. | | sound | boolean | no | false | true adds sound generated to match the scene. false gives a silent clip. | Returns: { "id": "…", "status": "processing" }. Pass the id to get_result. - SFW-only accounts are capped at 12 seconds and 720p, and the price follows the cap. Example call: ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "generate_video", "arguments": { "prompt": "She turns to the camera and smiles, hair moving in the wind", "image_urls": [ "https://example.com/me.jpg" ], "duration_seconds": 5, "resolution": "720p", "sound": true } } } ``` ### get_result Check on a video job. **Free.** Call it every 5–10 seconds with the id from generate_video. Most clips finish in 1–3 minutes; long 1080p clips take longer. | Parameter | Type | Required | Default | Details | | --- | --- | --- | --- | --- | | id | string | yes | — | The job id from generate_video. | Returns: { "status": "processing" }, then { "status": "completed", "url": "…mp4" }, or { "status": "failed", "error": "…", "refunded": true }. - The same id is the clip's library id, so you can pass it straight to stitch_videos. Example call: ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_result", "arguments": { "id": "cm2x9k1ab0001" } } } ``` ### recast Put a person into any reference shot. **14 credits.** Keeps the pose, outfit, and framing of the reference and puts your person in it. Add up to two partner photos for group shots. | Parameter | Type | Required | Default | Details | | --- | --- | --- | --- | --- | | person_image_url | string (https URL) | yes | — | A clear photo of the person, face and body visible. | | pose_image_url | string (https URL) | yes | — | The reference shot. A photo, drawing, or stick figure all work. | | partner_image_urls | string (https URL)[] | no | — | 0–2 more people to put in the shot. | | prompt | string | no | — | Extra direction, e.g. "wearing a red dress". | Returns: { "images": [{ "id": "…", "url": "…" }] }. Keep the id to delete it later. - Not available on SFW-only accounts. Example call: ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "recast", "arguments": { "person_image_url": "https://example.com/me.jpg", "pose_image_url": "https://example.com/pose.jpg" } } } ``` ### copy_pose New poses of the same person, same outfit and room. **5–56 credits.** Give it one photo and pick up to four poses. Every shot keeps the face, outfit, location, and lighting. Price: Per pose, at the quality tier: standard 5 · ultra 10 · max_effort 14. | Parameter | Type | Required | Default | Details | | --- | --- | --- | --- | --- | | image_url | string (https URL) | yes | — | The photo to build from. | | poses | ("three-quarter" \| "from-behind" \| "low-angle" \| "close-up" \| "seated")[] | yes | — | 1–4 poses. You get one image per pose. | | copy_expression | boolean | no | true | Keep the facial expression from the photo. | | quality | "standard" \| "ultra" \| "max_effort" | no | "max_effort" | Per pose: standard 5 · ultra 10 · max_effort 14 credits. | | aspect_ratio | "1:1" \| "2:3" \| "3:2" \| "3:4" \| "4:3" \| "9:16" \| "16:9" | no | the photo's shape | Output shape. | Returns: { "images": [{ "id", "url" }, …] }, one per pose. Example call: ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "copy_pose", "arguments": { "image_url": "https://example.com/me.jpg", "poses": [ "three-quarter", "close-up" ], "quality": "ultra" } } } ``` ### outfit_try_on Put any outfit on any person. **10 credits.** Takes the clothes from one photo and puts them on the person in another. A product shot or flat lay works best. | Parameter | Type | Required | Default | Details | | --- | --- | --- | --- | --- | | person_image_url | string (https URL) | yes | — | The person, face and body visible. | | clothing_image_url | string (https URL) | yes | — | The clothes. If someone is wearing them, only the clothes are used. | | prompt | string | no | — | Extra direction, e.g. "full body, walking down a street". | Returns: { "images": [{ "id": "…", "url": "…" }] }. Keep the id to delete it later. Example call: ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "outfit_try_on", "arguments": { "person_image_url": "https://example.com/me.jpg", "clothing_image_url": "https://example.com/jacket.jpg" } } } ``` ### change_scene Move a person to a new location. **5–14 credits.** Keeps the person, pose, and outfit, and swaps the background for the scene in a second photo. Price: The quality tier: standard 5 · ultra 10 · max_effort 14. | Parameter | Type | Required | Default | Details | | --- | --- | --- | --- | --- | | image_url | string (https URL) | yes | — | The person photo. | | scene_image_url | string (https URL) | yes | — | A photo of the new location. | | quality | "standard" \| "ultra" \| "max_effort" | no | "max_effort" | standard 5 · ultra 10 · max_effort 14 credits. | Returns: { "images": [{ "id": "…", "url": "…" }] }. Keep the id to delete it later. Example call: ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "change_scene", "arguments": { "image_url": "https://example.com/me.jpg", "scene_image_url": "https://example.com/beach.jpg" } } } ``` ### headshots Professional headshots from a selfie. **5–20 credits.** Pick a style, expression, outfit, background, and lighting, and get up to four variations. Price: 5 credits per variation. | Parameter | Type | Required | Default | Details | | --- | --- | --- | --- | --- | | image_url | string (https URL) | yes | — | A clear photo of the face. | | style_preset | "corporate" \| "executive" \| "linkedin" \| "creative" \| "academic" \| "casual" | no | "corporate" | Overall look. | | expression | "confident-smile" \| "friendly" \| "serious" \| "warm" \| "neutral" | no | "confident-smile" | Facial expression. | | clothing | "auto" \| "dark-suit" \| "navy-blazer" \| "white-shirt" \| "smart-casual" \| "turtleneck" \| "lab-coat" | no | "auto" | "auto" keeps what they're wearing. | | background | "studio" \| "office" \| "plain-white" \| "plain-gray" \| "outdoor" \| "dark" | no | "studio" | Backdrop. | | lighting | "studio-soft" \| "natural" \| "rembrandt" \| "ring-light" \| "dramatic" | no | "studio-soft" | Lighting setup. | | variations | "1" \| "2" \| "4" | no | "1" | How many images. Sent as a string. | | aspect_ratio | "1:1" \| "2:3" \| "3:2" \| "3:4" \| "4:3" \| "9:16" \| "16:9" | no | the photo's shape | Output shape. | | prompt | string | no | — | Extra direction. | Returns: { "images": [{ "id", "url" }, …] }, one per variation. Example call: ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "headshots", "arguments": { "image_url": "https://example.com/me.jpg", "style_preset": "linkedin", "background": "office", "variations": "2" } } } ``` ### product_photo Studio product shots from a phone snap. **5–20 credits.** The product stays exactly as it is; only the setup, surface, and lighting change. Price: 5 credits per variation. | Parameter | Type | Required | Default | Details | | --- | --- | --- | --- | --- | | product_image_url | string (https URL) | yes | — | A photo of the product. A plain phone snap is fine. | | shot_type | "ecommerce" \| "lifestyle" \| "flat-lay" \| "hero" \| "macro" \| "in-hand" | no | "ecommerce" | Kind of shot. | | surface | "white-sweep" \| "marble" \| "wood" \| "concrete" \| "fabric" \| "colour-block" \| "outdoor" | no | "white-sweep" | Surface and backdrop. | | lighting | "softbox" \| "bright-airy" \| "window" \| "dramatic" \| "golden-hour" \| "neon" | no | "softbox" | Lighting setup. | | variations | "1" \| "2" \| "4" | no | "1" | How many images. Sent as a string. | | aspect_ratio | "1:1" \| "2:3" \| "3:2" \| "3:4" \| "4:3" \| "9:16" \| "16:9" | no | "1:1" | Output shape. | | prompt | string | no | — | Extra direction, e.g. "a few eucalyptus leaves". | Returns: { "images": [{ "id", "url" }, …] }, one per variation. Example call: ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "product_photo", "arguments": { "product_image_url": "https://example.com/mug.jpg", "shot_type": "lifestyle", "surface": "wood", "lighting": "window" } } } ``` ### upscale Sharpen and enlarge a photo. **5 credits.** Best for faces, portraits, and products. | Parameter | Type | Required | Default | Details | | --- | --- | --- | --- | --- | | image_url | string (https URL) | yes | — | The image to upscale. | | scale_factor | "2" \| "4" \| "6" | no | 2 | How many times larger. | | creativity | integer 0–10 | no | 0 | 0 keeps the original exactly. Higher adds more invented detail. | | output_format | "png" \| "jpg" | no | "png" | File type. | Returns: { "images": [{ "id": "…", "url": "…" }] }. Keep the id to delete it later. Example call: ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "upscale", "arguments": { "image_url": "https://example.com/me.jpg", "scale_factor": 4 } } } ``` ### remove_background Cut the subject out as a transparent PNG. **5 credits.** Tuned for people. Products and pets work too, with softer edges. | Parameter | Type | Required | Default | Details | | --- | --- | --- | --- | --- | | image_url | string (https URL) | yes | — | The photo to cut out. | Returns: One PNG URL with a transparent background. Cut-outs are not saved to your library. - If the cut-out fails, the credits are refunded in full. Example call: ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "remove_background", "arguments": { "image_url": "https://example.com/me.jpg" } } } ``` ### stitch_videos Join finished clips into one video. **1 credit.** Puts clips from your library end to end, with optional trims, and saves the result to your library. | Parameter | Type | Required | Default | Details | | --- | --- | --- | --- | --- | | clips | { id: string, startAt?: number, endAt?: number }[] | yes | — | 1–50 clips in play order. Ids come from generate_video. startAt / endAt trim each clip, in seconds. | | ratio | "original" \| "9:16" \| "1:1" \| "16:9" | no | "original" | Output frame. Clips that don't match are cropped to fill. | Returns: { "id": "…", "url": "…mp4" } - Each clip must be finished and yours. Up to 30 seconds per clip and 5 minutes in total. - If stitching or saving fails, the credit is refunded. Example call: ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "stitch_videos", "arguments": { "clips": [ { "id": "cm2x9k1ab0001" }, { "id": "cm2x9k1ab0002", "endAt": 3.5 } ], "ratio": "9:16" } } } ``` ### delete_media Permanently delete images or videos. **Free.** Removes items from your library and deletes the files, so their URLs stop working. Works on anything you made, on the site or over MCP. | Parameter | Type | Required | Default | Details | | --- | --- | --- | --- | --- | | ids | string[] | yes | — | 1–50 library ids, as returned by the other tools. | Returns: { "deleted": ["…"], "failed": [{ "id": "…", "error": "…" }] } - This can't be undone, and credits aren't refunded. - Ids that aren't in your library come back under failed; nothing else is touched. Example call: ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "delete_media", "arguments": { "ids": [ "cm2x9k1ab0001", "cm2x9k1ab0002" ] } } } ``` ### check_credits Your balance and the price list. **Free.** Handy before a big batch. The prices come from the same table the server charges from. No parameters. Returns: Plain text: your balance, then prices for every tool. Example call: ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "check_credits", "arguments": {} } } ``` ## Limits | What | Limit | | --- | --- | | Photos per image edit | 5 | | Photos per video | 10 | | Video length | 3–30 seconds, whole seconds | | Video resolution | 480p, 720p, 1080p | | Video sound | On or off (default off), same price | | Image aspect ratios | 1:1, 2:3, 3:2, 3:4, 4:3, 9:16, 16:9 | | Video aspect ratios | 16:9, 9:16, 1:1, 4:3, 3:4 | | Copy Pose poses per call | 1–4 | | Headshot / product variations | 1, 2, or 4 | | Recast partners | 0–2 | | Upscale factor | 2×, 4×, 6× | | Stitch | 1–50 clips, each up to 30s, 5 minutes total. Ratios: original, 9:16, 1:1, 16:9 | | Delete per call | 1–50 ids | | SFW-only accounts | Video up to 12s and 720p. No Recast. | ## Pricing (credits) Same prices as the web app, from the same balance. Buy credits at https://photostudiogen.com/pricing; purchased credits never expire. `check_credits` returns this list live. | Operation | Credits | | --- | --- | | New image from text | 5 | | Edit / Scene / per pose — Good (standard) | 5 | | Edit / Scene / per pose — Better (ultra) | 10 | | Edit / Scene / per pose — Best (max_effort) | 14 | | Video — 480p | 7 / second | | Video — 720p | 14 / second | | Video — 1080p | 28 / second | | Recast | 14 | | Outfit try-on | 10 | | Headshots | 5 each | | Product photo | 5 each | | Upscale | 5 | | Remove background | 5 | | Stitch videos | 1 | | get_result, check_credits, delete_media | Free | ## Errors | You see | Why | Charged? | | --- | --- | --- | | HTTP 401 Invalid or missing API key | No key, a typo, or a revoked key | No | | You need N credits but only have M | Balance too low for this call | No | | Please fill in all required fields | A required parameter is missing or empty | No | | That photo isn't available any more | A photo URL returned 404 or 410 | No | | That reference is a video | A photo parameter points at a video | No | | This feature is not available | The tool is off for your account (e.g. Recast on SFW-only) | No | | get_result → "status": "failed" | The render failed or was blocked by moderation | See refunds | ## Refunds and billing - Credits are taken when a call starts. Anything rejected before rendering is never charged. - If a render finishes but we fail to save it, you get the credits back. get_result shows "refunded": true. - If moderation blocks a render, a small fee is kept and the rest comes back. - A render that ran and failed on our provider's side isn't refunded in full, because it was billed to us. - Remove background and stitching refund in full if they fail. ## Good to know - Everything made over MCP lands in the account's library in the app. - Returned URLs stay up until the item is deleted, and are yours to use commercially. `delete_media` removes files for good, with no credit refund. - The server is stateless; every request stands alone.