> **Building with AI coding agents?** Install the authstack plugin with one command. This equips your agent with accurate Scalekit implementation patterns.
>
> **Recommended** (the `-y` flags skip prompts, so the command runs without an interactive terminal):
> ```bash
> npx -y @scalekit-inc/cli setup -y
> ```
>
> Global:
> ```bash
> npm install -g @scalekit-inc/cli
> scalekit setup -y
> ```
>
> Supports Claude Code, Cursor, GitHub Copilot, Codex + skills for other Agent Skills-compatible agents in `~/.agents/skills`.
> Skills: integrate-agentkit, implement-saaskit, add-mcp-oauth, implement-sso, implement-scim.
> [Full setup guide](https://docs.scalekit.com/dev-kit/build-with-ai/)

---

# Connect AI agents to the Flux MCP server

The Flux MCP connector routes your AI agent's tool calls to Flux's own MCP server through Scalekit. Each user signs in to Flux once, and Scalekit stores and refreshes their tokens, so your agent never handles credentials. It comes with 10 tools.

**Authentication:** OAuth 2.1/DCR
**Categories:** AI
**Tools:** 10: 3 read, 7 write, 0 destructive
**Users sign in with:** OAuth
**OAuth app:** Scalekit's or your own
**Built by:** Vendor MCP
**Try it:** [Playground in the Scalekit dashboard](https://app.scalekit.com/ws/signup?sk_intent=playground&provider=FLUXMCP)

## Setup

1. ### Install the SDK

   **Node.js**

   ```bash
   npm install @scalekit-sdk/node dotenv
   ```

   **Python**

   ```bash
   pip install scalekit-sdk-python python-dotenv
   ```

2. ### Set your credentials

   Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**.

   ```sh title=".env"
   SCALEKIT_ENVIRONMENT_URL=<your-environment-url>
   SCALEKIT_CLIENT_ID=<your-client-id>
   SCALEKIT_CLIENT_SECRET=<your-client-secret>
   ```

3. ### Create the Flux MCP connection

   In **AgentKit > Connections**, create a Flux MCP connection. The name you give it is the `connection_name` your code passes. See [Configure connections](/agentkit/connections/).

   Scalekit credentials are available for Flux MCP server, so you don't need to register an OAuth app.

4. ### Authorize a user and make your first call

   **Node.js** (`quickstart.mts`)

   ```typescript
   import { ScalekitClient } from '@scalekit-sdk/node'
   import 'dotenv/config'
   import { createInterface } from 'node:readline/promises'

   const scalekit = new ScalekitClient(
     process.env.SCALEKIT_ENVIRONMENT_URL,
     process.env.SCALEKIT_CLIENT_ID,
     process.env.SCALEKIT_CLIENT_SECRET,
   )
   const actions = scalekit.actions

   const connector = 'fluxmcp'
   const identifier = 'user_123'

   // Generate an authorization link for the user
   const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier })
   console.log('Authorize Flux MCP:', link)
   const rl = createInterface({ input: process.stdin, output: process.stdout })
   await rl.question('Press Enter after authorizing...')
   rl.close()

   // Make your first call
   const result = await actions.executeTool({
     connector,
     identifier,
     toolName: 'fluxmcp_get_credits',
     toolInput: {},
   })
   console.log(result)
   ```

   ```bash
   npx tsx quickstart.mts
   ```

   **Python** (`quickstart.py`)

   ```python
   import os
   from scalekit import ScalekitClient
   from dotenv import load_dotenv
   load_dotenv()

   scalekit_client = ScalekitClient(
       env_url=os.getenv("SCALEKIT_ENVIRONMENT_URL"),
       client_id=os.getenv("SCALEKIT_CLIENT_ID"),
       client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"),
   )
   actions = scalekit_client.actions

   connection_name = "fluxmcp"
   identifier = "user_123"

   # Generate an authorization link for the user
   link_response = actions.get_authorization_link(
       connection_name=connection_name,
       identifier=identifier,
   )
   print("Authorize Flux MCP:", link_response.link)
   input("Press Enter after authorizing...")

   # Make your first call
   result = actions.execute_tool(
       tool_input={},
       tool_name="fluxmcp_get_credits",
       connection_name=connection_name,
       identifier=identifier,
   )
   print(result)
   ```

   ```bash
   python quickstart.py
   ```

   Each user signs in once. See [Authorize a user](/agentkit/tools/authorize/) for the full flow and statuses.

## Tools

Pass the exact name to `execute_tool`, with an input like each tool's example. To ask for a tool that's missing, use the [request form](https://scalekitsupport.portal.usepylon.com/forms/request-a-connector-tool).

**Node.js**

```typescript
const result = await actions.executeTool({
  toolName: 'fluxmcp_get_credits',
  toolInput: {},
  connector: 'fluxmcp',
  identifier: 'user_123',
})
```

**Python**

```python
result = actions.execute_tool(
    tool_name="fluxmcp_get_credits",
    tool_input={},
    connection_name="fluxmcp",
    identifier="user_123",
)
```

`result.data` is the app's response as JSON, and `result.execution_id` (`executionId` in Node.js) is the ID of the call.

### `fluxmcp_get_credits`

GetCredits · Read-only

Check the user's remaining BFL API credits AND welcome-bonus
free-generation balance.

The free pool is a one-time grant of N generations issued when
the user first connects MCP (counted in generations, not dollars
— every model decrements 1 from the pool regardless of cost).
Free generations are consumed automatically before paid credits;
when the free pool hits zero, gens charge the user's paid
balance.

Returns `{credits, free_generations, free_generations_total_granted}`
on success. `free_generations` may be 0 — surface that explicitly to
the user so they know the bonus is exhausted (it's a one-time
grant, not a refill).

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs: none.

Example input: `{}`

### `fluxmcp_get_history`

GetHistory · Read-only

List the user's recent FLUX generations as a grid of thumbnails.

Each item carries the original prompt, model, seed, dimensions, plus `image_url` (full-res, 24h signed). The viewer offers per-tile Variations (regenerate via `generate_variations`) and Use (use the image as an `input_medias` entry on a follow-up `generate_image`), plus copy / download.

Pagination is keyset on `created_at`. Pass `cursor=<next_cursor>` to page back in time; combine with `before` / `after` for date ranges.

Returns the standard `{data, message}` shape. Errors: UNAUTHENTICATED, STORAGE_NOT_CONFIGURED, HISTORY_LIST_FAILED.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `after` (`string`, optional): Optional date filter: only return items strictly newer than this ISO-8601 timestamp.
- `before` (`string`, optional): Optional date filter: only return items strictly older than this ISO-8601 timestamp. Combine with `cursor` for keyset pagination over a fixed range.
- `cursor` (`string`, optional): Pagination cursor. Pass `data.next_cursor` from a previous response to fetch the next (older) page. ISO-8601 timestamp; omit for the first page.
- `limit` (`integer`, optional): DO NOT pass this argument. The viewer paginates with a Load more button — extra rows aren't useful in a single response. Omit so the server's default of 8 is used. Only override if the user literally said a number. Hard cap 16. Default: `8`.
- `status` (`string`, optional): Filter by generation lifecycle status. Defaults to `ready`: completed generations only. Use `all` to include in-flight and failed rows. One of: `ready`, `pending`, `moderated_input`, `moderated_output`, `failed`, `all`. Default: `ready`.

Example input: `{}`

### `fluxmcp_get_result`

GetResult · Read-only

DO NOT CALL FROM THE LLM. The image-viewer iframe handles all result polling automatically.

Internal tool: the iframe invokes this per pending item via `app.callServerTool` after `generate_image` returns with items in `pending` status. Each call inline-polls BFL up to `INLINE_POLL_TIMEOUT_SECONDS`; if the request is still running when the budget expires, the response is `{status: "pending", request_id, ...}` and the iframe just calls again.

Returns the same shape as a single-image `generate_image` result: `{data: {status, image_url, image_path, seed, cost, remaining_credits, ...}, message}` on ready, or `{error: {code, ...}, message}` on terminal failure.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `request_id` (`string`, required): The `request_id` returned by a previous `generate_image` call. The image-viewer iframe calls this tool per item to poll BFL until that item reaches a terminal state.

Example input: `{"request_id":"<request_id>"}`

### `fluxmcp_enhance_video`

Enhance Video · Write

Render a prior DRAFT video at full quality. The draft's cached
generation plan is replayed with more denoising steps — same
composition, seed, and prompt plan, sharper detail — on an
`fhd` (1080p-class) canvas by default, or `hd` via
`resolution`. Everything else is pinned by the draft.

Only works on a draft (`draft: true`) generation that has
reached `status: "ready"`. Use when the user picks a draft they
like ("make this one full quality", "finalize it", "enhance
it"). To change the CONTENT instead, submit a new
`generate_video` call.

Returns immediately with a pending item, exactly like
`generate_video`; the full-quality render takes minutes and the
widget (or `get_result`) resolves it.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `request_id` (`string`, required): The `request_id` of a READY draft generation (submitted with `draft: true` on `generate_video`).
- `resolution` (`string`, optional): Output canvas for the enhanced render. Defaults to `fhd` (1080p-class, upsampled) when omitted; pass `hd` only when the user wants to keep the draft's 704p-class canvas.

Example input: `{"request_id":"<request_id>"}`

### `fluxmcp_generate_image`

GenerateImage · Write

Submit one or more FLUX.2 image generations. Returns immediately after BFL accepts each submit; the iframe streams the actual images in as they finish.

Reference images go in `input_medias: InputMedia[]`. Two shapes:
  • `{id: <string>}` — for content already in our bucket.
  • `{url: <https URL>}` — ONLY for URLs the user typed from the open web.

For user-attached files, call `request_upload_url` FIRST and pass `{id: data.media_id}` from the tool's response.

Each `requests` entry is one image. Pass a single-item list for one image, or up to 8 entries to produce a batch in parallel.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `requests` (`array`, required): List of image generation requests. Pass a single-item list for one image; pass multiple to generate a batch in one call (each entry is submitted to BFL in parallel and the iframe renders the results as a clickable grid). Cap is 8 entries per call.

Example input: `{"requests":[]}`

### `fluxmcp_generate_variations`

GenerateVariations · Write

Generate N more images "in the same direction" as a previously completed generation. Use this tool whenever the user asks for variations of an existing generation — "more like that one", "give me variations", "another version", "show me alternatives", and similar.

Reads the original request from the DB (prompt, model, dimensions, safety, Flex knobs, AND input_image slots), and for models that support it sets `prompt_upsampling=True` so each call rephrases the prompt diversely before generation — combined with BFL's auto-picked seeds this produces compositionally distinct outputs in the same direction.

Pass ONLY the `request_id` (and optionally `count`) — every other field is sourced from the DB row.

Returns the same shape as `generate_image`: `{data: {items: [{status: "pending", request_id, ...}, ...]}, message}`.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `request_id` (`string`, required): The `request_id` of an existing ready generation to vary. The server reads its prompt, model, dimensions, safety_tolerance, Flex knobs, and any input_image slots and submits `count` fresh-seed clones in parallel.
- `count` (`integer`, optional): Number of variations to generate. Defaults to 4. Pass a different value only if the user specifically asked for it. Hard cap 8. Default: `4`.

Example input: `{"request_id":"<request_id>"}`

### `fluxmcp_generate_video`

Generate Video · Write

Generate videos with FLUX.3. Each `requests` entry picks an
explicit `mode`:

  * `t2v` — text-to-video, prompt only.
  * `i2v` — image-to-video via `keyframes`: 1 keyframe animates
    a still (it becomes the opening frame); 2 keyframes
    transition/morph from the first to the second; 3-10 form a
    storyboard spread across the clip (needs explicit
    `duration`). Per-keyframe `timestamp` (seconds, all-or-none)
    pins exact positions.
  * `v2v` — video continuation via `start_video`: the output
    picks up from the source clip's final seconds (video AND
    audio). Chain results for longer sequences.

When the user PROVIDES media, it MUST go into `keyframes` or
`start_video` — never describe provided images/videos in prose
and call with `t2v` (the output would look unrelated to the
user's media). Media uses the same `{id}` / `{url}` shapes as
`generate_image`; upload user-attached files via
`request_upload_url` first (`kind: "video"` for MP4s).

Reference-based conditioning (keep a subject's identity from
reference images / borrow motion from a reference video) is NOT
available on this endpoint yet — tell the user so instead of
faking it with t2v.

Clips are 5-20s at 24fps, `hd` or `fhd`, with synchronized
audio by default. The harness reasons about the prompt before
rendering and picks `duration` / `aspect_ratio` itself when
they're `auto`.

DRAFT WORKFLOW: pass `draft: true` for a fast, cheap preview of
the exact planned generation. When the user likes a draft, call
`enhance_video(request_id=<draft's id>)` to re-render the SAME
clip (same composition, seed, and prompt plan) at full quality.
Prefer drafts when the user is exploring ideas or wants
variants — submit a batch of drafts, let them pick, enhance the
winner. Drafts render at hd; `enhance_video` finishes the
winner at `fhd` (default) or `hd`.

VIDEO TAKES TIME: minutes for short clips, up to ~an hour for
long ones. The response returns IMMEDIATELY with pending items.
In hosts with the video-viewer widget the widget polls and
renders automatically — do not poll yourself; tell the user the
video is rendering. In text-only hosts, call
`get_result(request_id=...)` per item to check progress (each
call waits up to ~45s and returns `pending` again with a
`progress` fraction until done). Results survive chat closes —
the same `request_id` resumes from the server even days later.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `requests` (`array`, required): List of video generation requests. Pass a single-item list for one video; pass up to 4 to generate a batch in one call (each entry is submitted to BFL in parallel). Each entry carries its own mode / prompt / media, so a batch can mix variations of one idea or entirely different clips.

Example input: `{"requests":[]}`

### `fluxmcp_refresh_image_url`

RefreshImageUrl · Write

Mint a fresh 24h signed URL for an image already stored in this server's bucket. Internal: used by the iframe viewers to recover from expired URLs in older chats.

You normally do not need to call this from the LLM. Every fresh `generate_image` / `get_history` response includes a usable URL already. Call this only when the user explicitly asks for a refreshed link to an image they generated previously.

Returns `{data: {url, expires_in_seconds}, message}` on success, or an `error` payload (UNAUTHENTICATED, STORAGE_NOT_CONFIGURED, INVALID_IMAGE_PATH, REFRESH_FAILED).

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `image_path` (`string`, required): Storage path of the image to renew, e.g. `<user_id>/generations/<request_id>.jpg`. Returned as `data.image_path` by `generate_image`, `get_result`, and `get_history`. Only paths owned by the calling user are accepted.

Example input: `{"image_path":"<user_id>/generations/<request_id>.jpg"}`

### `fluxmcp_request_upload_url`

RequestUploadUrl · Write

Issue a signed PUT URL for a direct image upload to BFL's Storage bucket.

Use this ONLY when the user has attached a file in the chat and the image has no URL of its own. If the user already provided a public image URL, pass it as `{url: <that URL>}` inside `input_medias` on `generate_image` — do NOT route it through this upload step.

Flow:
  1. Call `request_upload_url()`. The response contains `data.upload_url` AND `data.media_id`.
  2. From your sandbox, PUT the raw bytes to `data.upload_url` with the appropriate `Content-Type`.
  3. Call `generate_image` with `input_medias: [{id: data.media_id}]`.

Allowed types: PNG, JPEG, WebP (static). Max size: 10 MB. Per-user cap: 20 image uploads (LRU eviction).

Returns the standard `{data, message}` / `{error, message}` shape.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs: none.

Example input: `{}`

### `fluxmcp_vto`

VirtualTryOn · Write

Virtual try-on: dress `person` in `garment`. Preserves the subject's face, hair, and pose; only the worn item changes.

Use this tool — not `generate_image` — whenever the user wants to see a subject wearing a specific item from a reference image. Covers ALL wearable items: garments, hats, sunglasses, shoes, bags, jewelry, and accessories.

Prompt formula: "The person of image 1, maintaining exactly their face and pose, wearing the {GARMENT DESCRIPTION} of image 2."

Sourcing inputs (same {id}/{url} rules as generate_image):
- Person: upload via request_upload_url and pass {id: data.media_id}.
- Garment: if the user named a product without attaching an image, web-search for an official product image and pass its public URL as {url: <URL>}.

Response shape matches generate_image: returns immediately with {data: {items: [{status: "pending", request_id, ...}]}, message}.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `garment` (`string`, required): The item to try on — garment, hat, sunglasses, shoes, bag, or other wearable. Same {id}/{url} shape as person. Image 2 in the prompt template. For multi-piece outfits, merge into a single canvas first.
- `person` (`string`, required): The subject to dress — usually a person, but a pet or mascot works too. Use {id: <media_id>} for uploads via request_upload_url or prior generation request_ids. Use {url: <https URL>} for public web images only. Keep at or below 1 MP for best results.
- `prompt` (`string`, required): Styling instruction using the BFL formula: "The person of image 1, maintaining exactly their face and pose, wearing the {GARMENT DESCRIPTION} of image 2." Describe only the garment (category, fit, color). Do NOT describe what to preserve from the person.
- `height` (`string`, optional): Output canvas height in pixels. Same caps as width. Omit to let BFL default.
- `seed` (`string`, optional): Optional seed for reproducibility. Same seed + same inputs → same result.
- `steps` (`string`, optional): Number of denoising steps, 1–4. BFL defaults to 4 (highest quality). Lower values trade quality for speed.
- `width` (`string`, optional): Output canvas width in pixels. Omit to let BFL default. width * height must stay under 2 MP.

Example input: `{"garment":"<garment>","person":"<person>","prompt":"<prompt>"}`

## Related

Other AI connectors ([all 116](/agentkit/connectors/?category=ai)):

- [Chorus](/agentkit/connectors/chorus/): Scalekit connector, Username and password, 9 tools
- [Gong](/agentkit/connectors/gong/): Scalekit connector, OAuth, 49 tools
- [Jiminny](/agentkit/connectors/jiminny/): Scalekit connector, Access token, 27 tools


---

## More Scalekit documentation

| Resource | What it contains | When to use it |
|----------|-----------------|----------------|
| [/llms.txt](/llms.txt) | Structured index with routing hints per product area | Start here — find which documentation set covers your topic before loading full content |
| [/llms-full.txt](/llms-full.txt) | Complete documentation for all Scalekit products in one file | Use when you need exhaustive context across multiple products or when the topic spans several areas |
| [sitemap-0.xml](https://docs.scalekit.com/sitemap-0.xml) | Full URL list of every documentation page | Use to discover specific page URLs you can fetch for targeted, page-level answers |
