ultimate-gemini-mcp 1.0.2__py3-none-any.whl → 1.0.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ultimate-gemini-mcp might be problematic. Click here for more details.

src/__init__.py CHANGED
@@ -7,7 +7,7 @@ A unified MCP server that combines the best features from:
7
7
  - Advanced features: batch processing, editing, templates, and more
8
8
  """
9
9
 
10
- __version__ = "1.0.2"
10
+ __version__ = "1.0.4"
11
11
  __author__ = "Ultimate Gemini MCP"
12
12
 
13
13
  from .config import get_settings
src/config/constants.py CHANGED
@@ -31,13 +31,13 @@ DEFAULT_ENHANCEMENT_MODEL = "gemini-flash-latest"
31
31
 
32
32
  # Aspect ratios
33
33
  ASPECT_RATIOS = [
34
- "1:1", # Square
35
- "2:3", # Portrait
36
- "3:2", # Landscape
37
- "3:4", # Portrait
38
- "4:3", # Standard landscape
39
- "4:5", # Portrait
40
- "5:4", # Landscape
34
+ "1:1", # Square
35
+ "2:3", # Portrait
36
+ "3:2", # Landscape
37
+ "3:4", # Portrait
38
+ "4:3", # Standard landscape
39
+ "4:5", # Portrait
40
+ "5:4", # Landscape
41
41
  "9:16", # Vertical mobile
42
42
  "16:9", # Widescreen
43
43
  "21:9", # Ultrawide
src/config/settings.py CHANGED
@@ -4,7 +4,6 @@ Configuration settings for the Ultimate Gemini MCP server.
4
4
 
5
5
  import os
6
6
  from pathlib import Path
7
- from typing import Optional
8
7
 
9
8
  from pydantic import Field
10
9
  from pydantic_settings import BaseSettings, SettingsConfigDict
@@ -81,9 +80,7 @@ class APIConfig(BaseSettings):
81
80
  enable_prompt_enhancement: bool = Field(
82
81
  default=True, description="Enable automatic prompt enhancement"
83
82
  )
84
- enable_batch_processing: bool = Field(
85
- default=True, description="Enable batch processing"
86
- )
83
+ enable_batch_processing: bool = Field(default=True, description="Enable batch processing")
87
84
 
88
85
  # Request settings
89
86
  request_timeout: int = Field(default=DEFAULT_TIMEOUT, description="API request timeout")
@@ -104,9 +101,7 @@ class APIConfig(BaseSettings):
104
101
  self.gemini_api_key = os.getenv("GOOGLE_API_KEY", "")
105
102
 
106
103
  if not self.gemini_api_key:
107
- raise ValueError(
108
- "GEMINI_API_KEY or GOOGLE_API_KEY environment variable is required"
109
- )
104
+ raise ValueError("GEMINI_API_KEY or GOOGLE_API_KEY environment variable is required")
110
105
 
111
106
  @classmethod
112
107
  def from_env(cls) -> "APIConfig":
@@ -132,7 +127,7 @@ class Settings:
132
127
 
133
128
 
134
129
  # Global settings instance (lazy initialization)
135
- _settings: Optional[Settings] = None
130
+ _settings: Settings | None = None
136
131
 
137
132
 
138
133
  def get_settings() -> Settings:
src/core/__init__.py CHANGED
@@ -21,7 +21,6 @@ from .validation import (
21
21
  validate_model,
22
22
  validate_negative_prompt,
23
23
  validate_number_of_images,
24
- validate_person_generation,
25
24
  validate_prompt,
26
25
  validate_prompts_list,
27
26
  validate_seed,
@@ -45,7 +44,6 @@ __all__ = [
45
44
  "validate_aspect_ratio",
46
45
  "validate_number_of_images",
47
46
  "validate_image_format",
48
- "validate_person_generation",
49
47
  "validate_seed",
50
48
  "validate_file_path",
51
49
  "validate_base64_image",
src/core/exceptions.py CHANGED
@@ -24,7 +24,9 @@ class ValidationError(UltimateGeminiError):
24
24
  class APIError(UltimateGeminiError):
25
25
  """Raised when an API request fails."""
26
26
 
27
- def __init__(self, message: str, status_code: int | None = None, response_data: dict | None = None):
27
+ def __init__(
28
+ self, message: str, status_code: int | None = None, response_data: dict | None = None
29
+ ):
28
30
  super().__init__(message)
29
31
  self.status_code = status_code
30
32
  self.response_data = response_data
src/core/validation.py CHANGED
@@ -5,7 +5,6 @@ Input validation utilities.
5
5
  import base64
6
6
  import re
7
7
  from pathlib import Path
8
- from typing import Any
9
8
 
10
9
  from ..config.constants import (
11
10
  ALL_MODELS,
@@ -14,7 +13,6 @@ from ..config.constants import (
14
13
  MAX_IMAGES_PER_REQUEST,
15
14
  MAX_NEGATIVE_PROMPT_LENGTH,
16
15
  MAX_PROMPT_LENGTH,
17
- PERSON_GENERATION_OPTIONS,
18
16
  )
19
17
  from .exceptions import ValidationError
20
18
 
@@ -50,9 +48,7 @@ def validate_aspect_ratio(aspect_ratio: str) -> None:
50
48
  """Validate aspect ratio."""
51
49
  if aspect_ratio not in ASPECT_RATIOS:
52
50
  available = ", ".join(ASPECT_RATIOS)
53
- raise ValidationError(
54
- f"Invalid aspect ratio '{aspect_ratio}'. Available: {available}"
55
- )
51
+ raise ValidationError(f"Invalid aspect ratio '{aspect_ratio}'. Available: {available}")
56
52
 
57
53
 
58
54
  def validate_number_of_images(num: int) -> None:
@@ -61,27 +57,14 @@ def validate_number_of_images(num: int) -> None:
61
57
  raise ValidationError(f"Number of images must be at least 1, got {num}")
62
58
 
63
59
  if num > MAX_IMAGES_PER_REQUEST:
64
- raise ValidationError(
65
- f"Number of images exceeds maximum: {num} > {MAX_IMAGES_PER_REQUEST}"
66
- )
60
+ raise ValidationError(f"Number of images exceeds maximum: {num} > {MAX_IMAGES_PER_REQUEST}")
67
61
 
68
62
 
69
63
  def validate_image_format(format_str: str) -> None:
70
64
  """Validate image format."""
71
65
  if format_str.lower() not in IMAGE_FORMATS:
72
66
  available = ", ".join(IMAGE_FORMATS.keys())
73
- raise ValidationError(
74
- f"Invalid image format '{format_str}'. Available: {available}"
75
- )
76
-
77
-
78
- def validate_person_generation(option: str) -> None:
79
- """Validate person generation option."""
80
- if option not in PERSON_GENERATION_OPTIONS:
81
- available = ", ".join(PERSON_GENERATION_OPTIONS)
82
- raise ValidationError(
83
- f"Invalid person generation option '{option}'. Available: {available}"
84
- )
67
+ raise ValidationError(f"Invalid image format '{format_str}'. Available: {available}")
85
68
 
86
69
 
87
70
  def validate_seed(seed: int | None) -> None:
@@ -98,7 +81,7 @@ def validate_file_path(path: str) -> Path:
98
81
  try:
99
82
  file_path = Path(path).resolve()
100
83
  except Exception as e:
101
- raise ValidationError(f"Invalid file path '{path}': {e}")
84
+ raise ValidationError(f"Invalid file path '{path}': {e}") from e
102
85
 
103
86
  if not file_path.exists():
104
87
  raise ValidationError(f"File does not exist: {file_path}")
@@ -120,7 +103,7 @@ def validate_base64_image(data: str) -> None:
120
103
  if len(decoded) == 0:
121
104
  raise ValidationError("Decoded image data is empty")
122
105
  except Exception as e:
123
- raise ValidationError(f"Invalid base64 image data: {e}")
106
+ raise ValidationError(f"Invalid base64 image data: {e}") from e
124
107
 
125
108
 
126
109
  def validate_prompts_list(prompts: list[str]) -> None:
@@ -137,7 +120,7 @@ def validate_prompts_list(prompts: list[str]) -> None:
137
120
  try:
138
121
  validate_prompt(prompt)
139
122
  except ValidationError as e:
140
- raise ValidationError(f"Invalid prompt at index {i}: {e}")
123
+ raise ValidationError(f"Invalid prompt at index {i}: {e}") from e
141
124
 
142
125
 
143
126
  def sanitize_filename(filename: str) -> str:
src/server.py CHANGED
@@ -10,7 +10,6 @@ Unified MCP server supporting:
10
10
 
11
11
  import logging
12
12
  import sys
13
- from pathlib import Path
14
13
 
15
14
  from fastmcp import FastMCP
16
15
 
@@ -69,9 +68,9 @@ def create_app() -> FastMCP:
69
68
  "Image editing",
70
69
  "Character consistency",
71
70
  "Multi-image blending",
72
- "World knowledge integration"
71
+ "World knowledge integration",
73
72
  ],
74
- "default": True
73
+ "default": True,
75
74
  }
76
75
  },
77
76
  "imagen": {
@@ -84,8 +83,8 @@ def create_app() -> FastMCP:
84
83
  "Negative prompts",
85
84
  "Seed-based reproducibility",
86
85
  "Person generation controls",
87
- "Advanced controls"
88
- ]
86
+ "Advanced controls",
87
+ ],
89
88
  },
90
89
  "imagen-4-fast": {
91
90
  "name": "Imagen 4 Fast",
@@ -96,8 +95,8 @@ def create_app() -> FastMCP:
96
95
  "Negative prompts",
97
96
  "Seed-based reproducibility",
98
97
  "Person generation controls",
99
- "Cost-effective"
100
- ]
98
+ "Cost-effective",
99
+ ],
101
100
  },
102
101
  "imagen-4-ultra": {
103
102
  "name": "Imagen 4 Ultra",
@@ -107,10 +106,10 @@ def create_app() -> FastMCP:
107
106
  "Best prompt adherence",
108
107
  "Professional results",
109
108
  "Enhanced text rendering",
110
- "Advanced controls"
111
- ]
112
- }
113
- }
109
+ "Advanced controls",
110
+ ],
111
+ },
112
+ },
114
113
  }
115
114
 
116
115
  return json.dumps(models_info, indent=2)
src/services/__init__.py CHANGED
@@ -1,8 +1,8 @@
1
1
  """Services module for Ultimate Gemini MCP."""
2
2
 
3
3
  from .gemini_client import GeminiClient
4
- from .imagen_client import ImagenClient
5
4
  from .image_service import ImageResult, ImageService
5
+ from .imagen_client import ImagenClient
6
6
  from .prompt_enhancer import PromptEnhancer, create_prompt_enhancer
7
7
 
8
8
  __all__ = [
@@ -3,7 +3,6 @@ Gemini API client for Gemini 2.5 Flash Image generation.
3
3
  Uses the generateContent API endpoint per Google's documentation.
4
4
  """
5
5
 
6
- import base64
7
6
  import logging
8
7
  from typing import Any
9
8
 
@@ -69,12 +68,7 @@ class GeminiClient:
69
68
 
70
69
  # Add input image if provided (for editing)
71
70
  if input_image:
72
- parts.append({
73
- "inline_data": {
74
- "mime_type": "image/png",
75
- "data": input_image
76
- }
77
- })
71
+ parts.append({"inline_data": {"mime_type": "image/png", "data": input_image}})
78
72
 
79
73
  # Add text prompt (include aspect ratio hint if specified)
80
74
  prompt_text = prompt
@@ -83,7 +77,14 @@ class GeminiClient:
83
77
 
84
78
  parts.append({"text": prompt_text})
85
79
 
86
- request_body = {"contents": [{"parts": parts}]}
80
+ # Build generation config for image generation
81
+ generation_config = {"responseModalities": ["Image"]}
82
+
83
+ # Add aspect ratio to image config if specified
84
+ if aspect_ratio:
85
+ generation_config["imageConfig"] = {"aspectRatio": aspect_ratio}
86
+
87
+ request_body = {"contents": [{"parts": parts}], "generationConfig": generation_config}
87
88
 
88
89
  headers = {
89
90
  "x-goog-api-key": self.api_key,
@@ -92,14 +93,23 @@ class GeminiClient:
92
93
 
93
94
  try:
94
95
  logger.debug(f"Sending request to {url}")
96
+ logger.debug(f"Request body: {request_body}")
95
97
  response = await self.client.post(url, json=request_body, headers=headers)
96
98
  response.raise_for_status()
97
99
  data = response.json()
98
100
 
101
+ logger.debug(f"Response status: {response.status_code}")
102
+ logger.debug(f"Response data: {data}")
103
+
99
104
  # Extract images from response
100
105
  images = self._extract_images(data)
101
106
 
102
107
  if not images:
108
+ logger.error(
109
+ f"No images extracted from response. Response structure: {list(data.keys())}"
110
+ )
111
+ if "candidates" in data:
112
+ logger.error(f"Candidates: {data['candidates']}")
103
113
  raise APIError("No image data found in Gemini API response")
104
114
 
105
115
  return {"images": images, "model": model, "response": data}
@@ -108,7 +118,7 @@ class GeminiClient:
108
118
  self._handle_http_error(e)
109
119
  except Exception as e:
110
120
  logger.error(f"Gemini API request failed: {e}")
111
- raise APIError(f"Gemini API request failed: {e}")
121
+ raise APIError(f"Gemini API request failed: {e}") from e
112
122
 
113
123
  async def generate_text(
114
124
  self,
@@ -131,14 +141,10 @@ class GeminiClient:
131
141
  model_id = GEMINI_MODELS.get(model, model)
132
142
  url = f"{self.base_url}/models/{model_id}:generateContent"
133
143
 
134
- request_body = {
135
- "contents": [{"parts": [{"text": prompt}]}]
136
- }
144
+ request_body = {"contents": [{"parts": [{"text": prompt}]}]}
137
145
 
138
146
  if system_instruction:
139
- request_body["system_instruction"] = {
140
- "parts": [{"text": system_instruction}]
141
- }
147
+ request_body["system_instruction"] = {"parts": [{"text": system_instruction}]}
142
148
 
143
149
  headers = {
144
150
  "x-goog-api-key": self.api_key,
@@ -158,7 +164,7 @@ class GeminiClient:
158
164
  self._handle_http_error(e)
159
165
  except Exception as e:
160
166
  logger.error(f"Gemini text generation failed: {e}")
161
- raise APIError(f"Gemini text generation failed: {e}")
167
+ raise APIError(f"Gemini text generation failed: {e}") from e
162
168
 
163
169
  def _extract_images(self, response_data: dict[str, Any]) -> list[str]:
164
170
  """Extract base64 image data from Gemini API response."""
@@ -170,10 +176,13 @@ class GeminiClient:
170
176
  content = candidate.get("content", {})
171
177
  parts = content.get("parts", [])
172
178
  for part in parts:
173
- if "inline_data" in part:
174
- image_data = part["inline_data"].get("data")
179
+ # Handle both inline_data and inlineData formats
180
+ inline_data = part.get("inline_data") or part.get("inlineData")
181
+ if inline_data:
182
+ image_data = inline_data.get("data")
175
183
  if image_data:
176
184
  images.append(image_data)
185
+ logger.debug(f"Extracted image data of length: {len(image_data)}")
177
186
  except Exception as e:
178
187
  logger.warning(f"Error extracting images from response: {e}")
179
188
 
@@ -206,23 +215,23 @@ class GeminiClient:
206
215
 
207
216
  if status_code == 401 or status_code == 403:
208
217
  raise AuthenticationError(
209
- "Authentication failed. Please check your Gemini API key.",
210
- status_code=status_code
218
+ "Authentication failed. Please check your Gemini API key.", status_code=status_code
211
219
  )
212
220
  elif status_code == 429:
213
221
  raise RateLimitError(
214
- "Rate limit exceeded. Please try again later.",
215
- status_code=status_code
222
+ "Rate limit exceeded. Please try again later.", status_code=status_code
216
223
  )
217
- elif status_code == 400 and ("SAFETY" in error_text.upper() or "BLOCKED" in error_text.upper()):
224
+ elif status_code == 400 and (
225
+ "SAFETY" in error_text.upper() or "BLOCKED" in error_text.upper()
226
+ ):
218
227
  raise ContentPolicyError(
219
228
  "Content was blocked by safety filters. Please modify your prompt.",
220
- status_code=status_code
229
+ status_code=status_code,
221
230
  )
222
231
  else:
223
232
  raise APIError(
224
233
  f"API request failed with status {status_code}: {error_text}",
225
- status_code=status_code
234
+ status_code=status_code,
226
235
  )
227
236
 
228
237
  async def close(self) -> None:
@@ -9,8 +9,6 @@ from datetime import datetime
9
9
  from pathlib import Path
10
10
  from typing import Any
11
11
 
12
- from PIL import Image
13
-
14
12
  from ..config.constants import GEMINI_MODELS, IMAGEN_MODELS
15
13
  from ..core import sanitize_filename
16
14
  from ..core.exceptions import ImageProcessingError
@@ -30,7 +28,7 @@ class ImageResult:
30
28
  prompt: str,
31
29
  model: str,
32
30
  index: int = 0,
33
- metadata: dict[str, Any] | None = None
31
+ metadata: dict[str, Any] | None = None,
34
32
  ):
35
33
  self.image_data = image_data # Base64-encoded
36
34
  self.prompt = prompt
@@ -53,7 +51,7 @@ class ImageResult:
53
51
  logger.info(f"Saved image to {output_path}")
54
52
  return output_path
55
53
  except Exception as e:
56
- raise ImageProcessingError(f"Failed to save image: {e}")
54
+ raise ImageProcessingError(f"Failed to save image: {e}") from e
57
55
 
58
56
  def _generate_filename(self) -> str:
59
57
  """Generate descriptive filename."""
@@ -71,13 +69,7 @@ class ImageResult:
71
69
  class ImageService:
72
70
  """Unified service for image generation using Gemini or Imagen."""
73
71
 
74
- def __init__(
75
- self,
76
- api_key: str,
77
- *,
78
- enable_enhancement: bool = True,
79
- timeout: int = 60
80
- ):
72
+ def __init__(self, api_key: str, *, enable_enhancement: bool = True, timeout: int = 60):
81
73
  """
82
74
  Initialize image service.
83
75
 
@@ -100,12 +92,7 @@ class ImageService:
100
92
  self.prompt_enhancer = PromptEnhancer(self.gemini_client)
101
93
 
102
94
  async def generate(
103
- self,
104
- prompt: str,
105
- *,
106
- model: str | None = None,
107
- enhance_prompt: bool = True,
108
- **kwargs: Any
95
+ self, prompt: str, *, model: str | None = None, enhance_prompt: bool = True, **kwargs: Any
109
96
  ) -> list[ImageResult]:
110
97
  """
111
98
  Generate images using the appropriate API.
@@ -136,8 +123,7 @@ class ImageService:
136
123
  if enhance_prompt and self.enable_enhancement and self.prompt_enhancer:
137
124
  try:
138
125
  result = await self.prompt_enhancer.enhance_prompt(
139
- prompt,
140
- context=enhancement_context
126
+ prompt, context=enhancement_context
141
127
  )
142
128
  prompt = result["enhanced_prompt"]
143
129
  logger.info(f"Prompt enhanced: {len(original_prompt)} -> {len(prompt)} chars")
@@ -151,18 +137,10 @@ class ImageService:
151
137
  return await self._generate_with_imagen(prompt, model, original_prompt, kwargs)
152
138
 
153
139
  async def _generate_with_gemini(
154
- self,
155
- prompt: str,
156
- model: str,
157
- original_prompt: str,
158
- params: dict[str, Any]
140
+ self, prompt: str, model: str, original_prompt: str, params: dict[str, Any]
159
141
  ) -> list[ImageResult]:
160
142
  """Generate images using Gemini API."""
161
- response = await self.gemini_client.generate_image(
162
- prompt=prompt,
163
- model=model,
164
- **params
165
- )
143
+ response = await self.gemini_client.generate_image(prompt=prompt, model=model, **params)
166
144
 
167
145
  images = response["images"]
168
146
  results = []
@@ -173,29 +151,17 @@ class ImageService:
173
151
  prompt=original_prompt,
174
152
  model=model,
175
153
  index=i,
176
- metadata={
177
- "enhanced_prompt": prompt,
178
- "api": "gemini",
179
- **params
180
- }
154
+ metadata={"enhanced_prompt": prompt, "api": "gemini", **params},
181
155
  )
182
156
  results.append(result)
183
157
 
184
158
  return results
185
159
 
186
160
  async def _generate_with_imagen(
187
- self,
188
- prompt: str,
189
- model: str,
190
- original_prompt: str,
191
- params: dict[str, Any]
161
+ self, prompt: str, model: str, original_prompt: str, params: dict[str, Any]
192
162
  ) -> list[ImageResult]:
193
163
  """Generate images using Imagen API."""
194
- response = await self.imagen_client.generate_image(
195
- prompt=prompt,
196
- model=model,
197
- **params
198
- )
164
+ response = await self.imagen_client.generate_image(prompt=prompt, model=model, **params)
199
165
 
200
166
  images = response["images"]
201
167
  results = []
@@ -206,11 +172,7 @@ class ImageService:
206
172
  prompt=original_prompt,
207
173
  model=model,
208
174
  index=i,
209
- metadata={
210
- "enhanced_prompt": prompt,
211
- "api": "imagen",
212
- **params
213
- }
175
+ metadata={"enhanced_prompt": prompt, "api": "imagen", **params},
214
176
  )
215
177
  results.append(result)
216
178
 
@@ -73,25 +73,26 @@ class ImagenClient:
73
73
 
74
74
  # Build request body according to Imagen API
75
75
  request_body: dict[str, Any] = {
76
- "instances": [
77
- {
78
- "prompt": prompt
79
- }
80
- ],
76
+ "instances": [{"prompt": prompt}],
81
77
  "parameters": {
82
78
  "outputMimeType": output_format,
83
79
  "sampleCount": number_of_images,
84
80
  "personGeneration": person_generation,
85
- "aspectRatio": aspect_ratio
86
- }
81
+ "aspectRatio": aspect_ratio,
82
+ },
87
83
  }
88
84
 
89
85
  # Add optional parameters
90
86
  if negative_prompt:
91
87
  request_body["instances"][0]["negativePrompt"] = negative_prompt
92
88
 
89
+ # Note: Seed parameter is not supported by Imagen API (as of 2025)
90
+ # The API returns a 400 error if seed is provided
93
91
  if seed is not None:
94
- request_body["parameters"]["seed"] = seed
92
+ logger.warning(
93
+ "Seed parameter is not supported by Imagen API and will be ignored. "
94
+ "Images cannot be reproduced with a seed value."
95
+ )
95
96
 
96
97
  headers = {
97
98
  "Content-Type": "application/json",
@@ -101,9 +102,7 @@ class ImagenClient:
101
102
  logger.debug(f"Sending request to {url}")
102
103
  # Add API key as query parameter
103
104
  response = await self.client.post(
104
- f"{url}?key={self.api_key}",
105
- json=request_body,
106
- headers=headers
105
+ f"{url}?key={self.api_key}", json=request_body, headers=headers
107
106
  )
108
107
  response.raise_for_status()
109
108
  data = response.json()
@@ -114,17 +113,13 @@ class ImagenClient:
114
113
  if not images:
115
114
  raise APIError("No image data found in Imagen API response")
116
115
 
117
- return {
118
- "images": images,
119
- "model": model,
120
- "response": data
121
- }
116
+ return {"images": images, "model": model, "response": data}
122
117
 
123
118
  except httpx.HTTPStatusError as e:
124
119
  self._handle_http_error(e)
125
120
  except Exception as e:
126
121
  logger.error(f"Imagen API request failed: {e}")
127
- raise APIError(f"Imagen API request failed: {e}")
122
+ raise APIError(f"Imagen API request failed: {e}") from e
128
123
 
129
124
  def _extract_images(self, response_data: dict[str, Any]) -> list[str]:
130
125
  """Extract base64 image data from Imagen API response."""
@@ -151,23 +146,21 @@ class ImagenClient:
151
146
 
152
147
  if status_code == 401 or status_code == 403:
153
148
  raise AuthenticationError(
154
- "Authentication failed. Please check your API key.",
155
- status_code=status_code
149
+ "Authentication failed. Please check your API key.", status_code=status_code
156
150
  )
157
151
  elif status_code == 429:
158
152
  raise RateLimitError(
159
- "Rate limit exceeded. Please try again later.",
160
- status_code=status_code
153
+ "Rate limit exceeded. Please try again later.", status_code=status_code
161
154
  )
162
155
  elif status_code == 400 and "SAFETY" in error_text.upper():
163
156
  raise ContentPolicyError(
164
157
  "Content was blocked by safety filters. Please modify your prompt.",
165
- status_code=status_code
158
+ status_code=status_code,
166
159
  )
167
160
  else:
168
161
  raise APIError(
169
162
  f"API request failed with status {status_code}: {error_text}",
170
- status_code=status_code
163
+ status_code=status_code,
171
164
  )
172
165
 
173
166
  async def close(self) -> None:
@@ -64,7 +64,7 @@ class PromptEnhancer:
64
64
  enhanced = await self.gemini_client.generate_text(
65
65
  prompt=instruction,
66
66
  system_instruction=PROMPT_ENHANCEMENT_SYSTEM_INSTRUCTION,
67
- model="gemini-flash-latest"
67
+ model="gemini-flash-latest",
68
68
  )
69
69
 
70
70
  # Clean up the enhanced prompt
@@ -84,11 +84,7 @@ class PromptEnhancer:
84
84
  "enhanced_prompt": original_prompt,
85
85
  }
86
86
 
87
- def _build_enhancement_instruction(
88
- self,
89
- prompt: str,
90
- context: dict[str, Any] | None
91
- ) -> str:
87
+ def _build_enhancement_instruction(self, prompt: str, context: dict[str, Any] | None) -> str:
92
88
  """Build the instruction for prompt enhancement."""
93
89
  instruction_parts = [f"Enhance this image generation prompt:\n\n{prompt}"]
94
90
 
@@ -54,12 +54,12 @@ async def batch_generate_images(
54
54
  "batch_size": batch_size,
55
55
  "completed": 0,
56
56
  "failed": 0,
57
- "results": []
57
+ "results": [],
58
58
  }
59
59
 
60
60
  # Process prompts in batches
61
61
  for i in range(0, len(prompts), batch_size):
62
- batch = prompts[i:i + batch_size]
62
+ batch = prompts[i : i + batch_size]
63
63
  logger.info(f"Processing batch {i // batch_size + 1}: {len(batch)} prompts")
64
64
 
65
65
  # Create tasks for parallel processing
@@ -71,7 +71,7 @@ async def batch_generate_images(
71
71
  aspect_ratio=aspect_ratio,
72
72
  output_format=output_format,
73
73
  number_of_images=1,
74
- **shared_params
74
+ **shared_params,
75
75
  )
76
76
  for prompt in batch
77
77
  ]
@@ -86,19 +86,19 @@ async def batch_generate_images(
86
86
  if isinstance(result, Exception):
87
87
  logger.error(f"Failed to generate image for prompt {prompt_index}: {result}")
88
88
  results["failed"] += 1
89
- results["results"].append({
90
- "prompt_index": prompt_index,
91
- "prompt": batch[j],
92
- "success": False,
93
- "error": str(result)
94
- })
89
+ results["results"].append(
90
+ {
91
+ "prompt_index": prompt_index,
92
+ "prompt": batch[j],
93
+ "success": False,
94
+ "error": str(result),
95
+ }
96
+ )
95
97
  else:
96
98
  results["completed"] += 1
97
- results["results"].append({
98
- "prompt_index": prompt_index,
99
- "prompt": batch[j],
100
- **result
101
- })
99
+ results["results"].append(
100
+ {"prompt_index": prompt_index, "prompt": batch[j], **result}
101
+ )
102
102
 
103
103
  return results
104
104
 
@@ -114,7 +114,6 @@ def register_batch_generate_tool(mcp_server: Any) -> None:
114
114
  aspect_ratio: str = "1:1",
115
115
  output_format: str = "png",
116
116
  batch_size: int | None = None,
117
- person_generation: str = "allow_adult",
118
117
  negative_prompt: str | None = None,
119
118
  ) -> str:
120
119
  """
@@ -130,7 +129,6 @@ def register_batch_generate_tool(mcp_server: Any) -> None:
130
129
  aspect_ratio: Aspect ratio for all images (default: 1:1)
131
130
  output_format: Image format for all images (default: png)
132
131
  batch_size: Parallel batch size (default: from config)
133
- person_generation: Person policy for Imagen models (default: allow_adult)
134
132
  negative_prompt: Negative prompt for Imagen models (optional)
135
133
 
136
134
  Returns:
@@ -144,7 +142,6 @@ def register_batch_generate_tool(mcp_server: Any) -> None:
144
142
  aspect_ratio=aspect_ratio,
145
143
  output_format=output_format,
146
144
  batch_size=batch_size,
147
- person_generation=person_generation,
148
145
  negative_prompt=negative_prompt,
149
146
  )
150
147
 
@@ -152,8 +149,6 @@ def register_batch_generate_tool(mcp_server: Any) -> None:
152
149
 
153
150
  except Exception as e:
154
151
  logger.error(f"Batch generation error: {e}")
155
- return json.dumps({
156
- "success": False,
157
- "error": str(e),
158
- "error_type": type(e).__name__
159
- }, indent=2)
152
+ return json.dumps(
153
+ {"success": False, "error": str(e), "error_type": type(e).__name__}, indent=2
154
+ )
@@ -8,15 +8,12 @@ import logging
8
8
  from pathlib import Path
9
9
  from typing import Any
10
10
 
11
- from fastmcp import Image
12
-
13
- from ..config import ALL_MODELS, ASPECT_RATIOS, get_settings
11
+ from ..config import get_settings
14
12
  from ..core import (
15
13
  validate_aspect_ratio,
16
14
  validate_image_format,
17
15
  validate_model,
18
16
  validate_number_of_images,
19
- validate_person_generation,
20
17
  validate_prompt,
21
18
  validate_seed,
22
19
  )
@@ -38,7 +35,6 @@ async def generate_image_tool(
38
35
  blend_images: bool = False,
39
36
  use_world_knowledge: bool = False,
40
37
  # Imagen-specific options
41
- person_generation: str = "allow_adult",
42
38
  negative_prompt: str | None = None,
43
39
  seed: int | None = None,
44
40
  # Output options
@@ -59,7 +55,6 @@ async def generate_image_tool(
59
55
  maintain_character_consistency: Maintain character features across generations (Gemini)
60
56
  blend_images: Enable multi-image blending (Gemini)
61
57
  use_world_knowledge: Use real-world knowledge for context (Gemini)
62
- person_generation: Person generation policy (Imagen: dont_allow, allow_adult, allow_all)
63
58
  negative_prompt: What to avoid in the image (Imagen)
64
59
  seed: Random seed for reproducibility (Imagen)
65
60
  save_to_disk: Save images to output directory
@@ -75,10 +70,11 @@ async def generate_image_tool(
75
70
  validate_aspect_ratio(aspect_ratio)
76
71
  validate_image_format(output_format)
77
72
 
78
- if person_generation:
79
- validate_person_generation(person_generation)
80
73
  if seed is not None:
81
74
  validate_seed(seed)
75
+ logger.warning(
76
+ "Note: The seed parameter is not currently supported by Imagen API and will be ignored."
77
+ )
82
78
 
83
79
  # Get settings
84
80
  settings = get_settings()
@@ -121,7 +117,7 @@ async def generate_image_tool(
121
117
  if model.startswith("imagen"):
122
118
  params["number_of_images"] = number_of_images
123
119
  params["output_format"] = f"image/{output_format}"
124
- params["person_generation"] = person_generation
120
+ params["person_generation"] = "allow_adult" # Hard-coded to allow adults
125
121
  if negative_prompt:
126
122
  params["negative_prompt"] = negative_prompt
127
123
  if seed is not None:
@@ -132,7 +128,7 @@ async def generate_image_tool(
132
128
  prompt=prompt,
133
129
  model=model,
134
130
  enhance_prompt=enhance_prompt and settings.api.enable_prompt_enhancement,
135
- **params
131
+ **params,
136
132
  )
137
133
 
138
134
  # Prepare response
@@ -145,7 +141,7 @@ async def generate_image_tool(
145
141
  "metadata": {
146
142
  "enhance_prompt": enhance_prompt,
147
143
  "aspect_ratio": aspect_ratio,
148
- }
144
+ },
149
145
  }
150
146
 
151
147
  # Save images and prepare for MCP response
@@ -189,7 +185,6 @@ def register_generate_image_tool(mcp_server: Any) -> None:
189
185
  maintain_character_consistency: bool = False,
190
186
  blend_images: bool = False,
191
187
  use_world_knowledge: bool = False,
192
- person_generation: str = "allow_adult",
193
188
  negative_prompt: str | None = None,
194
189
  seed: int | None = None,
195
190
  ) -> str:
@@ -211,9 +206,8 @@ def register_generate_image_tool(mcp_server: Any) -> None:
211
206
  maintain_character_consistency: Maintain character features (Gemini only)
212
207
  blend_images: Enable multi-image blending (Gemini only)
213
208
  use_world_knowledge: Use real-world knowledge (Gemini only)
214
- person_generation: Person policy: dont_allow, allow_adult, allow_all (Imagen only)
215
209
  negative_prompt: What to avoid in the image (Imagen only)
216
- seed: Random seed for reproducibility (Imagen only)
210
+ seed: Random seed for reproducibility (NOT SUPPORTED - will be ignored)
217
211
 
218
212
  Available models:
219
213
  - gemini-2.5-flash-image (default)
@@ -236,7 +230,6 @@ def register_generate_image_tool(mcp_server: Any) -> None:
236
230
  maintain_character_consistency=maintain_character_consistency,
237
231
  blend_images=blend_images,
238
232
  use_world_knowledge=use_world_knowledge,
239
- person_generation=person_generation,
240
233
  negative_prompt=negative_prompt,
241
234
  seed=seed,
242
235
  )
@@ -245,8 +238,6 @@ def register_generate_image_tool(mcp_server: Any) -> None:
245
238
 
246
239
  except Exception as e:
247
240
  logger.error(f"Error generating image: {e}")
248
- return json.dumps({
249
- "success": False,
250
- "error": str(e),
251
- "error_type": type(e).__name__
252
- }, indent=2)
241
+ return json.dumps(
242
+ {"success": False, "error": str(e), "error_type": type(e).__name__}, indent=2
243
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ultimate-gemini-mcp
3
- Version: 1.0.2
3
+ Version: 1.0.4
4
4
  Summary: Ultimate image generation MCP server unifying Gemini 2.5 Flash Image and Imagen 4/Fast/Ultra with advanced features
5
5
  Project-URL: Homepage, https://github.com/anand-92/ultimate-image-gen-mcp
6
6
  Project-URL: Repository, https://github.com/anand-92/ultimate-image-gen-mcp
@@ -0,0 +1,21 @@
1
+ src/__init__.py,sha256=7kfqcAK61FJqoPQ6GcvaB5vV2c0Z69MqwuAg3TJjFew,435
2
+ src/server.py,sha256=nZI63qIDL3JWv3dyyFk6lIXStZuHe1MTdZqZSrqK56k,5862
3
+ src/config/__init__.py,sha256=hL0recV_ycXBEGCym7BqwyaPCnQHy8o429pBirnBeiA,704
4
+ src/config/constants.py,sha256=ue4dT6wFwCzAgDWvSt8RnbdaoaGHY8c7SViNxI-P73w,1870
5
+ src/config/settings.py,sha256=uTxxblnyqmglz1COd3QxjC2w3o6l51S9MTanP4HBrgE,4257
6
+ src/core/__init__.py,sha256=PQKMAY5eYIEO1oS_hrzeuDgRopl76Wn6NLfhtXBX92I,1200
7
+ src/core/exceptions.py,sha256=NVb4tpwoX_DR-Wp8r73brXvZhEP35SUMyuEis8llFR0,1211
8
+ src/core/validation.py,sha256=JTB6_ft4nN6NHRVD5vOv_q12aPupBrlaNl32jwR2TuE,4758
9
+ src/services/__init__.py,sha256=n6FVDj052zvTerDFJSjaiLIycWGePm8Wr6dabbvd9o0,395
10
+ src/services/gemini_client.py,sha256=VPuBPDHhAag_8xBqzMuVIEWVG0BINXOHkwKIALzcFAk,8324
11
+ src/services/image_service.py,sha256=ovYsxDMwqsSiG1YbwBcTBrgmGTA7bdhDF6_jPs9XV_k,7095
12
+ src/services/imagen_client.py,sha256=OBeaPgoTyL2O9q02uRdYSmXmo_8vZk4B1ZWIUm0UelQ,5749
13
+ src/services/prompt_enhancer.py,sha256=J_0s1EVmXdPAhjZPM4hL1vk9YiawqfH_ogF89VrFkW8,4776
14
+ src/tools/__init__.py,sha256=zBfAjFT51LvvD7WXTfDYiyJstRdphr2ChddAmGMZxkI,346
15
+ src/tools/batch_generate.py,sha256=LLrIwVvx-0kZBCiR0tcYnmKi5iYIdV-93ekIQbMrabo,5004
16
+ src/tools/generate_image.py,sha256=oYi0wH5uP2ZW9wuh8ngMg-2dgcAUkK2bI30Y2pfQbAI,8454
17
+ ultimate_gemini_mcp-1.0.4.dist-info/METADATA,sha256=LhF-B1lhEXUPIHX46liE_jjQIsoVaQoKXTilqW3gkwQ,11646
18
+ ultimate_gemini_mcp-1.0.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
19
+ ultimate_gemini_mcp-1.0.4.dist-info/entry_points.txt,sha256=-BeRTT4oR05e-YnF1ZNbNxlaekD4LsWhD-Zy_1dyRnc,56
20
+ ultimate_gemini_mcp-1.0.4.dist-info/licenses/LICENSE,sha256=ilyzUnN0QHYtYGJks-NFUwiniNu08IedLmn_muRqa0o,1480
21
+ ultimate_gemini_mcp-1.0.4.dist-info/RECORD,,
@@ -1,21 +0,0 @@
1
- src/__init__.py,sha256=GIg4XdKkxcMxMWTdeku0la3TuFsEep-WFF7XSqy7KK0,435
2
- src/server.py,sha256=vVJ96VMl-Z7y756qjASywZaD2QYj5v8nIQ8r504Mg3g,5877
3
- src/config/__init__.py,sha256=hL0recV_ycXBEGCym7BqwyaPCnQHy8o429pBirnBeiA,704
4
- src/config/constants.py,sha256=iLHMFVJzWRkSMkG6gXHBWepVLbcDvlgqZtkgfEuaaUQ,1877
5
- src/config/settings.py,sha256=IxxvETiW-CK5VLeTOegx3hAfd5gIxBSVAUyKbc9GBpU,4332
6
- src/core/__init__.py,sha256=h8ik58bFn0g0XFVtMEI4MDoFapi4FlQywgd3S0eR2Oo,1266
7
- src/core/exceptions.py,sha256=U09aPGmlDTAGbt6PbF-PKbmT1ee9LHqghkOXGn094UI,1197
8
- src/core/validation.py,sha256=NzF01GVhmBefn_bo4cGDMREuE6kc3RujbfOtfx9eLyk,5186
9
- src/services/__init__.py,sha256=5iCkCasXmsBcJ0zDIu3qCSdTZtmKPDmWuz14dMK0N7I,395
10
- src/services/gemini_client.py,sha256=nVhx2s8sVhizeHa1D6IbxT5TRqGIF23kobMTcBh5vyA,7490
11
- src/services/image_service.py,sha256=UcErTYMjG6ihKeT2w0UaGiiVIoUwf2Kt7oSafEjx28w,7526
12
- src/services/imagen_client.py,sha256=dvDa0Z6xV-xeF428_arf3JXMf-rYlwOXoDD_6pTZzOE,5660
13
- src/services/prompt_enhancer.py,sha256=JFTKqwWafngq37yGjiYT5-FJbIj4Buvt0js2nU_9gsw,4805
14
- src/tools/__init__.py,sha256=zBfAjFT51LvvD7WXTfDYiyJstRdphr2ChddAmGMZxkI,346
15
- src/tools/batch_generate.py,sha256=tcilmaOWXMFnVBMdMbIUXxHtbpnBLeIkJe2LDXnE7qU,5200
16
- src/tools/generate_image.py,sha256=dMj3SD4aNx4XAbkMNJ3TfO4F0N68GmZZQzbmqz03xsM,8806
17
- ultimate_gemini_mcp-1.0.2.dist-info/METADATA,sha256=zwOiznrvjmMegCO3jqT0Q89OVX8PyYov-GifG_lL0-Y,11646
18
- ultimate_gemini_mcp-1.0.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
19
- ultimate_gemini_mcp-1.0.2.dist-info/entry_points.txt,sha256=-BeRTT4oR05e-YnF1ZNbNxlaekD4LsWhD-Zy_1dyRnc,56
20
- ultimate_gemini_mcp-1.0.2.dist-info/licenses/LICENSE,sha256=ilyzUnN0QHYtYGJks-NFUwiniNu08IedLmn_muRqa0o,1480
21
- ultimate_gemini_mcp-1.0.2.dist-info/RECORD,,