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 +1 -1
- src/config/constants.py +7 -7
- src/config/settings.py +3 -8
- src/core/__init__.py +0 -2
- src/core/exceptions.py +3 -1
- src/core/validation.py +6 -23
- src/server.py +10 -11
- src/services/__init__.py +1 -1
- src/services/gemini_client.py +34 -25
- src/services/image_service.py +11 -49
- src/services/imagen_client.py +16 -23
- src/services/prompt_enhancer.py +2 -6
- src/tools/batch_generate.py +17 -22
- src/tools/generate_image.py +11 -20
- {ultimate_gemini_mcp-1.0.2.dist-info → ultimate_gemini_mcp-1.0.4.dist-info}/METADATA +1 -1
- ultimate_gemini_mcp-1.0.4.dist-info/RECORD +21 -0
- ultimate_gemini_mcp-1.0.2.dist-info/RECORD +0 -21
- {ultimate_gemini_mcp-1.0.2.dist-info → ultimate_gemini_mcp-1.0.4.dist-info}/WHEEL +0 -0
- {ultimate_gemini_mcp-1.0.2.dist-info → ultimate_gemini_mcp-1.0.4.dist-info}/entry_points.txt +0 -0
- {ultimate_gemini_mcp-1.0.2.dist-info → ultimate_gemini_mcp-1.0.4.dist-info}/licenses/LICENSE +0 -0
src/__init__.py
CHANGED
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",
|
|
35
|
-
"2:3",
|
|
36
|
-
"3:2",
|
|
37
|
-
"3:4",
|
|
38
|
-
"4:3",
|
|
39
|
-
"4:5",
|
|
40
|
-
"5:4",
|
|
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:
|
|
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__(
|
|
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__ = [
|
src/services/gemini_client.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
174
|
-
|
|
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 (
|
|
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:
|
src/services/image_service.py
CHANGED
|
@@ -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
|
|
src/services/imagen_client.py
CHANGED
|
@@ -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
|
-
|
|
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:
|
src/services/prompt_enhancer.py
CHANGED
|
@@ -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
|
|
src/tools/batch_generate.py
CHANGED
|
@@ -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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
)
|
src/tools/generate_image.py
CHANGED
|
@@ -8,15 +8,12 @@ import logging
|
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from typing import Any
|
|
10
10
|
|
|
11
|
-
from
|
|
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"] =
|
|
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 (
|
|
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
|
-
|
|
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.
|
|
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,,
|
|
File without changes
|
{ultimate_gemini_mcp-1.0.2.dist-info → ultimate_gemini_mcp-1.0.4.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{ultimate_gemini_mcp-1.0.2.dist-info → ultimate_gemini_mcp-1.0.4.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|