Skip to main content
Alongside the tools that mirror the Fanvue API, the MCP server includes custom tools, each purpose-built around a complete job and easy to spot by its custom__ prefix. This page is the reference for every custom tool: the arguments it accepts and the fields it returns. Each tool returns a single JSON object as the text content of its result.
The custom toolset will grow over time as more jobs get their own purpose-built tool.

The image post flow

Publishing an image post takes three steps, two tool calls with an image upload in between:
1

Start the upload

Call custom__start-image-upload. It returns a mediaUuid, an uploadId and a short-lived uploadUrl for one image.
2

Upload the image

HTTP PUT the raw image bytes (not base64) to uploadUrl, with no Authorization header. Keep the ETag response header, it confirms the upload in the next step.
3

Create the post

Call custom__create-image-post with image: { mediaUuid, uploadId, etag } and the post fields. The post is created once the image is ready to display.
Run the flow once per image: a paid post with its own teaser image needs two uploads, one for the locked image, one for the free preview.

custom__start-image-upload

Starts an image post (step 1 of 3) by reserving an upload slot for one image and returning everything the upload step needs. Takes no arguments. Requires the write:media scope.

Result

string
required
UUID of the new media item. Pass it to custom__create-image-post, and use it to reference the media in your library afterwards.
string
required
Opaque identifier of this upload. Pass it to custom__create-image-post exactly as received, don’t parse or modify it.
string
required
Short-lived URL to PUT the raw image bytes to. If it expires before the upload happens, call custom__start-image-upload again for a fresh one, an unused slot is harmless.
string
required
A reminder of the remaining steps, embedded in the result so an assistant can complete the flow without consulting this page.
Example result

custom__create-image-post

Publishes a post carrying the uploaded image (step 3 of 3). It accepts the same post fields as create a new post, text, audience, pricing, scheduling, expiry, collections, plus the identifiers of the uploaded image. The post is only created once the image has finished processing and is ready to display, so a post never reaches the feed with broken media. Requires the write:media, read:media and write:post scopes.

Arguments

object
required
The uploaded image to attach to the post.
string
required
Who can view the post: subscribers or followers-and-subscribers.
string
Text content of the post, up to 5,000 characters.
number
Price in cents for a pay-to-view post, minimum 300 ($3.00). Omit for a free post.
string
Future date/time to publish the post, in ISO 8601 format with a timezone (for example 2026-07-01T18:00:00Z). When set, the post is scheduled instead of published immediately.
string
Date/time when the post expires, in ISO 8601 format with a timezone.
object
Free teaser image shown to non-subscribers before they unlock a paid post. Upload it through its own custom__start-image-upload call; the fields are the same as image.
string[]
UUIDs of the collections to add the post to.

Result

The created post, the same shape as the create a new post response.
string
required
Unique identifier of the created post.
string
required
Date/time when the post was created (ISO 8601 format).
string | null
required
Text content of the post.
number | null
required
Price in cents for paid posts.
string | null
required
UUID of the free preview media, when a previewImage was supplied.
string
required
Audience the post is visible to.
string | null
required
Future date/time when the post will be published, for scheduled posts.
string | null
required
Date/time when the post was published. null for scheduled posts that have not gone live yet.
string | null
required
Date/time when the post expires.
Example result

If a call fails

  • A failed call never publishes a broken post: if the image could not be processed, no post is created and the error names the media that failed.
  • An image that was already uploaded is not lost, the error lists the media UUIDs that remain in your media library, ready to attach to a post.
  • The call is safe to retry with the same arguments; a retry picks up where the failed call left off rather than uploading or posting twice.
  • If the error reports a rejected upload, check that the bytes were PUT to the uploadUrl and that etag is the ETag header from that PUT. An expired uploadUrl means starting over with custom__start-image-upload.