sizzlebop commited on
Commit
e6992ba
·
1 Parent(s): 51fc219

`feat: Add image-to-image generation tools and transparent background support`

Browse files
CHANGELOG.md CHANGED
@@ -5,6 +5,32 @@ All notable changes to the MCPollinations will be documented in this file.
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  ## [1.1.3] - `2025-07-25`
9
 
10
  ### Added
 
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
 
8
+ ## [1.2.0] - `2025-07-25`
9
+
10
+ ### Added
11
+ - **Transparent Background Support**: Added `transparent` parameter for generating images with transparent backgrounds
12
+ - Works with `gptimage` model only
13
+ - Perfect for logos, icons, and graphics that need transparent backgrounds
14
+ - Added to both `generateImageUrl` and `generateImage` tools
15
+ - Configurable in MCP configuration with default settings
16
+ - **Image-to-Image Generation**: Two new powerful tools for working with existing images
17
+ - `editImage` tool: Edit or modify existing images based on text prompts (e.g., "remove the cat and add a dog", "change background to mountains")
18
+ - `generateImageFromReference` tool: Generate new images using existing images as reference (e.g., "make this into a cartoon", "create a painting version")
19
+ - Both tools support `gptimage` and `kontext` models
20
+ - Full parameter support including transparent backgrounds, custom dimensions, and file saving
21
+ - **Enhanced Configuration**: Updated configuration generator and example files
22
+ - Added transparent parameter configuration prompts
23
+ - Updated tool lists to include new image-to-image tools
24
+ - Enhanced model guidance and descriptions
25
+
26
+ ### Changed
27
+ - Updated tool schemas to include new transparent parameter
28
+ - Enhanced image generation capabilities with more flexible model options
29
+ - Improved tool descriptions for better AI assistant understanding
30
+
31
+ ### Fixed
32
+ - Ensured all new tools follow consistent parameter patterns and error handling
33
+
34
  ## [1.1.3] - `2025-07-25`
35
 
36
  ### Added
README.md CHANGED
@@ -192,13 +192,18 @@ If it shows a version lower than 16.0.0, consider upgrading for best compatibili
192
 
193
  The MCP server provides the following tools:
194
 
 
195
  1. `generateImageUrl` - Generates an image URL from a text prompt
196
  2. `generateImage` - Generates an image, returns it as base64-encoded data, and saves it to a file by default (PNG format)
197
- 3. `respondAudio` - Generates an audio response to a text prompt (customizable voice parameter)
198
- 4. `respondText` - Responds with text to a prompt using text models (customizable model parameter)
199
  5. `listImageModels` - Lists available models for image generation
200
- 6. `listTextModels` - Lists available models for text generation
201
- 7. `listAudioVoices` - Lists all available voices for audio generation
 
 
 
 
202
 
203
  ## Text Generation Details
204
 
@@ -244,6 +249,51 @@ In your MCP configuration, you can set defaults:
244
  }
245
  ```
246
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  ## Image Generation Details
248
 
249
  ### Default Behavior
@@ -272,6 +322,7 @@ const options = {
272
  seed: 12345, // Specific seed for reproducibility (defaults to random)
273
  enhance: true, // Enhance the prompt using an LLM before generating (defaults to true)
274
  safe: false, // Content filtering (defaults to false)
 
275
 
276
  // File saving options
277
  saveToFile: true, // Set to false to skip saving to disk
 
192
 
193
  The MCP server provides the following tools:
194
 
195
+ ### **Image Generation Tools**
196
  1. `generateImageUrl` - Generates an image URL from a text prompt
197
  2. `generateImage` - Generates an image, returns it as base64-encoded data, and saves it to a file by default (PNG format)
198
+ 3. `editImage` - **NEW!** Edit or modify existing images based on text prompts
199
+ 4. `generateImageFromReference` - **NEW!** Generate new images using existing images as reference
200
  5. `listImageModels` - Lists available models for image generation
201
+
202
+ ### **Text & Audio Tools**
203
+ 6. `respondText` - Responds with text to a prompt using text models (customizable parameters)
204
+ 7. `respondAudio` - Generates an audio response to a text prompt (customizable voice parameter)
205
+ 8. `listTextModels` - Lists available models for text generation
206
+ 9. `listAudioVoices` - Lists all available voices for audio generation
207
 
208
  ## Text Generation Details
209
 
 
249
  }
250
  ```
251
 
252
+ ## Image-to-Image Generation (NEW!)
253
+
254
+ MCPollinations now supports powerful image-to-image generation with two specialized tools:
255
+
256
+ ### **editImage Tool**
257
+ Perfect for modifying existing images:
258
+ - **Remove objects**: "remove the cat from this image"
259
+ - **Add elements**: "add a dog to this scene"
260
+ - **Change backgrounds**: "replace the background with mountains"
261
+ - **Style modifications**: "make the lighting more dramatic"
262
+
263
+ ### **generateImageFromReference Tool**
264
+ Perfect for creating variations and new styles:
265
+ - **Style transfer**: "make this photo look like a painting"
266
+ - **Format changes**: "convert this to a cartoon style"
267
+ - **Creative variations**: "create a futuristic version of this"
268
+ - **Artistic interpretations**: "make this look like a sketch"
269
+
270
+ ### **Supported Models**
271
+ - **`gptimage`**: Versatile model for both editing and reference generation
272
+ - **`kontext`**: Specialized model optimized for image-to-image tasks
273
+
274
+ ### **Example Usage**
275
+ ```javascript
276
+ // Edit an existing image
277
+ const editResult = await editImage(
278
+ "change the background to a sunset beach",
279
+ "https://example.com/photo.jpg",
280
+ "gptimage"
281
+ );
282
+
283
+ // Generate from reference
284
+ const referenceResult = await generateImageFromReference(
285
+ "make this into a watercolor painting",
286
+ "https://example.com/photo.jpg",
287
+ "kontext"
288
+ );
289
+ ```
290
+
291
+ ### **Transparent Backgrounds (NEW!)**
292
+ Generate images with transparent backgrounds using the `gptimage` model:
293
+ - Perfect for logos, icons, and graphics
294
+ - Set `transparent: true` in your requests
295
+ - Only works with the `gptimage` model
296
+
297
  ## Image Generation Details
298
 
299
  ### Default Behavior
 
322
  seed: 12345, // Specific seed for reproducibility (defaults to random)
323
  enhance: true, // Enhance the prompt using an LLM before generating (defaults to true)
324
  safe: false, // Content filtering (defaults to false)
325
+ transparent: false, // Generate with transparent background (gptimage model only)
326
 
327
  // File saving options
328
  saveToFile: true, // Set to false to skip saving to disk
example-mcp.json CHANGED
@@ -18,7 +18,8 @@
18
  "width": 1024,
19
  "height": 1024,
20
  "safe": false,
21
- "enhance": true
 
22
  },
23
  "text": {
24
  "model": "openai",
@@ -34,6 +35,8 @@
34
  "alwaysAllow": [
35
  "generateImageUrl",
36
  "generateImage",
 
 
37
  "listImageModels",
38
  "respondAudio",
39
  "listAudioVoices",
 
18
  "width": 1024,
19
  "height": 1024,
20
  "safe": false,
21
+ "enhance": true,
22
+ "transparent": false
23
  },
24
  "text": {
25
  "model": "openai",
 
35
  "alwaysAllow": [
36
  "generateImageUrl",
37
  "generateImage",
38
+ "editImage",
39
+ "generateImageFromReference",
40
  "listImageModels",
41
  "respondAudio",
42
  "listAudioVoices",
generate-mcp-config.js CHANGED
@@ -29,7 +29,8 @@ const defaultConfig = {
29
  "width": 1024,
30
  "height": 1024,
31
  "safe": false,
32
- "enhance": true
 
33
  },
34
  "text": {
35
  "model": "openai",
@@ -45,6 +46,8 @@ const defaultConfig = {
45
  "alwaysAllow": [
46
  "generateImageUrl",
47
  "generateImage",
 
 
48
  "listImageModels",
49
  "respondAudio",
50
  "listAudioVoices",
@@ -149,6 +152,9 @@ async function generateMcpConfig() {
149
 
150
  const imageEnhance = await promptYesNo('Enable prompt enhancement using LLM before image generation?', true);
151
  config[configKey].default_params.image.enhance = imageEnhance;
 
 
 
152
  }
153
 
154
  // Text parameters
@@ -191,6 +197,8 @@ async function generateMcpConfig() {
191
  const allTools = [
192
  'generateImageUrl',
193
  'generateImage',
 
 
194
  'listImageModels',
195
  'respondAudio',
196
  'listAudioVoices',
 
29
  "width": 1024,
30
  "height": 1024,
31
  "safe": false,
32
+ "enhance": true,
33
+ "transparent": false
34
  },
35
  "text": {
36
  "model": "openai",
 
46
  "alwaysAllow": [
47
  "generateImageUrl",
48
  "generateImage",
49
+ "editImage",
50
+ "generateImageFromReference",
51
  "listImageModels",
52
  "respondAudio",
53
  "listAudioVoices",
 
152
 
153
  const imageEnhance = await promptYesNo('Enable prompt enhancement using LLM before image generation?', true);
154
  config[configKey].default_params.image.enhance = imageEnhance;
155
+
156
+ const imageTransparent = await promptYesNo('Enable transparent background by default? (gptimage model only, default: false)', false);
157
+ config[configKey].default_params.image.transparent = imageTransparent;
158
  }
159
 
160
  // Text parameters
 
197
  const allTools = [
198
  'generateImageUrl',
199
  'generateImage',
200
+ 'editImage',
201
+ 'generateImageFromReference',
202
  'listImageModels',
203
  'respondAudio',
204
  'listAudioVoices',
package.json CHANGED
@@ -1,6 +1,6 @@
1
  {
2
  "name": "@pinkpixel/mcpollinations",
3
- "version": "1.1.3",
4
  "description": "Model Context Protocol (MCP) server for the Pollinations APIs with image saving functionality.",
5
  "type": "module",
6
  "bin": {
 
1
  {
2
  "name": "@pinkpixel/mcpollinations",
3
+ "version": "1.2.0",
4
  "description": "Model Context Protocol (MCP) server for the Pollinations APIs with image saving functionality.",
5
  "type": "module",
6
  "bin": {
pollinations-mcp-server.js CHANGED
@@ -86,6 +86,8 @@ import {
86
  import {
87
  generateImageUrl,
88
  generateImage,
 
 
89
  respondAudio,
90
  listImageModels,
91
  listTextModels,
@@ -125,7 +127,7 @@ if (finalAuthConfig) {
125
  const server = new Server(
126
  {
127
  name: '@pinkpixel/mcpollinations',
128
- version: '1.1.3',
129
  },
130
  {
131
  capabilities: {
@@ -153,8 +155,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
153
 
154
  if (name === 'generateImageUrl') {
155
  try {
156
- const { prompt, model = 'flux', seed, width = 1024, height = 1024, enhance = true, safe = false } = args;
157
- const result = await generateImageUrl(prompt, model, seed, width, height, enhance, safe, finalAuthConfig);
158
  return {
159
  content: [
160
  { type: 'text', text: JSON.stringify(result, null, 2) }
@@ -170,8 +172,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
170
  }
171
  } else if (name === 'generateImage') {
172
  try {
173
- const { prompt, model = 'flux', seed, width = 1024, height = 1024, enhance = true, safe = false, outputPath = './mcpollinations-output', fileName = '', format = 'png' } = args;
174
- const result = await generateImage(prompt, model, seed, width, height, enhance, safe, outputPath, fileName, format, finalAuthConfig);
175
 
176
  // Prepare the response content
177
  const content = [
@@ -308,6 +310,79 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
308
  };
309
  }
310
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
311
 
312
  } else {
313
  throw new McpError(
 
86
  import {
87
  generateImageUrl,
88
  generateImage,
89
+ editImage,
90
+ generateImageFromReference,
91
  respondAudio,
92
  listImageModels,
93
  listTextModels,
 
127
  const server = new Server(
128
  {
129
  name: '@pinkpixel/mcpollinations',
130
+ version: '1.2.0',
131
  },
132
  {
133
  capabilities: {
 
155
 
156
  if (name === 'generateImageUrl') {
157
  try {
158
+ const { prompt, model = 'flux', seed, width = 1024, height = 1024, enhance = true, safe = false, transparent = false } = args;
159
+ const result = await generateImageUrl(prompt, model, seed, width, height, enhance, safe, transparent, finalAuthConfig);
160
  return {
161
  content: [
162
  { type: 'text', text: JSON.stringify(result, null, 2) }
 
172
  }
173
  } else if (name === 'generateImage') {
174
  try {
175
+ const { prompt, model = 'flux', seed, width = 1024, height = 1024, enhance = true, safe = false, transparent = false, outputPath = './mcpollinations-output', fileName = '', format = 'png' } = args;
176
+ const result = await generateImage(prompt, model, seed, width, height, enhance, safe, transparent, outputPath, fileName, format, finalAuthConfig);
177
 
178
  // Prepare the response content
179
  const content = [
 
310
  };
311
  }
312
 
313
+ } else if (name === 'editImage') {
314
+ try {
315
+ const { prompt, imageUrl, model = 'gptimage', seed, width = 1024, height = 1024, enhance = true, safe = false, transparent = false, outputPath = './mcpollinations-output', fileName = '', format = 'png' } = args;
316
+ const result = await editImage(prompt, imageUrl, model, seed, width, height, enhance, safe, transparent, outputPath, fileName, format, finalAuthConfig);
317
+
318
+ // Prepare the response content
319
+ const content = [
320
+ {
321
+ type: 'image',
322
+ data: result.data,
323
+ mimeType: result.mimeType
324
+ }
325
+ ];
326
+
327
+ // Prepare the response text
328
+ let responseText = `Edited image from prompt: "${prompt}"\nInput image: ${imageUrl}\n\nImage metadata: ${JSON.stringify(result.metadata, null, 2)}`;
329
+
330
+ // Add file path information if the image was saved to a file
331
+ if (result.filePath) {
332
+ responseText += `\n\nImage saved to: ${result.filePath}`;
333
+ }
334
+
335
+ content.push({
336
+ type: 'text',
337
+ text: responseText
338
+ });
339
+
340
+ return { content };
341
+ } catch (error) {
342
+ return {
343
+ content: [
344
+ { type: 'text', text: `Error editing image: ${error.message}` }
345
+ ],
346
+ isError: true
347
+ };
348
+ }
349
+
350
+ } else if (name === 'generateImageFromReference') {
351
+ try {
352
+ const { prompt, imageUrl, model = 'gptimage', seed, width = 1024, height = 1024, enhance = true, safe = false, transparent = false, outputPath = './mcpollinations-output', fileName = '', format = 'png' } = args;
353
+ const result = await generateImageFromReference(prompt, imageUrl, model, seed, width, height, enhance, safe, transparent, outputPath, fileName, format, finalAuthConfig);
354
+
355
+ // Prepare the response content
356
+ const content = [
357
+ {
358
+ type: 'image',
359
+ data: result.data,
360
+ mimeType: result.mimeType
361
+ }
362
+ ];
363
+
364
+ // Prepare the response text
365
+ let responseText = `Generated image from reference: "${prompt}"\nReference image: ${imageUrl}\n\nImage metadata: ${JSON.stringify(result.metadata, null, 2)}`;
366
+
367
+ // Add file path information if the image was saved to a file
368
+ if (result.filePath) {
369
+ responseText += `\n\nImage saved to: ${result.filePath}`;
370
+ }
371
+
372
+ content.push({
373
+ type: 'text',
374
+ text: responseText
375
+ });
376
+
377
+ return { content };
378
+ } catch (error) {
379
+ return {
380
+ content: [
381
+ { type: 'text', text: `Error generating image from reference: ${error.message}` }
382
+ ],
383
+ isError: true
384
+ };
385
+ }
386
 
387
  } else {
388
  throw new McpError(
src/index.js CHANGED
@@ -5,7 +5,7 @@
5
  */
6
 
7
  // Import services
8
- import { generateImageUrl, generateImage, listImageModels } from './services/imageService.js';
9
  import { respondAudio, listAudioVoices } from './services/audioService.js';
10
  import { respondText, listTextModels } from './services/textService.js';
11
 
@@ -15,6 +15,8 @@ export {
15
  // Image services
16
  generateImageUrl,
17
  generateImage,
 
 
18
  listImageModels,
19
 
20
  // Audio services
 
5
  */
6
 
7
  // Import services
8
+ import { generateImageUrl, generateImage, editImage, generateImageFromReference, listImageModels } from './services/imageService.js';
9
  import { respondAudio, listAudioVoices } from './services/audioService.js';
10
  import { respondText, listTextModels } from './services/textService.js';
11
 
 
15
  // Image services
16
  generateImageUrl,
17
  generateImage,
18
+ editImage,
19
+ generateImageFromReference,
20
  listImageModels,
21
 
22
  // Audio services
src/schemas.js CHANGED
@@ -2,7 +2,7 @@
2
  * Central export for all schema definitions
3
  */
4
 
5
- import { generateImageUrlSchema, generateImageSchema, listImageModelsSchema } from './services/imageSchema.js';
6
  import { respondAudioSchema, listAudioVoicesSchema } from './services/audioSchema.js';
7
  import { respondTextSchema, listTextModelsSchema } from './services/textSchema.js';
8
 
@@ -12,6 +12,8 @@ export {
12
  // Image schemas
13
  generateImageUrlSchema,
14
  generateImageSchema,
 
 
15
  listImageModelsSchema,
16
 
17
  // Audio schemas
@@ -31,6 +33,8 @@ export function getAllToolSchemas() {
31
  return [
32
  generateImageUrlSchema,
33
  generateImageSchema,
 
 
34
  listImageModelsSchema,
35
  respondAudioSchema,
36
  listAudioVoicesSchema,
 
2
  * Central export for all schema definitions
3
  */
4
 
5
+ import { generateImageUrlSchema, generateImageSchema, editImageSchema, generateImageFromReferenceSchema, listImageModelsSchema } from './services/imageSchema.js';
6
  import { respondAudioSchema, listAudioVoicesSchema } from './services/audioSchema.js';
7
  import { respondTextSchema, listTextModelsSchema } from './services/textSchema.js';
8
 
 
12
  // Image schemas
13
  generateImageUrlSchema,
14
  generateImageSchema,
15
+ editImageSchema,
16
+ generateImageFromReferenceSchema,
17
  listImageModelsSchema,
18
 
19
  // Audio schemas
 
33
  return [
34
  generateImageUrlSchema,
35
  generateImageSchema,
36
+ editImageSchema,
37
+ generateImageFromReferenceSchema,
38
  listImageModelsSchema,
39
  respondAudioSchema,
40
  listAudioVoicesSchema,
src/services/imageSchema.js CHANGED
@@ -38,6 +38,10 @@ export const generateImageUrlSchema = {
38
  safe: {
39
  type: 'boolean',
40
  description: 'Whether to apply content filtering (default: false)'
 
 
 
 
41
  }
42
  },
43
  required: ['prompt']
@@ -81,6 +85,10 @@ export const generateImageSchema = {
81
  type: 'boolean',
82
  description: 'Whether to apply content filtering (default: false)'
83
  },
 
 
 
 
84
  outputPath: {
85
  type: 'string',
86
  description: 'Directory path where to save the image (default: "./mcpollinations-output")'
@@ -109,3 +117,127 @@ export const listImageModelsSchema = {
109
  properties: {}
110
  }
111
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  safe: {
39
  type: 'boolean',
40
  description: 'Whether to apply content filtering (default: false)'
41
+ },
42
+ transparent: {
43
+ type: 'boolean',
44
+ description: 'Generate image with transparent background (gptimage model only, default: false)'
45
  }
46
  },
47
  required: ['prompt']
 
85
  type: 'boolean',
86
  description: 'Whether to apply content filtering (default: false)'
87
  },
88
+ transparent: {
89
+ type: 'boolean',
90
+ description: 'Generate image with transparent background (gptimage model only, default: false)'
91
+ },
92
  outputPath: {
93
  type: 'string',
94
  description: 'Directory path where to save the image (default: "./mcpollinations-output")'
 
117
  properties: {}
118
  }
119
  };
120
+
121
+ /**
122
+ * Schema for the editImage tool
123
+ */
124
+ export const editImageSchema = {
125
+ name: 'editImage',
126
+ description: 'Edit or modify an existing image based on a text prompt. User-configured settings in MCP config will be used as defaults unless specifically overridden.',
127
+ inputSchema: {
128
+ type: 'object',
129
+ properties: {
130
+ prompt: {
131
+ type: 'string',
132
+ description: 'The text description of how to edit the image (e.g., "remove the cat and add a dog", "change background to mountains")'
133
+ },
134
+ imageUrl: {
135
+ type: 'string',
136
+ description: 'URL of the input image to edit'
137
+ },
138
+ model: {
139
+ type: 'string',
140
+ description: 'Model name to use for editing (default: user config or "gptimage"). Available: "gptimage", "kontext"'
141
+ },
142
+ seed: {
143
+ type: 'number',
144
+ description: 'Seed for reproducible results (default: random)'
145
+ },
146
+ width: {
147
+ type: 'number',
148
+ description: 'Width of the generated image (default: 1024)'
149
+ },
150
+ height: {
151
+ type: 'number',
152
+ description: 'Height of the generated image (default: 1024)'
153
+ },
154
+ enhance: {
155
+ type: 'boolean',
156
+ description: 'Whether to enhance the prompt using an LLM before generating (default: true)'
157
+ },
158
+ safe: {
159
+ type: 'boolean',
160
+ description: 'Whether to apply content filtering (default: false)'
161
+ },
162
+ transparent: {
163
+ type: 'boolean',
164
+ description: 'Generate image with transparent background (gptimage model only, default: false)'
165
+ },
166
+ outputPath: {
167
+ type: 'string',
168
+ description: 'Directory path where to save the image (default: user config or "./mcpollinations-output")'
169
+ },
170
+ fileName: {
171
+ type: 'string',
172
+ description: 'Name of the file to save (without extension, default: generated from prompt)'
173
+ },
174
+ format: {
175
+ type: 'string',
176
+ description: 'Image format to save as (png, jpeg, jpg, webp - default: png)'
177
+ }
178
+ },
179
+ required: ['prompt', 'imageUrl']
180
+ }
181
+ };
182
+
183
+ /**
184
+ * Schema for the generateImageFromReference tool
185
+ */
186
+ export const generateImageFromReferenceSchema = {
187
+ name: 'generateImageFromReference',
188
+ description: 'Generate a new image using an existing image as reference. User-configured settings in MCP config will be used as defaults unless specifically overridden.',
189
+ inputSchema: {
190
+ type: 'object',
191
+ properties: {
192
+ prompt: {
193
+ type: 'string',
194
+ description: 'The text description of what to generate based on the reference image (e.g., "create a cartoon version", "make it look like a painting")'
195
+ },
196
+ imageUrl: {
197
+ type: 'string',
198
+ description: 'URL of the reference image to base the generation on'
199
+ },
200
+ model: {
201
+ type: 'string',
202
+ description: 'Model name to use for generation (default: user config or "gptimage"). Available: "gptimage", "kontext"'
203
+ },
204
+ seed: {
205
+ type: 'number',
206
+ description: 'Seed for reproducible results (default: random)'
207
+ },
208
+ width: {
209
+ type: 'number',
210
+ description: 'Width of the generated image (default: 1024)'
211
+ },
212
+ height: {
213
+ type: 'number',
214
+ description: 'Height of the generated image (default: 1024)'
215
+ },
216
+ enhance: {
217
+ type: 'boolean',
218
+ description: 'Whether to enhance the prompt using an LLM before generating (default: true)'
219
+ },
220
+ safe: {
221
+ type: 'boolean',
222
+ description: 'Whether to apply content filtering (default: false)'
223
+ },
224
+ transparent: {
225
+ type: 'boolean',
226
+ description: 'Generate image with transparent background (gptimage model only, default: false)'
227
+ },
228
+ outputPath: {
229
+ type: 'string',
230
+ description: 'Directory path where to save the image (default: user config or "./mcpollinations-output")'
231
+ },
232
+ fileName: {
233
+ type: 'string',
234
+ description: 'Name of the file to save (without extension, default: generated from prompt)'
235
+ },
236
+ format: {
237
+ type: 'string',
238
+ description: 'Image format to save as (png, jpeg, jpg, webp - default: png)'
239
+ }
240
+ },
241
+ required: ['prompt', 'imageUrl']
242
+ }
243
+ };
src/services/imageService.js CHANGED
@@ -14,11 +14,12 @@
14
  * @param {number} [height=1024] - Height of the generated image
15
  * @param {boolean} [enhance=true] - Whether to enhance the prompt using an LLM before generating
16
  * @param {boolean} [safe=false] - Whether to apply content filtering
 
17
  * @param {Object} [authConfig] - Optional authentication configuration {token, referrer}
18
  * @returns {Object} - Object containing the image URL and metadata
19
  * @note Always includes nologo=true and private=true parameters
20
  */
21
- export async function generateImageUrl(prompt, model = 'flux', seed = Math.floor(Math.random() * 1000000), width = 1024, height = 1024, enhance = true, safe = false, authConfig = null) {
22
  if (!prompt || typeof prompt !== 'string') {
23
  throw new Error('Prompt is required and must be a string');
24
  }
@@ -43,6 +44,7 @@ export async function generateImageUrl(prompt, model = 'flux', seed = Math.floor
43
  queryParams.append('nologo', 'true'); // Always set nologo to true
44
  queryParams.append('private', 'true'); // Always set private to true)
45
  queryParams.append('safe', safe.toString()); // Use the customizable safe parameter
 
46
 
47
  // Construct the URL
48
  const encodedPrompt = encodeURIComponent(prompt);
@@ -64,7 +66,8 @@ export async function generateImageUrl(prompt, model = 'flux', seed = Math.floor
64
  enhance,
65
  private: true,
66
  nologo: true,
67
- safe
 
68
  };
69
  }
70
 
@@ -79,6 +82,7 @@ export async function generateImageUrl(prompt, model = 'flux', seed = Math.floor
79
  * @param {number} [height=1024] - Height of the generated image
80
  * @param {boolean} [enhance=true] - Whether to enhance the prompt using an LLM before generating
81
  * @param {boolean} [safe=false] - Whether to apply content filtering
 
82
  * @param {string} [outputPath='./mcpollinations-output'] - Directory path where to save the image
83
  * @param {string} [fileName] - Name of the file to save (without extension)
84
  * @param {string} [format='png'] - Image format to save as (png, jpeg, jpg, webp)
@@ -86,13 +90,13 @@ export async function generateImageUrl(prompt, model = 'flux', seed = Math.floor
86
  * @returns {Promise<Object>} - Object containing the base64 image data, mime type, metadata, and file path if saved
87
  * @note Always includes nologo=true and private=true parameters
88
  */
89
- export async function generateImage(prompt, model = 'flux', seed = Math.floor(Math.random() * 1000000), width = 1024, height = 1024, enhance = true, safe = false, outputPath = './mcpollinations-output', fileName = '', format = 'png', authConfig = null) {
90
  if (!prompt || typeof prompt !== 'string') {
91
  throw new Error('Prompt is required and must be a string');
92
  }
93
 
94
  // First, generate the image URL
95
- const urlResult = await generateImageUrl(prompt, model, seed, width, height, enhance, safe, authConfig);
96
 
97
  try {
98
  // Prepare fetch options with optional auth headers
@@ -136,7 +140,8 @@ export async function generateImage(prompt, model = 'flux', seed = Math.floor(Ma
136
  enhance: urlResult.enhance,
137
  private: urlResult.private,
138
  nologo: urlResult.nologo,
139
- safe: urlResult.safe
 
140
  }
141
  };
142
 
@@ -193,6 +198,310 @@ export async function generateImage(prompt, model = 'flux', seed = Math.floor(Ma
193
  }
194
  }
195
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  /**
197
  * List available image generation models from Pollinations API
198
  *
 
14
  * @param {number} [height=1024] - Height of the generated image
15
  * @param {boolean} [enhance=true] - Whether to enhance the prompt using an LLM before generating
16
  * @param {boolean} [safe=false] - Whether to apply content filtering
17
+ * @param {boolean} [transparent=false] - Generate image with transparent background (gptimage model only)
18
  * @param {Object} [authConfig] - Optional authentication configuration {token, referrer}
19
  * @returns {Object} - Object containing the image URL and metadata
20
  * @note Always includes nologo=true and private=true parameters
21
  */
22
+ export async function generateImageUrl(prompt, model = 'flux', seed = Math.floor(Math.random() * 1000000), width = 1024, height = 1024, enhance = true, safe = false, transparent = false, authConfig = null) {
23
  if (!prompt || typeof prompt !== 'string') {
24
  throw new Error('Prompt is required and must be a string');
25
  }
 
44
  queryParams.append('nologo', 'true'); // Always set nologo to true
45
  queryParams.append('private', 'true'); // Always set private to true)
46
  queryParams.append('safe', safe.toString()); // Use the customizable safe parameter
47
+ if (transparent) queryParams.append('transparent', 'true'); // Add transparent parameter if true
48
 
49
  // Construct the URL
50
  const encodedPrompt = encodeURIComponent(prompt);
 
66
  enhance,
67
  private: true,
68
  nologo: true,
69
+ safe,
70
+ transparent
71
  };
72
  }
73
 
 
82
  * @param {number} [height=1024] - Height of the generated image
83
  * @param {boolean} [enhance=true] - Whether to enhance the prompt using an LLM before generating
84
  * @param {boolean} [safe=false] - Whether to apply content filtering
85
+ * @param {boolean} [transparent=false] - Generate image with transparent background (gptimage model only)
86
  * @param {string} [outputPath='./mcpollinations-output'] - Directory path where to save the image
87
  * @param {string} [fileName] - Name of the file to save (without extension)
88
  * @param {string} [format='png'] - Image format to save as (png, jpeg, jpg, webp)
 
90
  * @returns {Promise<Object>} - Object containing the base64 image data, mime type, metadata, and file path if saved
91
  * @note Always includes nologo=true and private=true parameters
92
  */
93
+ export async function generateImage(prompt, model = 'flux', seed = Math.floor(Math.random() * 1000000), width = 1024, height = 1024, enhance = true, safe = false, transparent = false, outputPath = './mcpollinations-output', fileName = '', format = 'png', authConfig = null) {
94
  if (!prompt || typeof prompt !== 'string') {
95
  throw new Error('Prompt is required and must be a string');
96
  }
97
 
98
  // First, generate the image URL
99
+ const urlResult = await generateImageUrl(prompt, model, seed, width, height, enhance, safe, transparent, authConfig);
100
 
101
  try {
102
  // Prepare fetch options with optional auth headers
 
140
  enhance: urlResult.enhance,
141
  private: urlResult.private,
142
  nologo: urlResult.nologo,
143
+ safe: urlResult.safe,
144
+ transparent: urlResult.transparent
145
  }
146
  };
147
 
 
198
  }
199
  }
200
 
201
+ /**
202
+ * Edits or modifies an existing image based on a text prompt
203
+ *
204
+ * @param {string} prompt - The text description of how to edit the image
205
+ * @param {string} imageUrl - URL of the input image to edit
206
+ * @param {string} [model='gptimage'] - Model name to use for editing (gptimage or kontext)
207
+ * @param {number} [seed] - Seed for reproducible results (defaults to random if not specified)
208
+ * @param {number} [width=1024] - Width of the generated image
209
+ * @param {number} [height=1024] - Height of the generated image
210
+ * @param {boolean} [enhance=true] - Whether to enhance the prompt using an LLM before generating
211
+ * @param {boolean} [safe=false] - Whether to apply content filtering
212
+ * @param {boolean} [transparent=false] - Generate image with transparent background (gptimage model only)
213
+ * @param {string} [outputPath='./mcpollinations-output'] - Directory path where to save the image
214
+ * @param {string} [fileName] - Name of the file to save (without extension)
215
+ * @param {string} [format='png'] - Image format to save as (png, jpeg, jpg, webp)
216
+ * @param {Object} [authConfig] - Optional authentication configuration {token, referrer}
217
+ * @returns {Promise<Object>} - Object containing the base64 image data, mime type, metadata, and file path if saved
218
+ * @note Always includes nologo=true and private=true parameters
219
+ */
220
+ export async function editImage(prompt, imageUrl, model = 'gptimage', seed = Math.floor(Math.random() * 1000000), width = 1024, height = 1024, enhance = true, safe = false, transparent = false, outputPath = './mcpollinations-output', fileName = '', format = 'png', authConfig = null) {
221
+ if (!prompt || typeof prompt !== 'string') {
222
+ throw new Error('Prompt is required and must be a string');
223
+ }
224
+
225
+ if (!imageUrl || typeof imageUrl !== 'string') {
226
+ throw new Error('Image URL is required and must be a string');
227
+ }
228
+
229
+ // Build the query parameters
230
+ const queryParams = new URLSearchParams();
231
+ queryParams.append('model', model);
232
+ queryParams.append('image', imageUrl); // Add the input image URL
233
+ if (seed !== undefined) queryParams.append('seed', seed);
234
+ if (width !== 1024) queryParams.append('width', width);
235
+ if (height !== 1024) queryParams.append('height', height);
236
+
237
+ // Add enhance parameter if true
238
+ if (enhance) queryParams.append('enhance', 'true');
239
+
240
+ // Add parameters
241
+ queryParams.append('nologo', 'true'); // Always set nologo to true
242
+ queryParams.append('private', 'true'); // Always set private to true)
243
+ queryParams.append('safe', safe.toString()); // Use the customizable safe parameter
244
+ if (transparent) queryParams.append('transparent', 'true'); // Add transparent parameter if true
245
+
246
+ // Construct the URL
247
+ const encodedPrompt = encodeURIComponent(prompt);
248
+ const baseUrl = 'https://image.pollinations.ai';
249
+ let url = `${baseUrl}/prompt/${encodedPrompt}`;
250
+
251
+ // Add query parameters
252
+ const queryString = queryParams.toString();
253
+ url += `?${queryString}`;
254
+
255
+ try {
256
+ // Prepare fetch options with optional auth headers
257
+ const fetchOptions = {};
258
+ if (authConfig) {
259
+ fetchOptions.headers = {};
260
+ if (authConfig.token) {
261
+ fetchOptions.headers['Authorization'] = `Bearer ${authConfig.token}`;
262
+ }
263
+ if (authConfig.referrer) {
264
+ fetchOptions.headers['Referer'] = authConfig.referrer;
265
+ }
266
+ }
267
+
268
+ // Fetch the image from the URL
269
+ const response = await fetch(url, fetchOptions);
270
+
271
+ if (!response.ok) {
272
+ throw new Error(`Failed to edit image: ${response.statusText}`);
273
+ }
274
+
275
+ // Get the image data as an ArrayBuffer
276
+ const imageBuffer = await response.arrayBuffer();
277
+
278
+ // Convert the ArrayBuffer to a base64 string
279
+ const base64Data = Buffer.from(imageBuffer).toString('base64');
280
+
281
+ // Determine the mime type from the response headers or default to image/jpeg
282
+ const contentType = response.headers.get('content-type') || 'image/jpeg';
283
+
284
+ // Prepare the result object
285
+ const result = {
286
+ data: base64Data,
287
+ mimeType: contentType,
288
+ metadata: {
289
+ prompt,
290
+ inputImageUrl: imageUrl,
291
+ width,
292
+ height,
293
+ model,
294
+ seed,
295
+ enhance,
296
+ private: true,
297
+ nologo: true,
298
+ safe,
299
+ transparent
300
+ }
301
+ };
302
+
303
+ // Always save the image to a file
304
+ // Import required modules
305
+ const fs = await import('fs');
306
+ const path = await import('path');
307
+
308
+ // Create the output directory if it doesn't exist
309
+ if (!fs.existsSync(outputPath)) {
310
+ fs.mkdirSync(outputPath, { recursive: true });
311
+ }
312
+
313
+ // Generate a filename if not provided
314
+ let finalFileName = fileName;
315
+ if (!finalFileName) {
316
+ // Create a filename from the prompt (first 20 characters) and timestamp
317
+ const sanitizedPrompt = prompt.replace(/[^a-zA-Z0-9]/g, '_').substring(0, 20);
318
+ const timestamp = Date.now();
319
+ const randomSuffix = Math.floor(Math.random() * 1000);
320
+ finalFileName = `edited_${sanitizedPrompt}_${timestamp}_${randomSuffix}`;
321
+ }
322
+
323
+ // Ensure the filename has the correct extension
324
+ const extension = format.toLowerCase();
325
+ if (!finalFileName.endsWith(`.${extension}`)) {
326
+ finalFileName += `.${extension}`;
327
+ }
328
+
329
+ // Check if file already exists and add a number suffix if needed
330
+ let finalFilePath = path.join(outputPath, finalFileName);
331
+ let counter = 1;
332
+ while (fs.existsSync(finalFilePath)) {
333
+ const nameWithoutExt = finalFileName.replace(`.${extension}`, '');
334
+ const numberedFileName = `${nameWithoutExt}_${counter}.${extension}`;
335
+ finalFilePath = path.join(outputPath, numberedFileName);
336
+ counter++;
337
+ }
338
+
339
+ // Write the image data to the file
340
+ fs.writeFileSync(finalFilePath, Buffer.from(base64Data, 'base64'));
341
+
342
+ // Add the file path to the result
343
+ result.filePath = finalFilePath;
344
+
345
+ return result;
346
+
347
+ } catch (error) {
348
+ console.error('Error editing image:', error);
349
+ throw error;
350
+ }
351
+ }
352
+
353
+ /**
354
+ * Generates a new image using an existing image as reference
355
+ *
356
+ * @param {string} prompt - The text description of what to generate based on the reference image
357
+ * @param {string} imageUrl - URL of the reference image
358
+ * @param {string} [model='gptimage'] - Model name to use for generation (gptimage or kontext)
359
+ * @param {number} [seed] - Seed for reproducible results (defaults to random if not specified)
360
+ * @param {number} [width=1024] - Width of the generated image
361
+ * @param {number} [height=1024] - Height of the generated image
362
+ * @param {boolean} [enhance=true] - Whether to enhance the prompt using an LLM before generating
363
+ * @param {boolean} [safe=false] - Whether to apply content filtering
364
+ * @param {boolean} [transparent=false] - Generate image with transparent background (gptimage model only)
365
+ * @param {string} [outputPath='./mcpollinations-output'] - Directory path where to save the image
366
+ * @param {string} [fileName] - Name of the file to save (without extension)
367
+ * @param {string} [format='png'] - Image format to save as (png, jpeg, jpg, webp)
368
+ * @param {Object} [authConfig] - Optional authentication configuration {token, referrer}
369
+ * @returns {Promise<Object>} - Object containing the base64 image data, mime type, metadata, and file path if saved
370
+ * @note Always includes nologo=true and private=true parameters
371
+ */
372
+ export async function generateImageFromReference(prompt, imageUrl, model = 'gptimage', seed = Math.floor(Math.random() * 1000000), width = 1024, height = 1024, enhance = true, safe = false, transparent = false, outputPath = './mcpollinations-output', fileName = '', format = 'png', authConfig = null) {
373
+ if (!prompt || typeof prompt !== 'string') {
374
+ throw new Error('Prompt is required and must be a string');
375
+ }
376
+
377
+ if (!imageUrl || typeof imageUrl !== 'string') {
378
+ throw new Error('Reference image URL is required and must be a string');
379
+ }
380
+
381
+ // Build the query parameters
382
+ const queryParams = new URLSearchParams();
383
+ queryParams.append('model', model);
384
+ queryParams.append('image', imageUrl); // Add the reference image URL
385
+ if (seed !== undefined) queryParams.append('seed', seed);
386
+ if (width !== 1024) queryParams.append('width', width);
387
+ if (height !== 1024) queryParams.append('height', height);
388
+
389
+ // Add enhance parameter if true
390
+ if (enhance) queryParams.append('enhance', 'true');
391
+
392
+ // Add parameters
393
+ queryParams.append('nologo', 'true'); // Always set nologo to true
394
+ queryParams.append('private', 'true'); // Always set private to true)
395
+ queryParams.append('safe', safe.toString()); // Use the customizable safe parameter
396
+ if (transparent) queryParams.append('transparent', 'true'); // Add transparent parameter if true
397
+
398
+ // Construct the URL
399
+ const encodedPrompt = encodeURIComponent(prompt);
400
+ const baseUrl = 'https://image.pollinations.ai';
401
+ let url = `${baseUrl}/prompt/${encodedPrompt}`;
402
+
403
+ // Add query parameters
404
+ const queryString = queryParams.toString();
405
+ url += `?${queryString}`;
406
+
407
+ try {
408
+ // Prepare fetch options with optional auth headers
409
+ const fetchOptions = {};
410
+ if (authConfig) {
411
+ fetchOptions.headers = {};
412
+ if (authConfig.token) {
413
+ fetchOptions.headers['Authorization'] = `Bearer ${authConfig.token}`;
414
+ }
415
+ if (authConfig.referrer) {
416
+ fetchOptions.headers['Referer'] = authConfig.referrer;
417
+ }
418
+ }
419
+
420
+ // Fetch the image from the URL
421
+ const response = await fetch(url, fetchOptions);
422
+
423
+ if (!response.ok) {
424
+ throw new Error(`Failed to generate image from reference: ${response.statusText}`);
425
+ }
426
+
427
+ // Get the image data as an ArrayBuffer
428
+ const imageBuffer = await response.arrayBuffer();
429
+
430
+ // Convert the ArrayBuffer to a base64 string
431
+ const base64Data = Buffer.from(imageBuffer).toString('base64');
432
+
433
+ // Determine the mime type from the response headers or default to image/jpeg
434
+ const contentType = response.headers.get('content-type') || 'image/jpeg';
435
+
436
+ // Prepare the result object
437
+ const result = {
438
+ data: base64Data,
439
+ mimeType: contentType,
440
+ metadata: {
441
+ prompt,
442
+ referenceImageUrl: imageUrl,
443
+ width,
444
+ height,
445
+ model,
446
+ seed,
447
+ enhance,
448
+ private: true,
449
+ nologo: true,
450
+ safe,
451
+ transparent
452
+ }
453
+ };
454
+
455
+ // Always save the image to a file
456
+ // Import required modules
457
+ const fs = await import('fs');
458
+ const path = await import('path');
459
+
460
+ // Create the output directory if it doesn't exist
461
+ if (!fs.existsSync(outputPath)) {
462
+ fs.mkdirSync(outputPath, { recursive: true });
463
+ }
464
+
465
+ // Generate a filename if not provided
466
+ let finalFileName = fileName;
467
+ if (!finalFileName) {
468
+ // Create a filename from the prompt (first 20 characters) and timestamp
469
+ const sanitizedPrompt = prompt.replace(/[^a-zA-Z0-9]/g, '_').substring(0, 20);
470
+ const timestamp = Date.now();
471
+ const randomSuffix = Math.floor(Math.random() * 1000);
472
+ finalFileName = `reference_${sanitizedPrompt}_${timestamp}_${randomSuffix}`;
473
+ }
474
+
475
+ // Ensure the filename has the correct extension
476
+ const extension = format.toLowerCase();
477
+ if (!finalFileName.endsWith(`.${extension}`)) {
478
+ finalFileName += `.${extension}`;
479
+ }
480
+
481
+ // Check if file already exists and add a number suffix if needed
482
+ let finalFilePath = path.join(outputPath, finalFileName);
483
+ let counter = 1;
484
+ while (fs.existsSync(finalFilePath)) {
485
+ const nameWithoutExt = finalFileName.replace(`.${extension}`, '');
486
+ const numberedFileName = `${nameWithoutExt}_${counter}.${extension}`;
487
+ finalFilePath = path.join(outputPath, numberedFileName);
488
+ counter++;
489
+ }
490
+
491
+ // Write the image data to the file
492
+ fs.writeFileSync(finalFilePath, Buffer.from(base64Data, 'base64'));
493
+
494
+ // Add the file path to the result
495
+ result.filePath = finalFilePath;
496
+
497
+ return result;
498
+
499
+ } catch (error) {
500
+ console.error('Error generating image from reference:', error);
501
+ throw error;
502
+ }
503
+ }
504
+
505
  /**
506
  * List available image generation models from Pollinations API
507
  *
transparent-logo-test.png ADDED