Skip to main content
Custom tools on the Fanvue MCP server carry a custom__ prefix and sit alongside the tools that mirror the Fanvue API. Each one wraps a complete job and returns a single JSON object as the text content of its result. A connected client calls these tools for you when you ask it for an image post. The arguments and results matter when you build or debug a client that calls them directly.

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; the next step uses it to confirm the upload.
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.
Repeat the first two steps for each image. A paid post with its own teaser image needs two uploads, one for the locked image and 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, without parsing or modifying 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 the client can complete the flow without any other reference.
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 and collections) plus the identifiers of the uploaded image. The post is created only 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 USD 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.

Limits

  • uploadId must belong to mediaUuid. A pair mixed across two uploads is rejected before any upload completes, with an error naming the mismatched uploadId and mediaUuid.
  • The readiness wait is a short ladder of checks spanning about 10 seconds. Media still processing at the end of the ladder fails the call with uploaded media was still processing after the wait limit, so the post was not created.

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 USD 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, named so they can be attached to a post on the next attempt.
  • 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.

See also