# Directory Structure
```
├── .gitignore
├── Dockerfile
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── smithery.yaml
├── src
│ ├── config
│ │ └── index.ts
│ ├── index.ts
│ ├── resources
│ │ ├── imageList.ts
│ │ ├── index.ts
│ │ ├── predictionList.ts
│ │ └── svgList.ts
│ ├── server
│ │ └── index.ts
│ ├── services
│ │ └── replicate.ts
│ ├── tools
│ │ ├── createPrediction.ts
│ │ ├── generateImage.ts
│ │ ├── generateImageVariants.ts
│ │ ├── generateMultipleImages.ts
│ │ ├── generateSVG.ts
│ │ ├── getPrediction.ts
│ │ ├── index.ts
│ │ └── predictionList.ts
│ ├── types
│ │ └── index.ts
│ └── utils
│ ├── error.ts
│ └── image.ts
└── tsconfig.json
```
# Files
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
1 | /node_modules
2 | /build
3 | /dist
4 | /coverage
5 | /logs
6 | /tmp
7 | .env
8 | .cursor
9 | .cursorignore
10 | .cursorrules
11 | .cursorconfig
12 | .cursorignorerules
13 | .cursorignoreconfig
14 | .npmrc
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
1 | [](https://mseep.ai/app/awkoy-replicate-flux-mcp)
2 |
3 | # Replicate Flux MCP
4 |
5 | 
6 | 
7 | 
8 | 
9 | [](https://smithery.ai/server/@awkoy/replicate-flux-mcp)
10 | 
11 | 
12 |
13 | <a href="https://glama.ai/mcp/servers/ss8n1knen8">
14 | <img width="380" height="200" src="https://glama.ai/mcp/servers/ss8n1knen8/badge" />
15 | </a>
16 |
17 | **Replicate Flux MCP** is an advanced Model Context Protocol (MCP) server that empowers AI assistants to generate high-quality images and vector graphics. Leveraging [Black Forest Labs' Flux Schnell model](https://replicate.com/black-forest-labs/flux-schnell) for raster images and [Recraft's V3 SVG model](https://replicate.com/recraft-ai/recraft-v3-svg) for vector graphics via the Replicate API.
18 |
19 | ## 📑 Table of Contents
20 |
21 | - [Getting Started & Integration](#-getting-started--integration)
22 | - [Setup Process](#setup-process)
23 | - [Cursor Integration](#cursor-integration)
24 | - [Claude Desktop Integration](#claude-desktop-integration)
25 | - [Smithery Integration](#smithery-integration)
26 | - [Glama.ai Integration](#glamaai-integration)
27 | - [Features](#-features)
28 | - [Documentation](#-documentation)
29 | - [Available Tools](#available-tools)
30 | - [Available Resources](#available-resources)
31 | - [Development](#-development)
32 | - [Technical Details](#-technical-details)
33 | - [Troubleshooting](#-troubleshooting)
34 | - [Contributing](#-contributing)
35 | - [License](#-license)
36 | - [Resources](#-resources)
37 | - [Examples](#-examples)
38 |
39 | ## 🚀 Getting Started & Integration
40 |
41 | ### Setup Process
42 |
43 | 1. **Obtain a Replicate API Token**
44 | - Sign up at [Replicate](https://replicate.com/)
45 | - Create an API token in your account settings
46 |
47 | 2. **Choose Your Integration Method**
48 | - Follow one of the integration options below based on your preferred MCP client
49 |
50 | 3. **Ask Your AI Assistant to Generate an Image**
51 | - Simply ask naturally: "Can you generate an image of a serene mountain landscape at sunset?"
52 | - Or be more specific: "Please create an image showing a peaceful mountain scene with a lake reflecting the sunset colors in the foreground"
53 |
54 | 4. **Explore Advanced Features**
55 | - Try different parameter settings for customized results
56 | - Experiment with SVG generation using `generate_svg`
57 | - Use batch image generation or variant generation features
58 |
59 | ### Cursor Integration
60 |
61 | #### Method 1: Using mcp.json
62 |
63 | 1. Create or edit the `.cursor/mcp.json` file in your project directory:
64 |
65 | ```json
66 | {
67 | "mcpServers": {
68 | "replicate-flux-mcp": {
69 | "command": "env REPLICATE_API_TOKEN=YOUR_TOKEN npx",
70 | "args": ["-y", "replicate-flux-mcp"]
71 | }
72 | }
73 | }
74 | ```
75 |
76 | 2. Replace `YOUR_TOKEN` with your actual Replicate API token
77 | 3. Restart Cursor to apply the changes
78 |
79 | #### Method 2: Manual Mode
80 |
81 | 1. Open Cursor and go to Settings
82 | 2. Navigate to the "MCP" or "Model Context Protocol" section
83 | 3. Click "Add Server" or equivalent
84 | 4. Enter the following command in the appropriate field:
85 |
86 | ```
87 | env REPLICATE_API_TOKEN=YOUR_TOKEN npx -y replicate-flux-mcp
88 | ```
89 |
90 | 5. Replace `YOUR_TOKEN` with your actual Replicate API token
91 | 6. Save the settings and restart Cursor if necessary
92 |
93 | ### Claude Desktop Integration
94 |
95 | 1. Create or edit the `mcp.json` file in your configuration directory:
96 |
97 | ```json
98 | {
99 | "mcpServers": {
100 | "replicate-flux-mcp": {
101 | "command": "npx",
102 | "args": ["-y", "replicate-flux-mcp"],
103 | "env": {
104 | "REPLICATE_API_TOKEN": "YOUR TOKEN"
105 | }
106 | }
107 | }
108 | }
109 | ```
110 |
111 | 2. Replace `YOUR_TOKEN` with your actual Replicate API token
112 | 3. Restart Claude Desktop to apply the changes
113 |
114 | ### Smithery Integration
115 |
116 | This MCP server is available as a hosted service on Smithery, allowing you to use it without setting up your own server.
117 |
118 | 1. Visit [Smithery](https://smithery.ai/) and create an account if you don't have one
119 | 2. Navigate to the [Replicate Flux MCP server page](https://smithery.ai/server/@awkoy/replicate-flux-mcp)
120 | 3. Click "Add to Workspace" to add the server to your Smithery workspace
121 | 4. Configure your MCP client (Cursor, Claude Desktop, etc.) to use your Smithery workspace URL
122 |
123 | For more information on using Smithery with your MCP clients, visit the [Smithery documentation](https://smithery.ai/docs).
124 |
125 | ### Glama.ai Integration
126 |
127 | This MCP server is also available as a hosted service on Glama.ai, providing another option to use it without local setup.
128 |
129 | 1. Visit [Glama.ai](https://glama.ai/) and create an account if you don't have one
130 | 2. Go to the [Replicate Flux MCP server page](https://glama.ai/mcp/servers/ss8n1knen8)
131 | 3. Click "Install Server" to add the server to your workspace
132 | 4. Configure your MCP client to use your Glama.ai workspace
133 |
134 | For more information, visit the [Glama.ai MCP servers documentation](https://glama.ai/mcp/servers).
135 |
136 | ## 🌟 Features
137 |
138 | - **🖼️ High-Quality Image Generation** - Create stunning images using Flux Schnell, a state-of-the-art AI model
139 | - **🎨 Vector Graphics Support** - Generate professional SVG vector graphics with Recraft V3 SVG model
140 | - **🤖 AI Assistant Integration** - Seamlessly enable AI assistants like Claude to generate visual content
141 | - **🎛️ Advanced Customization** - Fine-tune generation with controls for aspect ratio, quality, resolution, and more
142 | - **🔌 Universal MCP Compatibility** - Works with all MCP clients including Cursor, Claude Desktop, Cline, and Zed
143 | - **🔒 Secure Local Processing** - All requests are processed locally for enhanced privacy and security
144 | - **🔍 Comprehensive History Management** - Track, view, and retrieve your complete generation history
145 | - **📊 Batch Processing** - Generate multiple images from different prompts in a single request
146 | - **🔄 Variant Exploration** - Create and compare multiple interpretations of the same concept
147 | - **✏️ Prompt Engineering** - Fine-tune image variations with specialized prompt modifications
148 |
149 | ## 📚 Documentation
150 |
151 | ### Available Tools
152 |
153 | #### `generate_image`
154 |
155 | Generates an image based on a text prompt using the Flux Schnell model.
156 |
157 | ```typescript
158 | {
159 | prompt: string; // Required: Text description of the image to generate
160 | seed?: number; // Optional: Random seed for reproducible generation
161 | go_fast?: boolean; // Optional: Run faster predictions with optimized model (default: true)
162 | megapixels?: "1" | "0.25"; // Optional: Image resolution (default: "1")
163 | num_outputs?: number; // Optional: Number of images to generate (1-4) (default: 1)
164 | aspect_ratio?: string; // Optional: Aspect ratio (e.g., "16:9", "4:3") (default: "1:1")
165 | output_format?: string; // Optional: Output format ("webp", "jpg", "png") (default: "webp")
166 | output_quality?: number; // Optional: Image quality (0-100) (default: 80)
167 | num_inference_steps?: number; // Optional: Number of denoising steps (1-4) (default: 4)
168 | disable_safety_checker?: boolean; // Optional: Disable safety filter (default: false)
169 | }
170 | ```
171 |
172 | #### `generate_multiple_images`
173 |
174 | Generates multiple images based on an array of prompts using the Flux Schnell model.
175 |
176 | ```typescript
177 | {
178 | prompts: string[]; // Required: Array of text descriptions for images to generate (1-10 prompts)
179 | seed?: number; // Optional: Random seed for reproducible generation
180 | go_fast?: boolean; // Optional: Run faster predictions with optimized model (default: true)
181 | megapixels?: "1" | "0.25"; // Optional: Image resolution (default: "1")
182 | aspect_ratio?: string; // Optional: Aspect ratio (e.g., "16:9", "4:3") (default: "1:1")
183 | output_format?: string; // Optional: Output format ("webp", "jpg", "png") (default: "webp")
184 | output_quality?: number; // Optional: Image quality (0-100) (default: 80)
185 | num_inference_steps?: number; // Optional: Number of denoising steps (1-4) (default: 4)
186 | disable_safety_checker?: boolean; // Optional: Disable safety filter (default: false)
187 | }
188 | ```
189 |
190 | #### `generate_image_variants`
191 |
192 | Generates multiple variants of the same image from a single prompt.
193 |
194 | ```typescript
195 | {
196 | prompt: string; // Required: Text description for the image to generate variants of
197 | num_variants: number; // Required: Number of image variants to generate (2-10, default: 4)
198 | prompt_variations?: string[]; // Optional: List of prompt modifiers to apply to variants (e.g., ["in watercolor style", "in oil painting style"])
199 | variation_mode?: "append" | "replace"; // Optional: How to apply variations - 'append' adds to base prompt, 'replace' uses variations directly (default: "append")
200 | seed?: number; // Optional: Base random seed. Each variant will use seed+variant_index
201 | go_fast?: boolean; // Optional: Run faster predictions with optimized model (default: true)
202 | megapixels?: "1" | "0.25"; // Optional: Image resolution (default: "1")
203 | aspect_ratio?: string; // Optional: Aspect ratio (e.g., "16:9", "4:3") (default: "1:1")
204 | output_format?: string; // Optional: Output format ("webp", "jpg", "png") (default: "webp")
205 | output_quality?: number; // Optional: Image quality (0-100) (default: 80)
206 | num_inference_steps?: number; // Optional: Number of denoising steps (1-4) (default: 4)
207 | disable_safety_checker?: boolean; // Optional: Disable safety filter (default: false)
208 | }
209 | ```
210 |
211 | #### `generate_svg`
212 |
213 | Generates an SVG vector image based on a text prompt using the Recraft V3 SVG model.
214 |
215 | ```typescript
216 | {
217 | prompt: string; // Required: Text description of the SVG to generate
218 | size?: string; // Optional: Size of the generated SVG (default: "1024x1024")
219 | style?: string; // Optional: Style of the generated image (default: "any")
220 | // Options: "any", "engraving", "line_art", "line_circuit", "linocut"
221 | }
222 | ```
223 |
224 | #### `prediction_list`
225 |
226 | Retrieves a list of your recent predictions from Replicate.
227 |
228 | ```typescript
229 | {
230 | limit?: number; // Optional: Maximum number of predictions to return (1-100) (default: 50)
231 | }
232 | ```
233 |
234 | #### `get_prediction`
235 |
236 | Gets detailed information about a specific prediction.
237 |
238 | ```typescript
239 | {
240 | predictionId: string; // Required: ID of the prediction to retrieve
241 | }
242 | ```
243 |
244 | ### Available Resources
245 |
246 | #### `imagelist`
247 |
248 | Browse your history of generated images created with the Flux Schnell model.
249 |
250 | #### `svglist`
251 |
252 | Browse your history of generated SVG images created with the Recraft V3 SVG model.
253 |
254 | #### `predictionlist`
255 |
256 | Browse all your Replicate predictions history.
257 |
258 | ## 💻 Development
259 |
260 | 1. Clone the repository:
261 |
262 | ```bash
263 | git clone https://github.com/awkoy/replicate-flux-mcp.git
264 | cd replicate-flux-mcp
265 | ```
266 |
267 | 2. Install dependencies:
268 |
269 | ```bash
270 | npm install
271 | ```
272 |
273 | 3. Start development mode:
274 |
275 | ```bash
276 | npm run dev
277 | ```
278 |
279 | 4. Build the project:
280 |
281 | ```bash
282 | npm run build
283 | ```
284 |
285 | 5. Connect to Client:
286 |
287 | ```json
288 | {
289 | "mcpServers": {
290 | "image-generation-mcp": {
291 | "command": "npx",
292 | "args": [
293 | "/Users/{USERNAME}/{PATH_TO}/replicate-flux-mcp/build/index.js"
294 | ],
295 | "env": {
296 | "REPLICATE_API_TOKEN": "YOUR REPLICATE API TOKEN"
297 | }
298 | }
299 | }
300 | }
301 | ```
302 |
303 | ## ⚙️ Technical Details
304 |
305 | ### Stack
306 |
307 | - **Model Context Protocol SDK** - Core MCP functionality for tool and resource management
308 | - **Replicate API** - Provides access to state-of-the-art AI image generation models
309 | - **TypeScript** - Ensures type safety and leverages modern JavaScript features
310 | - **Zod** - Implements runtime type validation for robust API interactions
311 |
312 | ### Configuration
313 |
314 | The server can be configured by modifying the `CONFIG` object in `src/config/index.ts`:
315 |
316 | ```javascript
317 | const CONFIG = {
318 | serverName: "replicate-flux-mcp",
319 | serverVersion: "0.1.2",
320 | imageModelId: "black-forest-labs/flux-schnell",
321 | svgModelId: "recraft-ai/recraft-v3-svg",
322 | pollingAttempts: 25,
323 | pollingInterval: 2000, // ms
324 | };
325 | ```
326 |
327 | ## 🔍 Troubleshooting
328 |
329 | ### Common Issues
330 |
331 | #### Authentication Error
332 | - Ensure your `REPLICATE_API_TOKEN` is correctly set in the environment
333 | - Verify your token is valid by testing it with the Replicate API directly
334 |
335 | #### Safety Filter Triggered
336 | - The model has a built-in safety filter that may block certain prompts
337 | - Try modifying your prompt to avoid potentially problematic content
338 |
339 | #### Timeout Error
340 | - For larger images or busy servers, you might need to increase `pollingAttempts` or `pollingInterval` in the configuration
341 | - Default settings should work for most use cases
342 |
343 | ## 🤝 Contributing
344 |
345 | Contributions are welcome! Please follow these steps to contribute:
346 |
347 | 1. Fork the repository
348 | 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
349 | 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
350 | 4. Push to the branch (`git push origin feature/amazing-feature`)
351 | 5. Open a Pull Request
352 |
353 | For feature requests or bug reports, please create a GitHub issue. If you like this project, consider starring the repository!
354 |
355 | ## 📄 License
356 |
357 | This project is licensed under the MIT License - see the LICENSE file for details.
358 |
359 | ## 🔗 Resources
360 |
361 | - [Model Context Protocol Documentation](https://modelcontextprotocol.io)
362 | - [Replicate API Documentation](https://replicate.com/docs)
363 | - [Flux Schnell Model](https://replicate.com/black-forest-labs/flux-schnell)
364 | - [Recraft V3 SVG Model](https://replicate.com/recraft-ai/recraft-v3-svg)
365 | - [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)
366 | - [Smithery Documentation](https://smithery.ai/docs)
367 | - [Glama.ai MCP Servers](https://glama.ai/mcp/servers)
368 |
369 | ## 🎨 Examples
370 |
371 | 
372 |
373 | | Multiple Prompts | Prompt Variants |
374 | |-----------------|-----------------|
375 | |  |  |
376 |
377 | Here are some examples of how to use the tools:
378 |
379 | ### Batch Image Generation with `generate_multiple_images`
380 |
381 | Create multiple distinct images at once with different prompts:
382 |
383 | ```json
384 | {
385 | "prompts": [
386 | "A red sports car on a mountain road",
387 | "A blue sports car on a beach",
388 | "A vintage sports car in a city street"
389 | ]
390 | }
391 | ```
392 |
393 | ### Image Variants with `generate_image_variants`
394 |
395 | Create different interpretations of the same concept using seeds:
396 |
397 | ```json
398 | {
399 | "prompt": "A futuristic city skyline at night",
400 | "num_variants": 4,
401 | "seed": 42
402 | }
403 | ```
404 |
405 | Or explore style variations with prompt modifiers:
406 |
407 | ```json
408 | {
409 | "prompt": "A character portrait",
410 | "prompt_variations": [
411 | "in anime style",
412 | "in watercolor style",
413 | "in oil painting style",
414 | "as a 3D render"
415 | ]
416 | }
417 | ```
418 |
419 | ---
420 |
421 | Made with ❤️ by Yaroslav Boiko
422 |
423 |
```
--------------------------------------------------------------------------------
/src/utils/error.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js";
2 |
3 | export function handleError(error: unknown): never {
4 | if (error instanceof Error) {
5 | throw new McpError(ErrorCode.InternalError, error.message);
6 | }
7 | throw new McpError(ErrorCode.InternalError, String(error));
8 | }
9 |
```
--------------------------------------------------------------------------------
/src/config/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | // Configuration
2 | export const CONFIG = {
3 | serverName: "replicate-flux-mcp",
4 | serverVersion: "0.1.2",
5 | imageModelId: "black-forest-labs/flux-schnell" as `${string}/${string}`,
6 | svgModelId: "recraft-ai/recraft-v3-svg" as `${string}/${string}`,
7 | pollingAttempts: 25,
8 | pollingInterval: 2000, // ms
9 | };
10 |
```
--------------------------------------------------------------------------------
/src/resources/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { registerImageListResource } from "./imageList.js";
2 | import { registerPreditionListResource } from "./predictionList.js";
3 | import { registerSvgListResource } from "./svgList.js";
4 |
5 | export const registerAllResources = () => {
6 | registerImageListResource();
7 | registerPreditionListResource();
8 | registerSvgListResource();
9 | };
10 |
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "compilerOptions": {
3 | "target": "ES2022",
4 | "module": "Node16",
5 | "moduleResolution": "Node16",
6 | "outDir": "./build",
7 | "rootDir": "./src",
8 | "strict": true,
9 | "esModuleInterop": true,
10 | "skipLibCheck": true,
11 | "forceConsistentCasingInFileNames": true
12 | },
13 | "include": ["src/**/*"],
14 | "exclude": ["node_modules"]
15 | }
16 |
```
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM node:22.12-alpine AS builder
2 |
3 | WORKDIR /app
4 |
5 | COPY package*.json ./
6 | COPY tsconfig.json ./
7 | COPY src/ ./src/
8 |
9 | RUN --mount=type=cache,target=/root/.npm npm install
10 | RUN npm run build
11 |
12 | FROM node:22.12-alpine AS release
13 |
14 | WORKDIR /app
15 |
16 | COPY --from=builder /app/build /app/build
17 | COPY --from=builder /app/package.json ./
18 | COPY --from=builder /app/package-lock.json ./
19 |
20 | ENV NODE_ENV=production
21 |
22 | RUN npm ci --ignore-scripts --omit-dev
23 |
24 | CMD ["node", "build/index.js"]
25 |
```
--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------
```yaml
1 | # Smithery configuration file: https://smithery.ai/docs/deployments
2 |
3 | startCommand:
4 | type: stdio
5 | configSchema:
6 | # JSON Schema defining the configuration options for the MCP.
7 | type: object
8 | required:
9 | - replicateApiToken
10 | properties:
11 | replicateApiToken:
12 | type: string
13 | description: The API key for the Replicate API.
14 | commandFunction:
15 | # A function that produces the CLI command to start the MCP on stdio.
16 | |-
17 | config=>({command:'node',args:['build/index.js'],env:{REPLICATE_API_TOKEN:config.replicateApiToken}})
```
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | #!/usr/bin/env node
2 | import { registerAllResources } from "./resources/index.js";
3 | import { startServer } from "./server/index.js";
4 | import { registerAllTools } from "./tools/index.js";
5 |
6 | registerAllTools();
7 | registerAllResources();
8 |
9 | async function main() {
10 | try {
11 | await startServer();
12 | } catch (error) {
13 | console.error(
14 | "Unhandled server error:",
15 | error instanceof Error ? error.message : String(error)
16 | );
17 | process.exit(1);
18 | }
19 | }
20 |
21 | main().catch((error: unknown) => {
22 | console.error(
23 | "Unhandled server error:",
24 | error instanceof Error ? error.message : String(error)
25 | );
26 | process.exit(1);
27 | });
28 |
```
--------------------------------------------------------------------------------
/src/tools/getPrediction.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { replicate } from "../services/replicate.js";
2 | import { handleError } from "../utils/error.js";
3 | import { GetPredictionParams } from "../types/index.js";
4 | import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
5 |
6 | export const registerGetPredictionTool = async ({
7 | predictionId,
8 | }: GetPredictionParams): Promise<CallToolResult> => {
9 | try {
10 | const prediction = await replicate.predictions.get(predictionId);
11 |
12 | return {
13 | content: [
14 | {
15 | type: "text",
16 | text: JSON.stringify(prediction, null, 2),
17 | },
18 | ],
19 | };
20 | } catch (error) {
21 | handleError(error);
22 | }
23 | };
24 |
```
--------------------------------------------------------------------------------
/src/services/replicate.ts:
--------------------------------------------------------------------------------
```typescript
1 | import Replicate from "replicate";
2 | import { CONFIG } from "../config/index.js";
3 |
4 | export function getReplicateApiToken(): string {
5 | const token = process.env.REPLICATE_API_TOKEN;
6 | if (!token) {
7 | console.error(
8 | "Error: REPLICATE_API_TOKEN environment variable is required"
9 | );
10 | process.exit(1);
11 | }
12 | return token;
13 | }
14 |
15 | export const replicate = new Replicate({
16 | auth: getReplicateApiToken(),
17 | });
18 |
19 | export async function pollForCompletion(predictionId: string) {
20 | for (let i = 0; i < CONFIG.pollingAttempts; i++) {
21 | const latest = await replicate.predictions.get(predictionId);
22 | if (latest.status !== "starting" && latest.status !== "processing") {
23 | return latest;
24 | }
25 | await new Promise((resolve) => setTimeout(resolve, CONFIG.pollingInterval));
26 | }
27 | return null;
28 | }
29 |
```
--------------------------------------------------------------------------------
/src/tools/createPrediction.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { CreatePredictionParams } from "../types/index.js";
2 | import { pollForCompletion, replicate } from "../services/replicate.js";
3 | import { handleError } from "../utils/error.js";
4 | import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
5 | import { CONFIG } from "../config/index.js";
6 |
7 | export const registerCreatePredictionTool = async (
8 | input: CreatePredictionParams
9 | ): Promise<CallToolResult> => {
10 | try {
11 | const prediction = await replicate.predictions.create({
12 | model: CONFIG.imageModelId,
13 | input,
14 | });
15 |
16 | await replicate.predictions.get(prediction.id);
17 | const completed = await pollForCompletion(prediction.id);
18 |
19 | return {
20 | content: [
21 | {
22 | type: "text",
23 | text: JSON.stringify(completed || "Processing timed out", null, 2),
24 | },
25 | ],
26 | };
27 | } catch (error) {
28 | handleError(error);
29 | }
30 | };
31 |
```
--------------------------------------------------------------------------------
/src/server/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3 | import { CONFIG } from "../config/index.js";
4 |
5 | export const server = new McpServer(
6 | {
7 | name: CONFIG.serverName,
8 | version: CONFIG.serverVersion,
9 | },
10 | {
11 | capabilities: {
12 | resources: {},
13 | tools: {},
14 | },
15 | instructions: `
16 | MCP server for the Replicate models.
17 | It is used to generate images and SVGs from text prompts.
18 | `,
19 | }
20 | );
21 |
22 | export async function startServer() {
23 | try {
24 | const transport = new StdioServerTransport();
25 | await server.connect(transport);
26 | console.error(
27 | `${CONFIG.serverName} v${CONFIG.serverVersion} running on stdio`
28 | );
29 | } catch (error) {
30 | console.error(
31 | "Server initialization error:",
32 | error instanceof Error ? error.message : String(error)
33 | );
34 | process.exit(1);
35 | }
36 | }
37 |
```
--------------------------------------------------------------------------------
/src/utils/image.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { FileOutput } from "replicate";
2 |
3 | export async function outputToBase64(output: FileOutput) {
4 | const blob = await output.blob();
5 | const buffer = Buffer.from(await blob.arrayBuffer());
6 | return buffer.toString("base64");
7 | }
8 |
9 | export async function urlToSvg(url: string) {
10 | try {
11 | const data = await fetch(url, {
12 | headers: {
13 | Authorization: `Bearer ${process.env.REPLICATE_API_TOKEN}`,
14 | },
15 | });
16 |
17 | const text = await data.text();
18 |
19 | return text;
20 | } catch (error) {
21 | throw new Error("Error fetching svg");
22 | }
23 | }
24 |
25 | export async function urlToBase64(url: string) {
26 | try {
27 | const data = await fetch(url, {
28 | headers: {
29 | Authorization: `Bearer ${process.env.REPLICATE_API_TOKEN}`,
30 | },
31 | });
32 |
33 | const blob = await data.blob();
34 |
35 | let buffer = Buffer.from(await blob.arrayBuffer());
36 | return buffer.toString("base64");
37 | } catch (error) {
38 | throw new Error("Error fetching image");
39 | }
40 | }
41 |
```
--------------------------------------------------------------------------------
/src/tools/predictionList.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { PredictionListParams } from "../types/index.js";
2 | import { replicate } from "../services/replicate.js";
3 | import { handleError } from "../utils/error.js";
4 | import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
5 |
6 | export const registerPredictionListTool = async ({
7 | limit,
8 | }: PredictionListParams): Promise<CallToolResult> => {
9 | try {
10 | const predictions = [];
11 | for await (const page of replicate.paginate(replicate.predictions.list)) {
12 | predictions.push(...page);
13 | if (predictions.length >= limit) {
14 | break;
15 | }
16 | }
17 |
18 | const limitedPredictions = predictions.slice(0, limit);
19 | const totalPages = Math.ceil(predictions.length / limit);
20 |
21 | return {
22 | content: [
23 | {
24 | type: "text",
25 | text: `Found ${limitedPredictions.length} predictions (showing ${limitedPredictions.length} of ${predictions.length} total, page 1 of ${totalPages})`,
26 | },
27 | {
28 | type: "text",
29 | text: JSON.stringify(limitedPredictions, null, 2),
30 | },
31 | ],
32 | };
33 | } catch (error) {
34 | handleError(error);
35 | }
36 | };
37 |
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "name": "replicate-flux-mcp",
3 | "version": "0.1.2",
4 | "type": "module",
5 | "bin": {
6 | "replicate-flux-mcp": "build/index.js"
7 | },
8 | "scripts": {
9 | "build": "tsc && shx chmod +x build/*.js",
10 | "prepare": "npm run build",
11 | "watch": "tsc --watch",
12 | "inspector": "npx @modelcontextprotocol/inspector build/index.js -e REPLICATE_API_TOKEN=YOUR_REPLICATE_API_TOKEN"
13 | },
14 | "homepage": "https://github.com/awkoy/replicate-flux-mcp",
15 | "keywords": [
16 | "replicate",
17 | "flux",
18 | "mcp",
19 | "flux-schnell",
20 | "flux-schnell-mcp",
21 | "modelcontextprotocol",
22 | "image-generation",
23 | "ai"
24 | ],
25 | "author": "Yaroslav Boiko <[email protected]>",
26 | "license": "MIT",
27 | "description": "MCP for Replicate Flux Model",
28 | "files": [
29 | "build"
30 | ],
31 | "dependencies": {
32 | "@modelcontextprotocol/sdk": "^1.7.0",
33 | "replicate": "^1.0.1",
34 | "zod": "^3.24.2"
35 | },
36 | "devDependencies": {
37 | "@types/node": "^22.13.10",
38 | "shx": "^0.3.4",
39 | "typescript": "^5.8.2"
40 | },
41 | "engines": {
42 | "node": ">=18"
43 | },
44 | "repository": {
45 | "type": "git",
46 | "url": "git+https://github.com/awkoy/replicate-flux-mcp.git"
47 | }
48 | }
49 |
```
--------------------------------------------------------------------------------
/src/tools/generateSVG.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { SvgGenerationParams } from "../types/index.js";
2 | import { replicate } from "../services/replicate.js";
3 | import { handleError } from "../utils/error.js";
4 | import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
5 | import { urlToSvg } from "../utils/image.js";
6 | import { CONFIG } from "../config/index.js";
7 | import { FileOutput } from "replicate";
8 |
9 | export const registerGenerateSvgTool = async (
10 | input: SvgGenerationParams
11 | ): Promise<CallToolResult> => {
12 | try {
13 | const output = (await replicate.run(CONFIG.svgModelId, {
14 | input,
15 | })) as FileOutput;
16 |
17 | const svgUrl = output.url() as unknown as string;
18 | if (!svgUrl) {
19 | throw new Error("Failed to generate SVG URL");
20 | }
21 |
22 | try {
23 | const svg = await urlToSvg(svgUrl);
24 |
25 | return {
26 | content: [
27 | {
28 | type: "text",
29 | text: `This is a generated SVG url: ${svgUrl}`,
30 | },
31 | {
32 | type: "text",
33 | text: svg,
34 | },
35 | ],
36 | };
37 | } catch (error) {
38 | return {
39 | content: [
40 | {
41 | type: "text",
42 | text: `This is a generated SVG url: ${svgUrl}`,
43 | },
44 | ],
45 | };
46 | }
47 | } catch (error) {
48 | return handleError(error);
49 | }
50 | };
51 |
```
--------------------------------------------------------------------------------
/src/resources/predictionList.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { server } from "../server/index.js";
2 | import {
3 | ListResourcesCallback,
4 | ResourceTemplate,
5 | } from "@modelcontextprotocol/sdk/server/mcp.js";
6 | import { replicate } from "../services/replicate.js";
7 | import { Prediction } from "replicate";
8 |
9 | export const registerPreditionListResource = () => {
10 | const list: ListResourcesCallback = async () => {
11 | try {
12 | const predictions: Prediction[] = [];
13 | for await (const page of replicate.paginate(replicate.predictions.list)) {
14 | predictions.push(...page);
15 | }
16 |
17 | return {
18 | resources: predictions.map((prediction) => ({
19 | uri: `predictions://${prediction.id}`,
20 | name: `Prediction ${prediction.id}`,
21 | mimeType: "application/json",
22 | })),
23 | nextCursor: undefined,
24 | };
25 | } catch (error) {
26 | console.error("Error listing predictions:", error);
27 | return {
28 | resources: [],
29 | nextCursor: undefined,
30 | };
31 | }
32 | };
33 |
34 | server.resource(
35 | "predictions",
36 | new ResourceTemplate("predictions://{id}", {
37 | list,
38 | }),
39 | async (uri, { id }) => {
40 | const prediction = await replicate.predictions.get(id as string);
41 |
42 | return {
43 | contents: [
44 | {
45 | name: "prediction",
46 | uri: uri.href,
47 | text: JSON.stringify(prediction),
48 | mimeType: "application/json",
49 | },
50 | ],
51 | };
52 | }
53 | );
54 | };
55 |
```
--------------------------------------------------------------------------------
/src/tools/generateImage.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { ImageGenerationParams } from "../types/index.js";
2 | import { replicate } from "../services/replicate.js";
3 | import { handleError } from "../utils/error.js";
4 | import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
5 | import { FileOutput } from "replicate";
6 | import { outputToBase64 } from "../utils/image.js";
7 | import { CONFIG } from "../config/index.js";
8 |
9 | export const registerGenerateImageTool = async (
10 | input: ImageGenerationParams
11 | ): Promise<CallToolResult> => {
12 | const { support_image_mcp_response_type, ...predictionInput } = input;
13 | try {
14 | const [output] = (await replicate.run(CONFIG.imageModelId, {
15 | input: predictionInput,
16 | })) as [FileOutput];
17 | const imageUrl = output.url() as unknown as string;
18 |
19 | if (support_image_mcp_response_type) {
20 | const imageBase64 = await outputToBase64(output);
21 | return {
22 | content: [
23 | {
24 | type: "text",
25 | text: `This is a generated image link: ${imageUrl}`,
26 | },
27 | {
28 | type: "image",
29 | data: imageBase64,
30 | mimeType: "image/png",
31 | },
32 | {
33 | type: "text",
34 | text: `The image above is generated by the Flux model and prompt: ${input.prompt}`,
35 | },
36 | ],
37 | };
38 | }
39 |
40 | return {
41 | content: [
42 | {
43 | type: "text",
44 | text: `This is a generated image link: ${imageUrl}`,
45 | },
46 | {
47 | type: "text",
48 | text: `The image above is generated by the Flux model and prompt: ${input.prompt}`,
49 | },
50 | ],
51 | };
52 | } catch (error) {
53 | handleError(error);
54 | }
55 | };
56 |
```
--------------------------------------------------------------------------------
/src/resources/svgList.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { server } from "../server/index.js";
2 | import {
3 | ListResourcesCallback,
4 | ResourceTemplate,
5 | } from "@modelcontextprotocol/sdk/server/mcp.js";
6 | import { replicate } from "../services/replicate.js";
7 | import { urlToSvg } from "../utils/image.js";
8 | import { Prediction } from "replicate";
9 | import { CONFIG } from "../config/index.js";
10 |
11 | export const registerSvgListResource = () => {
12 | const list: ListResourcesCallback = async () => {
13 | try {
14 | const predictions: Prediction[] = [];
15 | for await (const page of replicate.paginate(replicate.predictions.list)) {
16 | predictions.push(...page);
17 | }
18 |
19 | return {
20 | resources: predictions
21 | .filter((prediction) => prediction.model === CONFIG.svgModelId)
22 | .map((prediction) => ({
23 | uri: `svglist://${prediction.id}`,
24 | name: `SVG ${prediction.id}`,
25 | mimeType: "application/json",
26 | description: `Generated image by ${prediction.model} with id ${prediction.id}`,
27 | })),
28 | nextCursor: undefined,
29 | };
30 | } catch (error) {
31 | console.error("Error listing predictions:", error);
32 | return {
33 | resources: [],
34 | nextCursor: undefined,
35 | };
36 | }
37 | };
38 |
39 | server.resource(
40 | "svglist",
41 | new ResourceTemplate("svglist://{id}", {
42 | list,
43 | }),
44 | async (uri, { id }) => {
45 | const prediction = await replicate.predictions.get(id as string);
46 |
47 | if (!prediction.output) {
48 | return {
49 | contents: [
50 | {
51 | name: "Not Found!",
52 | uri: uri.href,
53 | text: `Data has been removed by Replicate automatically after an hour, by default. You have to save your own copies before it is removed.`,
54 | mimeType: "text/plain",
55 | },
56 | ],
57 | };
58 | }
59 |
60 | const svg = await urlToSvg(prediction.output);
61 |
62 | return {
63 | contents: [
64 | {
65 | name: "svglist",
66 | uri: uri.href,
67 | text: svg,
68 | mimeType: "image/svg+xml",
69 | },
70 | ],
71 | };
72 | }
73 | );
74 | };
75 |
```
--------------------------------------------------------------------------------
/src/tools/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import {
2 | getPredictionSchema,
3 | svgGenerationSchema,
4 | multiImageGenerationSchema,
5 | imageVariantsGenerationSchema,
6 | } from "../types/index.js";
7 | import { predictionListSchema } from "../types/index.js";
8 |
9 | import { server } from "../server/index.js";
10 | import { imageGenerationSchema } from "../types/index.js";
11 | import { registerGetPredictionTool } from "./getPrediction.js";
12 | import { registerPredictionListTool } from "./predictionList.js";
13 | import { registerGenerateImageTool } from "./generateImage.js";
14 | import { createPredictionSchema } from "../types/index.js";
15 | import { registerCreatePredictionTool } from "./createPrediction.js";
16 | import { registerGenerateSvgTool } from "./generateSVG.js";
17 | import { registerGenerateMultipleImagesTool } from "./generateMultipleImages.js";
18 | import { registerGenerateImageVariantsTool } from "./generateImageVariants.js";
19 |
20 | export const registerAllTools = () => {
21 | server.tool(
22 | "generate_image",
23 | "Generate an image from a text prompt using Flux Schnell model",
24 | imageGenerationSchema,
25 | registerGenerateImageTool
26 | );
27 | server.tool(
28 | "generate_multiple_images",
29 | "Generate multiple images from an array of prompts using Flux Schnell model",
30 | multiImageGenerationSchema,
31 | registerGenerateMultipleImagesTool
32 | );
33 | server.tool(
34 | "generate_image_variants",
35 | "Generate multiple variants of the same image from a single prompt",
36 | imageVariantsGenerationSchema,
37 | registerGenerateImageVariantsTool
38 | );
39 | server.tool(
40 | "generate_svg",
41 | "Generate an SVG from a text prompt using Recraft model",
42 | svgGenerationSchema,
43 | registerGenerateSvgTool
44 | );
45 | server.tool(
46 | "get_prediction",
47 | "Get details of a specific prediction by ID",
48 | getPredictionSchema,
49 | registerGetPredictionTool
50 | );
51 | server.tool(
52 | "create_prediction",
53 | "Generate an prediction from a text prompt using Flux Schnell model",
54 | createPredictionSchema,
55 | registerCreatePredictionTool
56 | );
57 | server.tool(
58 | "prediction_list",
59 | "Get a list of recent predictions from Replicate",
60 | predictionListSchema,
61 | registerPredictionListTool
62 | );
63 | };
64 |
```
--------------------------------------------------------------------------------
/src/resources/imageList.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { server } from "../server/index.js";
2 | import {
3 | ListResourcesCallback,
4 | ResourceTemplate,
5 | } from "@modelcontextprotocol/sdk/server/mcp.js";
6 | import { replicate } from "../services/replicate.js";
7 | import { urlToBase64 } from "../utils/image.js";
8 | import { Prediction } from "replicate";
9 | import { CONFIG } from "../config/index.js";
10 |
11 | export const registerImageListResource = () => {
12 | const list: ListResourcesCallback = async () => {
13 | try {
14 | const predictions: Prediction[] = [];
15 | for await (const page of replicate.paginate(replicate.predictions.list)) {
16 | predictions.push(...page);
17 | }
18 |
19 | return {
20 | resources: predictions
21 | .filter(
22 | (prediction) =>
23 | prediction.output?.length &&
24 | prediction.model === CONFIG.imageModelId
25 | )
26 | .map((prediction) => ({
27 | uri: `images://${prediction.id}`,
28 | name: `Image ${prediction.id}`,
29 | mimeType: "application/json",
30 | description: `Generated image by ${prediction.model} with id ${prediction.id}`,
31 | })),
32 | nextCursor: undefined,
33 | };
34 | } catch (error) {
35 | console.error("Error listing predictions:", error);
36 | return {
37 | resources: [],
38 | nextCursor: undefined,
39 | };
40 | }
41 | };
42 |
43 | server.resource(
44 | "images",
45 | new ResourceTemplate("images://{id}", {
46 | list,
47 | }),
48 | async (uri, { id }) => {
49 | const prediction = await replicate.predictions.get(id as string);
50 |
51 | if (!prediction.output?.length) {
52 | return {
53 | contents: [
54 | {
55 | name: "Not Found!",
56 | uri: uri.href,
57 | text: `Data has been removed by Replicate automatically after an hour, by default. You have to save your own copies before it is removed.`,
58 | mimeType: "text/plain",
59 | },
60 | ],
61 | };
62 | }
63 |
64 | const imageBase64 = await urlToBase64(prediction.output[0]);
65 |
66 | return {
67 | contents: [
68 | {
69 | name: "image",
70 | uri: uri.href,
71 | blob: imageBase64,
72 | mimeType: "image/png",
73 | },
74 | ],
75 | };
76 | }
77 | );
78 | };
79 |
```
--------------------------------------------------------------------------------
/src/tools/generateMultipleImages.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { MultiImageGenerationParams } from "../types/index.js";
2 | import { replicate } from "../services/replicate.js";
3 | import { handleError } from "../utils/error.js";
4 | import {
5 | CallToolResult,
6 | TextContent,
7 | ImageContent,
8 | } from "@modelcontextprotocol/sdk/types.js";
9 | import { FileOutput } from "replicate";
10 | import { outputToBase64 } from "../utils/image.js";
11 | import { CONFIG } from "../config/index.js";
12 |
13 | export const registerGenerateMultipleImagesTool = async (
14 | input: MultiImageGenerationParams
15 | ): Promise<CallToolResult> => {
16 | const { prompts, support_image_mcp_response_type, ...commonParams } = input;
17 | try {
18 | // Process all prompts in parallel
19 | const generationPromises = prompts.map(async (prompt) => {
20 | const [output] = (await replicate.run(CONFIG.imageModelId, {
21 | input: {
22 | prompt,
23 | ...commonParams,
24 | },
25 | })) as [FileOutput];
26 |
27 | const imageUrl = output.url() as unknown as string;
28 |
29 | if (support_image_mcp_response_type) {
30 | const imageBase64 = await outputToBase64(output);
31 | return {
32 | prompt,
33 | imageUrl,
34 | imageBase64,
35 | };
36 | }
37 |
38 | return {
39 | prompt,
40 | imageUrl,
41 | };
42 | });
43 |
44 | // Wait for all image generation to complete
45 | const results = await Promise.all(generationPromises);
46 |
47 | // Build response content
48 | const responseContent: (TextContent | ImageContent)[] = [];
49 |
50 | // Add intro text
51 | responseContent.push({
52 | type: "text",
53 | text: `Generated ${results.length} images based on your prompts:`,
54 | } as TextContent);
55 |
56 | // Add each image with its prompt
57 | for (const result of results) {
58 | responseContent.push({
59 | type: "text",
60 | text: `\n\nPrompt: "${result.prompt}"\nImage URL: ${result.imageUrl}`,
61 | } as TextContent);
62 |
63 | if (support_image_mcp_response_type && result.imageBase64) {
64 | responseContent.push({
65 | type: "image",
66 | data: result.imageBase64,
67 | mimeType: `image/${
68 | input.output_format === "jpg" ? "jpeg" : input.output_format
69 | }`,
70 | } as ImageContent);
71 | }
72 | }
73 |
74 | return {
75 | content: responseContent,
76 | };
77 | } catch (error) {
78 | handleError(error);
79 | }
80 | };
81 |
```
--------------------------------------------------------------------------------
/src/tools/generateImageVariants.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { ImageVariantsGenerationParams } from "../types/index.js";
2 | import { replicate } from "../services/replicate.js";
3 | import { handleError } from "../utils/error.js";
4 | import {
5 | CallToolResult,
6 | TextContent,
7 | ImageContent,
8 | } from "@modelcontextprotocol/sdk/types.js";
9 | import { FileOutput } from "replicate";
10 | import { outputToBase64 } from "../utils/image.js";
11 | import { CONFIG } from "../config/index.js";
12 |
13 | type ImageVariantResult = {
14 | variantIndex: number;
15 | imageUrl: string;
16 | imageBase64?: string;
17 | usedPrompt: string;
18 | };
19 |
20 | export const registerGenerateImageVariantsTool = async (
21 | input: ImageVariantsGenerationParams
22 | ): Promise<CallToolResult> => {
23 | const {
24 | prompt,
25 | num_variants,
26 | seed,
27 | support_image_mcp_response_type,
28 | prompt_variations,
29 | variation_mode,
30 | ...commonParams
31 | } = input;
32 |
33 | try {
34 | let effectiveVariants = num_variants;
35 | let usingPromptVariations = false;
36 |
37 | // Decide if we're using prompt variations
38 | if (prompt_variations && prompt_variations.length > 0) {
39 | usingPromptVariations = true;
40 | // If using prompt variations, number of variants is limited by available variations
41 | effectiveVariants = Math.min(num_variants, prompt_variations.length);
42 | }
43 |
44 | // Process all variants in parallel
45 | const generationPromises = Array.from(
46 | { length: effectiveVariants },
47 | (_, index) => {
48 | // If seed is provided, create deterministic variants by adding the index
49 | const variantSeed = seed !== undefined ? seed + index : undefined;
50 |
51 | // Determine which prompt to use for this variant
52 | let variantPrompt = prompt;
53 | if (usingPromptVariations) {
54 | const variation = prompt_variations![index];
55 | if (variation_mode === "append") {
56 | variantPrompt = `${prompt} ${variation}`;
57 | } else {
58 | // 'replace' mode
59 | variantPrompt = variation;
60 | }
61 | }
62 |
63 | return replicate
64 | .run(CONFIG.imageModelId, {
65 | input: {
66 | prompt: variantPrompt,
67 | seed: variantSeed,
68 | ...commonParams,
69 | },
70 | })
71 | .then((outputs) => {
72 | const [output] = outputs as [FileOutput];
73 | const imageUrl = output.url() as unknown as string;
74 |
75 | if (support_image_mcp_response_type) {
76 | return outputToBase64(output).then((imageBase64) => ({
77 | variantIndex: index + 1,
78 | imageUrl,
79 | imageBase64,
80 | usedPrompt: variantPrompt,
81 | }));
82 | }
83 |
84 | return {
85 | variantIndex: index + 1,
86 | imageUrl,
87 | usedPrompt: variantPrompt,
88 | };
89 | });
90 | }
91 | );
92 |
93 | // Wait for all variant generation to complete
94 | const results = (await Promise.all(
95 | generationPromises
96 | )) as ImageVariantResult[];
97 |
98 | // Build response content
99 | const responseContent: (TextContent | ImageContent)[] = [];
100 |
101 | // Add intro text - different based on whether we're using prompt variations
102 | if (usingPromptVariations) {
103 | responseContent.push({
104 | type: "text",
105 | text: `Generated ${results.length} variants of "${prompt}" using custom prompt variations (${variation_mode} mode)`,
106 | } as TextContent);
107 | } else {
108 | responseContent.push({
109 | type: "text",
110 | text: `Generated ${results.length} variants of: "${prompt}" using seed variations`,
111 | } as TextContent);
112 | }
113 |
114 | // Add each variant with its index and prompt info
115 | for (const result of results) {
116 | // Build an appropriate description based on variant type
117 | let variantDescription = `Variant #${result.variantIndex}`;
118 |
119 | if (usingPromptVariations) {
120 | variantDescription += `\nPrompt: "${result.usedPrompt}"`;
121 | } else if (seed !== undefined) {
122 | variantDescription += ` (seed: ${seed + (result.variantIndex - 1)})`;
123 | }
124 |
125 | variantDescription += `\nImage URL: ${result.imageUrl}`;
126 |
127 | responseContent.push({
128 | type: "text",
129 | text: `\n\n${variantDescription}`,
130 | } as TextContent);
131 |
132 | if (support_image_mcp_response_type && result.imageBase64) {
133 | responseContent.push({
134 | type: "image",
135 | data: result.imageBase64,
136 | mimeType: `image/${
137 | input.output_format === "jpg" ? "jpeg" : input.output_format
138 | }`,
139 | } as ImageContent);
140 | }
141 | }
142 |
143 | return {
144 | content: responseContent,
145 | };
146 | } catch (error) {
147 | handleError(error);
148 | }
149 | };
150 |
```
--------------------------------------------------------------------------------
/src/types/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { z } from "zod";
2 |
3 | export const createPredictionSchema = {
4 | prompt: z.string().min(1).describe("Prompt for generated image"),
5 | seed: z
6 | .number()
7 | .int()
8 | .optional()
9 | .describe("Random seed. Set for reproducible generation"),
10 | go_fast: z
11 | .boolean()
12 | .default(true)
13 | .describe(
14 | "Run faster predictions with model optimized for speed (currently fp8 quantized); disable to run in original bf16"
15 | ),
16 | megapixels: z
17 | .enum(["1", "0.25"])
18 | .default("1")
19 | .describe("Approximate number of megapixels for generated image"),
20 | num_outputs: z
21 | .number()
22 | .int()
23 | .min(1)
24 | .max(4)
25 | .default(1)
26 | .describe("Number of outputs to generate"),
27 | aspect_ratio: z
28 | .enum([
29 | "1:1",
30 | "16:9",
31 | "21:9",
32 | "3:2",
33 | "2:3",
34 | "4:5",
35 | "5:4",
36 | "3:4",
37 | "4:3",
38 | "9:16",
39 | "9:21",
40 | ])
41 | .default("1:1")
42 | .describe("Aspect ratio for the generated image"),
43 | output_format: z
44 | .enum(["webp", "jpg", "png"])
45 | .default("webp")
46 | .describe("Format of the output images"),
47 | output_quality: z
48 | .number()
49 | .int()
50 | .min(0)
51 | .max(100)
52 | .default(80)
53 | .describe(
54 | "Quality when saving the output images, from 0 to 100. 100 is best quality, 0 is lowest quality. Not relevant for .png outputs"
55 | ),
56 | num_inference_steps: z
57 | .number()
58 | .int()
59 | .min(1)
60 | .max(4)
61 | .default(4)
62 | .describe(
63 | "Number of denoising steps. 4 is recommended, and lower number of steps produce lower quality outputs, faster."
64 | ),
65 | disable_safety_checker: z
66 | .boolean()
67 | .default(false)
68 | .describe("Disable safety checker for generated images."),
69 | };
70 | const createPredictionObjectSchema = z.object(createPredictionSchema);
71 | export type CreatePredictionParams = z.infer<
72 | typeof createPredictionObjectSchema
73 | >;
74 |
75 | export const imageGenerationSchema = {
76 | ...createPredictionSchema,
77 | support_image_mcp_response_type: z
78 | .boolean()
79 | .default(true)
80 | .describe(
81 | "Disable if the image type is not supported in the response, if it's Cursor app for example"
82 | ),
83 | };
84 | const imageGenerationObjectSchema = z.object(imageGenerationSchema);
85 | export type ImageGenerationParams = z.infer<typeof imageGenerationObjectSchema>;
86 |
87 | export const svgGenerationSchema = {
88 | prompt: z.string().min(1).describe("Prompt for generated SVG"),
89 | size: z
90 | .enum([
91 | "1024x1024",
92 | "1365x1024",
93 | "1024x1365",
94 | "1536x1024",
95 | "1024x1536",
96 | "1820x1024",
97 | "1024x1820",
98 | "1024x2048",
99 | "2048x1024",
100 | "1434x1024",
101 | "1024x1434",
102 | "1024x1280",
103 | "1280x1024",
104 | "1024x1707",
105 | "1707x1024",
106 | ])
107 | .default("1024x1024")
108 | .describe("Size of the generated SVG"),
109 | style: z
110 | .enum(["any", "engraving", "line_art", "line_circuit", "linocut"])
111 | .default("any")
112 | .describe("Style of the generated image."),
113 | };
114 | const svgGenerationObjectSchema = z.object(svgGenerationSchema);
115 | export type SvgGenerationParams = z.infer<typeof svgGenerationObjectSchema>;
116 |
117 | export const predictionListSchema = {
118 | limit: z
119 | .number()
120 | .int()
121 | .min(1)
122 | .max(100)
123 | .default(50)
124 | .describe("Maximum number of predictions to return"),
125 | };
126 | const predictionListObjectSchema = z.object(predictionListSchema);
127 | export type PredictionListParams = z.infer<typeof predictionListObjectSchema>;
128 |
129 | export const getPredictionSchema = {
130 | predictionId: z.string().min(1).describe("ID of the prediction to retrieve"),
131 | };
132 | const getPredictionObjectSchema = z.object(getPredictionSchema);
133 | export type GetPredictionParams = z.infer<typeof getPredictionObjectSchema>;
134 |
135 | export const multiImageGenerationSchema = {
136 | prompts: z
137 | .array(z.string().min(1))
138 | .min(1)
139 | .max(10)
140 | .describe("Array of text descriptions for the images to generate"),
141 | seed: z
142 | .number()
143 | .int()
144 | .optional()
145 | .describe("Random seed. Set for reproducible generation"),
146 | go_fast: z
147 | .boolean()
148 | .default(true)
149 | .describe(
150 | "Run faster predictions with model optimized for speed (currently fp8 quantized); disable to run in original bf16"
151 | ),
152 | megapixels: z
153 | .enum(["1", "0.25"])
154 | .default("1")
155 | .describe("Approximate number of megapixels for generated image"),
156 | aspect_ratio: z
157 | .enum([
158 | "1:1",
159 | "16:9",
160 | "21:9",
161 | "3:2",
162 | "2:3",
163 | "4:5",
164 | "5:4",
165 | "3:4",
166 | "4:3",
167 | "9:16",
168 | "9:21",
169 | ])
170 | .default("1:1")
171 | .describe("Aspect ratio for the generated image"),
172 | output_format: z
173 | .enum(["webp", "jpg", "png"])
174 | .default("webp")
175 | .describe("Format of the output images"),
176 | output_quality: z
177 | .number()
178 | .int()
179 | .min(0)
180 | .max(100)
181 | .default(80)
182 | .describe(
183 | "Quality when saving the output images, from 0 to 100. 100 is best quality, 0 is lowest quality. Not relevant for .png outputs"
184 | ),
185 | num_inference_steps: z
186 | .number()
187 | .int()
188 | .min(1)
189 | .max(4)
190 | .default(4)
191 | .describe(
192 | "Number of denoising steps. 4 is recommended, and lower number of steps produce lower quality outputs, faster."
193 | ),
194 | disable_safety_checker: z
195 | .boolean()
196 | .default(false)
197 | .describe("Disable safety checker for generated images."),
198 | support_image_mcp_response_type: z
199 | .boolean()
200 | .default(true)
201 | .describe(
202 | "Disable if the image type is not supported in the response, if it's Cursor app for example"
203 | ),
204 | };
205 | const multiImageGenerationObjectSchema = z.object(multiImageGenerationSchema);
206 | export type MultiImageGenerationParams = z.infer<
207 | typeof multiImageGenerationObjectSchema
208 | >;
209 |
210 | export const imageVariantsGenerationSchema = {
211 | prompt: z
212 | .string()
213 | .min(1)
214 | .describe("Text description for the image to generate variants of"),
215 | num_variants: z
216 | .number()
217 | .int()
218 | .min(2)
219 | .max(10)
220 | .default(4)
221 | .describe("Number of image variants to generate (2-10)"),
222 | prompt_variations: z
223 | .array(z.string())
224 | .optional()
225 | .describe(
226 | "Optional list of prompt modifiers to apply to variants (e.g., ['in watercolor style', 'in oil painting style']). If provided, these will be used instead of random seeds."
227 | ),
228 | variation_mode: z
229 | .enum(["append", "replace"])
230 | .default("append")
231 | .describe(
232 | "How to apply prompt variations: 'append' adds to the base prompt, 'replace' uses variations as standalone prompts"
233 | ),
234 | seed: z
235 | .number()
236 | .int()
237 | .optional()
238 | .describe(
239 | "Base random seed. Each variant will use seed+variant_index for reproducibility"
240 | ),
241 | go_fast: z
242 | .boolean()
243 | .default(true)
244 | .describe(
245 | "Run faster predictions with model optimized for speed (currently fp8 quantized); disable to run in original bf16"
246 | ),
247 | megapixels: z
248 | .enum(["1", "0.25"])
249 | .default("1")
250 | .describe("Approximate number of megapixels for generated image"),
251 | aspect_ratio: z
252 | .enum([
253 | "1:1",
254 | "16:9",
255 | "21:9",
256 | "3:2",
257 | "2:3",
258 | "4:5",
259 | "5:4",
260 | "3:4",
261 | "4:3",
262 | "9:16",
263 | "9:21",
264 | ])
265 | .default("1:1")
266 | .describe("Aspect ratio for the generated image"),
267 | output_format: z
268 | .enum(["webp", "jpg", "png"])
269 | .default("webp")
270 | .describe("Format of the output images"),
271 | output_quality: z
272 | .number()
273 | .int()
274 | .min(0)
275 | .max(100)
276 | .default(80)
277 | .describe(
278 | "Quality when saving the output images, from 0 to 100. 100 is best quality, 0 is lowest quality. Not relevant for .png outputs"
279 | ),
280 | num_inference_steps: z
281 | .number()
282 | .int()
283 | .min(1)
284 | .max(4)
285 | .default(4)
286 | .describe(
287 | "Number of denoising steps. 4 is recommended, and lower number of steps produce lower quality outputs, faster."
288 | ),
289 | disable_safety_checker: z
290 | .boolean()
291 | .default(false)
292 | .describe("Disable safety checker for generated images."),
293 | support_image_mcp_response_type: z
294 | .boolean()
295 | .default(true)
296 | .describe("Support image MCP response type on client side"),
297 | };
298 | const imageVariantsGenerationObjectSchema = z.object(
299 | imageVariantsGenerationSchema
300 | );
301 | export type ImageVariantsGenerationParams = z.infer<
302 | typeof imageVariantsGenerationObjectSchema
303 | >;
304 |
```