GhostfeedGhostfeed MCPv5.3.0
MCP docs

Studio

The agent slideshow creation path: start a blank deck, then search Pinterest, read a TikTok/Instagram post to remix, browse the library images, and place every background and text box yourself, with the dashboard updating live. The agent is the creative director. (The one-shot server-composed create is a dashboard-only convenience, not exposed here.)

search_pinterest

READ

REST: POST /api/v2/pinterest/search

Search Pinterest directly (up to 8 queries, default 12 images each) and get raw image urls back. Search for SCENES, not topics. LOOK at the images yourself before picking - that is the whole point of this tool - then set_slide_background with the winner AND the query that found it, so the editor reopens on that search and shuffle can re-run it. Costs no credits.

Input schema
{
  "type": "object",
  "properties": {
    "queries": {
      "minItems": 1,
      "maxItems": 8,
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1,
        "maxLength": 100
      },
      "description": "Up to 8 Pinterest search queries. Search for SCENES, not topics: candid photos of moments, never concept words that fetch infographics."
    },
    "perQuery": {
      "description": "Images to return per query (default 12).",
      "type": "integer",
      "minimum": 1,
      "maximum": 20
    },
    "workspace": {
      "description": "Target workspace: a slug, id, or exact name from list_workspaces. Omit to use this connection's default workspace. Must be inside the credential's grant and your current access; overrides the connection-level X-Workspace-Id header.",
      "type": "string"
    }
  },
  "required": [
    "queries"
  ],
  "$schema": "http://json-schema.org/draft-07/schema#"
}
Example response
{
  "success": true,
  "data": {
    "workspace": {
      "id": "6a18c36ee586c7e44e4ece67",
      "name": "Client A",
      "slug": "client-a"
    },
    "results": [
      {
        "query": "candid morning skincare routine bathroom mirror",
        "imageUrls": [
          "https://i.pinimg.com/originals/a1/b2/c3/a1b2c3skincare1.jpg",
          "https://i.pinimg.com/originals/d4/e5/f6/d4e5f6skincare2.jpg"
        ]
      }
    ],
    "nextSteps": "LOOK at the candidates before choosing (download and view them); then set_slide_background with the winning url. Images under 720px on the short side will be rejected."
  }
}

read_social_post

READ

REST: POST /api/v2/social/read

Read a TikTok Photo Mode or Instagram carousel post so YOU can remix it: returns the caption, slide count, and each slide image url. This is the SOURCE, not a deck. There is no server-side remix; LOOK at the images, then create_blank_slideshow and hand-build - write your own copy in the same structure and cast fresh images. Costs no credits (the deck costs 1 credit on your first build edit).

Input schema
{
  "type": "object",
  "properties": {
    "url": {
      "type": "string",
      "minLength": 1,
      "maxLength": 2000,
      "description": "A TikTok Photo Mode or Instagram post (/p/) URL to read for remixing."
    },
    "workspace": {
      "description": "Target workspace: a slug, id, or exact name from list_workspaces. Omit to use this connection's default workspace. Must be inside the credential's grant and your current access; overrides the connection-level X-Workspace-Id header.",
      "type": "string"
    }
  },
  "required": [
    "url"
  ],
  "$schema": "http://json-schema.org/draft-07/schema#"
}
Example response
{
  "success": true,
  "data": {
    "workspace": {
      "id": "6a18c36ee586c7e44e4ece67",
      "name": "Client A",
      "slug": "client-a"
    },
    "platform": "tiktok",
    "sourceUrl": "https://www.tiktok.com/@creator/photo/7301234567890123456",
    "caption": "the 5 habits that fixed my skin ✨ #skincare #glowup",
    "slideCount": 5,
    "slides": [
      {
        "position": 0,
        "imageUrl": "https://cdn.ghostfeed.ai/scraped/64f1/s0.jpg"
      },
      {
        "position": 1,
        "imageUrl": "https://cdn.ghostfeed.ai/scraped/64f1/s1.jpg"
      }
    ],
    "nextSteps": "This is the SOURCE to remix, not your deck. LOOK at the slide images and read the caption to grasp the structure and voice, then create_blank_slideshow with this slideCount and hand-build: write your OWN copy in the same structure (never copy verbatim) and cast FRESH images. The deck costs 1 credit on your first build edit."
  }
}

list_collection_images

READ

REST: GET /api/v2/collections/:collectionId/images

List a collection's actual images (urls + AI captions of what each shows), 30 per page. Use to hand-pick a specific image for set_slide_background.

Input schema
{
  "type": "object",
  "properties": {
    "collectionId": {
      "type": "string",
      "maxLength": 100,
      "description": "A collection id from list_collections."
    },
    "page": {
      "description": "Page number (default 1).",
      "type": "integer",
      "minimum": 1,
      "maximum": 9007199254740991
    },
    "limit": {
      "description": "Per page (max 100). Each tool documents its own default.",
      "type": "integer",
      "minimum": 1,
      "maximum": 100
    },
    "workspace": {
      "description": "Target workspace: a slug, id, or exact name from list_workspaces. Omit to use this connection's default workspace. Must be inside the credential's grant and your current access; overrides the connection-level X-Workspace-Id header.",
      "type": "string"
    }
  },
  "required": [
    "collectionId"
  ],
  "$schema": "http://json-schema.org/draft-07/schema#"
}
Example response
{
  "success": true,
  "data": {
    "workspace": {
      "id": "6a18c36ee586c7e44e4ece67",
      "name": "Client A",
      "slug": "client-a"
    },
    "collection": {
      "id": "64f1c2a1b2c3d4e5f6073000",
      "name": "Skincare UGC"
    },
    "images": [
      {
        "url": "https://cdn.ghostfeed.ai/collections/64f1/img1.jpg",
        "caption": "Woman applying serum in a sunlit bathroom"
      },
      {
        "url": "https://cdn.ghostfeed.ai/collections/64f1/img2.jpg"
      }
    ],
    "total": 24,
    "page": 1,
    "pages": 1
  }
}

list_avatar_images

READ

REST: GET /api/v2/avatars/:avatarId/images

List a saved avatar's generated photos (urls + captions), newest first, 30 per page. A worked avatar can hold hundreds, so this is paged: read total/pages and ask for the next page rather than expecting everything at once. The captions describe each shot, so use them to pick a specific pose or framing — that is how you cast a first frame for generate_reaction_frames via referenceImageUrl. Reusing them is free; use set_slide_background type 'avatar' to cast one.

Input schema
{
  "type": "object",
  "properties": {
    "avatarId": {
      "type": "string",
      "maxLength": 100,
      "description": "An avatarId from list_avatars."
    },
    "page": {
      "description": "Page number (default 1).",
      "type": "integer",
      "minimum": 1,
      "maximum": 9007199254740991
    },
    "limit": {
      "description": "Per page (max 100). Each tool documents its own default.",
      "type": "integer",
      "minimum": 1,
      "maximum": 100
    },
    "workspace": {
      "description": "Target workspace: a slug, id, or exact name from list_workspaces. Omit to use this connection's default workspace. Must be inside the credential's grant and your current access; overrides the connection-level X-Workspace-Id header.",
      "type": "string"
    }
  },
  "required": [
    "avatarId"
  ],
  "$schema": "http://json-schema.org/draft-07/schema#"
}
Example response
{
  "success": true,
  "data": {
    "workspace": {
      "id": "6a18c36ee586c7e44e4ece67",
      "name": "Client A",
      "slug": "client-a"
    },
    "avatar": {
      "avatarId": "avt_1a2b3c4d5e6f7081",
      "name": "Priya"
    },
    "images": [
      {
        "url": "https://cdn.ghostfeed.ai/avatars/saved/avt_1a2b3c4d5e6f7081/v1.png",
        "caption": "Close-up smiling portrait, natural light"
      },
      {
        "url": "https://cdn.ghostfeed.ai/avatars/saved/avt_1a2b3c4d5e6f7081/v2.png"
      }
    ],
    "total": 2,
    "page": 1,
    "pages": 1
  }
}

create_blank_slideshow

WRITE

REST: POST /api/v2/slideshows/blank

Create an empty slideshow with N blank slides, then build it slide by slide with set_slide_background and set_slide_texts. This is how an agent creates a deck: YOU do the creative work. The empty deck is free; the deck costs 1 credit, charged on your first set_slide_background or set_slide_texts (same price as a dashboard-made deck). Later edits are free.

Input schema
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 120,
      "description": "The slideshow name shown in the dashboard."
    },
    "slideCount": {
      "type": "integer",
      "minimum": 1,
      "maximum": 12,
      "description": "How many blank slides to start with (add/remove later as needed)."
    },
    "workspace": {
      "type": "string",
      "minLength": 1,
      "description": "Required write target: a workspace slug or id from list_workspaces. Writes never inherit the connection default."
    }
  },
  "required": [
    "name",
    "slideCount",
    "workspace"
  ],
  "$schema": "http://json-schema.org/draft-07/schema#"
}
Example response
{
  "success": true,
  "data": {
    "workspace": {
      "id": "6a18c36ee586c7e44e4ece67",
      "name": "Client A",
      "slug": "client-a"
    },
    "slideshow": {
      "id": "64f1c2a1b2c3d4e5f6072500",
      "name": "Untitled Skincare Deck",
      "dashboardUrl": "https://ghostfeed.ai/w/client-a/slideshows/64f1c2a1b2c3d4e5f6072500?ref=agent"
    },
    "total": 1,
    "page": 1,
    "pages": 1,
    "nextSteps": "Build it slide by slide: search_pinterest (or list_collection_images / list_avatar_images) for backgrounds, set_slide_background + set_slide_texts per slide, set_caption at the end. The user sees every change live in the dashboard."
  }
}

request_slide_image_upload

WRITEIDEMPOTENT

REST: POST /api/v2/slideshows/image-upload-url

Get a short-lived presigned R2 upload URL for a LOCAL image file (too large to pass as a tool argument). PUT the raw bytes to uploadUrl with the given Content-Type, then call set_slide_background with background.type "url" and background.url = the returned fileUrl. Costs no credits.

Input schema
{
  "type": "object",
  "properties": {
    "workspace": {
      "type": "string",
      "minLength": 1,
      "description": "Required write target: a workspace slug or id from list_workspaces. Writes never inherit the connection default."
    },
    "filename": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "contentType": {
      "type": "string",
      "minLength": 3,
      "maxLength": 100
    }
  },
  "required": [
    "workspace"
  ],
  "$schema": "http://json-schema.org/draft-07/schema#"
}
Example response
{
  "success": true,
  "data": {
    "workspace": {
      "id": "6a18c36ee586c7e44e4ece67",
      "name": "Client A",
      "slug": "client-a"
    },
    "upload": {
      "method": "PUT",
      "uploadUrl": "https://abc123.r2.cloudflarestorage.com/shortsbro/users/1a2b3c4d/editor/images/2026/07/20/uuid.jpg?X-Amz-Signature=...",
      "fileUrl": "https://cdn.ghostfeed.ai/users/1a2b3c4d/editor/images/2026/07/20/uuid.jpg",
      "contentType": "image/jpeg",
      "expiresInSeconds": 900
    },
    "nextSteps": "PUT the bytes to uploadUrl, then set_slide_background with type 'url' and url=fileUrl."
  }
}

set_slide_background

WRITE

REST: PUT /api/v2/slideshows/:slideshowId/slides/:slideIndex/background

Set one slide's background: a Pinterest url, a collection image, an avatar photo, any image url, or a solid color. On a Pinterest pick, pass background.query (the search_pinterest query that found it) so the editor's Pinterest tab reopens on that search. For a LOCAL image file, call request_slide_image_upload first, PUT the bytes, then pass the returned fileUrl here as type 'url'. External urls are re-hosted to durable storage automatically. Image picks are gated server-side (browser-displayable, 720px+ short side). The dashboard updates live. If this is the deck's first build edit it charges 1 credit and returns creditsSpent/creditsRemaining.

Input schema
{
  "type": "object",
  "properties": {
    "slideshowId": {
      "type": "string",
      "maxLength": 100,
      "description": "The slideshow (project) id."
    },
    "variantIndex": {
      "description": "Which variant to edit (1 = the base deck; higher indexes come from create_variant). Omit for the base.",
      "type": "integer",
      "minimum": 1,
      "maximum": 10
    },
    "slideIndex": {
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991,
      "description": "The slide position, 0-based."
    },
    "background": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "pinterest",
            "collection",
            "avatar",
            "url",
            "color"
          ],
          "description": "Where this background comes from. 'pinterest' = a url from search_pinterest; 'collection'/'avatar' = a url from the matching list tool (id required); 'url' = any other image url, INCLUDING a fileUrl from request_slide_image_upload (an external url is re-hosted to durable storage automatically); 'color' = solid background. To use a LOCAL image file, call request_slide_image_upload first, PUT the bytes, then set type 'url' with the returned fileUrl."
        },
        "url": {
          "description": "The image url (types 'pinterest'/'collection'/'avatar'/'url').",
          "type": "string",
          "maxLength": 2000
        },
        "color": {
          "description": "Hex color like #101010 (type 'color' only).",
          "type": "string",
          "pattern": "^#[0-9a-fA-F]{6}$"
        },
        "collectionId": {
          "description": "Required when type is 'collection'.",
          "type": "string",
          "maxLength": 100
        },
        "avatarId": {
          "description": "Required when type is 'avatar'.",
          "type": "string",
          "maxLength": 100
        },
        "query": {
          "description": "The search_pinterest query that found this image (type 'pinterest'). Pass it: it is what the editor's Pinterest tab reopens with and what shuffle re-runs. Omit it and the user gets an empty search box on that slide.",
          "type": "string",
          "maxLength": 200
        },
        "candidateUrls": {
          "description": "The other image urls that same search returned (type 'pinterest'), so the editor reopens with the full candidate set instead of re-searching.",
          "maxItems": 50,
          "type": "array",
          "items": {
            "type": "string",
            "maxLength": 2000
          }
        }
      },
      "required": [
        "type"
      ],
      "description": "The background to set."
    },
    "transform": {
      "description": "Optional reposition/zoom of the background within the canvas.",
      "type": "object",
      "properties": {
        "x": {
          "type": "number"
        },
        "y": {
          "type": "number"
        },
        "scale": {
          "type": "number",
          "minimum": 0.1,
          "maximum": 10
        }
      },
      "required": [
        "x",
        "y",
        "scale"
      ]
    },
    "workspace": {
      "type": "string",
      "minLength": 1,
      "description": "Required write target: a workspace slug or id from list_workspaces. Writes never inherit the connection default."
    }
  },
  "required": [
    "slideshowId",
    "slideIndex",
    "background",
    "workspace"
  ],
  "$schema": "http://json-schema.org/draft-07/schema#"
}
Example response
{
  "success": true,
  "data": {
    "workspace": {
      "id": "6a18c36ee586c7e44e4ece67",
      "name": "Client A",
      "slug": "client-a"
    },
    "slide": {
      "position": 0,
      "backgroundSource": "pinterest"
    },
    "creditsSpent": 1,
    "creditsRemaining": 46
  }
}

set_slide_texts

WRITE

REST: PUT /api/v2/slideshows/:slideshowId/slides/:slideIndex/texts

Replace one slide's text boxes (1-5 texts with placement top/center/bottom and role heading/subheading/body/cta - role sets the font size, positions are computed server-side exactly like the dashboard). Optionally mark isProductSlide. If this is the deck's first build edit it charges 1 credit and returns creditsSpent/creditsRemaining.

Input schema
{
  "type": "object",
  "properties": {
    "slideshowId": {
      "type": "string",
      "maxLength": 100,
      "description": "The slideshow (project) id."
    },
    "variantIndex": {
      "description": "Which variant to edit (1 = the base deck; higher indexes come from create_variant). Omit for the base.",
      "type": "integer",
      "minimum": 1,
      "maximum": 10
    },
    "slideIndex": {
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991,
      "description": "The slide position, 0-based."
    },
    "texts": {
      "minItems": 1,
      "maxItems": 5,
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "text": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500
          },
          "placement": {
            "type": "string",
            "enum": [
              "top",
              "center",
              "bottom"
            ],
            "description": "Vertical zone on the slide."
          },
          "role": {
            "type": "string",
            "enum": [
              "heading",
              "subheading",
              "body",
              "cta"
            ],
            "description": "Typographic job; sets the font size (heading largest)."
          }
        },
        "required": [
          "text",
          "placement",
          "role"
        ]
      },
      "description": "Replaces ALL text boxes on the slide; positions are computed server-side."
    },
    "isProductSlide": {
      "description": "Mark/unmark this slide as the product slide (UI badge).",
      "type": "boolean"
    },
    "workspace": {
      "type": "string",
      "minLength": 1,
      "description": "Required write target: a workspace slug or id from list_workspaces. Writes never inherit the connection default."
    }
  },
  "required": [
    "slideshowId",
    "slideIndex",
    "texts",
    "workspace"
  ],
  "$schema": "http://json-schema.org/draft-07/schema#"
}
Example response
{
  "success": true,
  "data": {
    "workspace": {
      "id": "6a18c36ee586c7e44e4ece67",
      "name": "Client A",
      "slug": "client-a"
    },
    "slide": {
      "position": 0,
      "texts": [
        "5 morning habits",
        "that changed my skin"
      ]
    }
  }
}

add_slide

WRITE

REST: POST /api/v2/slideshows/:slideshowId/slides

Insert a blank slide (append by default). Decks cap at 12 slides.

Input schema
{
  "type": "object",
  "properties": {
    "slideshowId": {
      "type": "string",
      "maxLength": 100,
      "description": "The slideshow (project) id."
    },
    "variantIndex": {
      "description": "Which variant to edit (1 = the base deck; higher indexes come from create_variant). Omit for the base.",
      "type": "integer",
      "minimum": 1,
      "maximum": 10
    },
    "position": {
      "description": "Insert position (0-based); omit to append at the end.",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    },
    "workspace": {
      "type": "string",
      "minLength": 1,
      "description": "Required write target: a workspace slug or id from list_workspaces. Writes never inherit the connection default."
    }
  },
  "required": [
    "slideshowId",
    "workspace"
  ],
  "$schema": "http://json-schema.org/draft-07/schema#"
}
Example response
{
  "success": true,
  "data": {
    "workspace": {
      "id": "6a18c36ee586c7e44e4ece67",
      "name": "Client A",
      "slug": "client-a"
    },
    "slideCount": 7,
    "position": 3
  }
}

remove_slide

WRITE

REST: DELETE /api/v2/slideshows/:slideshowId/slides/:slideIndex

Remove a slide by position. A deck keeps at least one slide.

Input schema
{
  "type": "object",
  "properties": {
    "slideshowId": {
      "type": "string",
      "maxLength": 100,
      "description": "The slideshow (project) id."
    },
    "variantIndex": {
      "description": "Which variant to edit (1 = the base deck; higher indexes come from create_variant). Omit for the base.",
      "type": "integer",
      "minimum": 1,
      "maximum": 10
    },
    "slideIndex": {
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991,
      "description": "The slide position to remove, 0-based."
    },
    "workspace": {
      "type": "string",
      "minLength": 1,
      "description": "Required write target: a workspace slug or id from list_workspaces. Writes never inherit the connection default."
    }
  },
  "required": [
    "slideshowId",
    "slideIndex",
    "workspace"
  ],
  "$schema": "http://json-schema.org/draft-07/schema#"
}
Example response
{
  "success": true,
  "data": {
    "workspace": {
      "id": "6a18c36ee586c7e44e4ece67",
      "name": "Client A",
      "slug": "client-a"
    },
    "slideCount": 6
  }
}

reorder_slides

WRITE

REST: PUT /api/v2/slideshows/:slideshowId/slides/order

Reorder slides with a permutation of current positions, e.g. [2,0,1].

Input schema
{
  "type": "object",
  "properties": {
    "slideshowId": {
      "type": "string",
      "maxLength": 100,
      "description": "The slideshow (project) id."
    },
    "variantIndex": {
      "description": "Which variant to edit (1 = the base deck; higher indexes come from create_variant). Omit for the base.",
      "type": "integer",
      "minimum": 1,
      "maximum": 10
    },
    "order": {
      "minItems": 1,
      "maxItems": 12,
      "type": "array",
      "items": {
        "type": "integer",
        "minimum": 0,
        "maximum": 9007199254740991
      },
      "description": "The new order as a permutation of current positions, e.g. [2,0,1] makes the current slide 2 first."
    },
    "workspace": {
      "type": "string",
      "minLength": 1,
      "description": "Required write target: a workspace slug or id from list_workspaces. Writes never inherit the connection default."
    }
  },
  "required": [
    "slideshowId",
    "order",
    "workspace"
  ],
  "$schema": "http://json-schema.org/draft-07/schema#"
}
Example response
{
  "success": true,
  "data": {
    "workspace": {
      "id": "6a18c36ee586c7e44e4ece67",
      "name": "Client A",
      "slug": "client-a"
    },
    "slideCount": 6
  }
}

set_caption

WRITE

REST: PUT /api/v2/slideshows/:slideshowId/caption

Set the slideshow's post caption.

Input schema
{
  "type": "object",
  "properties": {
    "slideshowId": {
      "type": "string",
      "maxLength": 100,
      "description": "The slideshow (project) id."
    },
    "variantIndex": {
      "description": "Which variant to edit (1 = the base deck; higher indexes come from create_variant). Omit for the base.",
      "type": "integer",
      "minimum": 1,
      "maximum": 10
    },
    "caption": {
      "type": "string",
      "maxLength": 2200,
      "description": "The post caption (empty string clears it)."
    },
    "workspace": {
      "type": "string",
      "minLength": 1,
      "description": "Required write target: a workspace slug or id from list_workspaces. Writes never inherit the connection default."
    }
  },
  "required": [
    "slideshowId",
    "caption",
    "workspace"
  ],
  "$schema": "http://json-schema.org/draft-07/schema#"
}
Example response
{
  "success": true,
  "data": {
    "workspace": {
      "id": "6a18c36ee586c7e44e4ece67",
      "name": "Client A",
      "slug": "client-a"
    },
    "caption": "the 5 habits that finally fixed my skin ✨ #skincare"
  }
}

create_variant

WRITE

REST: POST /api/v2/slideshows/:slideshowId/variants

Add a variant to a slideshow: a COPY of the base deck, returned with a new variantIndex. Differentiate it the same way the dashboard shuffles variants, by passing that variantIndex to set_slide_background / set_slide_texts per slide: re-cast a FRESH image on each slide (search_pinterest + a new pick for Pinterest slides; a different image from the same collection/avatar for those) and REWRITE the copy. Keep uploaded, plain-url, and AI-generated images as-is. Costs 1 credit (same as a dashboard-generated variant); returns creditsSpent/creditsRemaining. Editing the variant afterwards is free.

Input schema
{
  "type": "object",
  "properties": {
    "slideshowId": {
      "type": "string",
      "maxLength": 100,
      "description": "The slideshow (project) id to add a variant to."
    },
    "workspace": {
      "type": "string",
      "minLength": 1,
      "description": "Required write target: a workspace slug or id from list_workspaces. Writes never inherit the connection default."
    }
  },
  "required": [
    "slideshowId",
    "workspace"
  ],
  "$schema": "http://json-schema.org/draft-07/schema#"
}
Example response
{
  "success": true,
  "data": {
    "workspace": {
      "id": "6a18c36ee586c7e44e4ece67",
      "name": "Client A",
      "slug": "client-a"
    },
    "slideshowId": "64f1c2a1b2c3d4e5f6072000",
    "variantIndex": 2,
    "creditsSpent": 1,
    "creditsRemaining": 45
  }
}