ultimate-gemini-mcp 1.0.1__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.

@@ -0,0 +1,175 @@
1
+ """
2
+ Imagen API client for Imagen 3, 4, and 4-Ultra models.
3
+ Uses the predict API endpoint per Google's documentation.
4
+ """
5
+
6
+ import logging
7
+ from typing import Any
8
+
9
+ import httpx
10
+
11
+ from ..config.constants import IMAGEN_API_BASE, IMAGEN_MODELS
12
+ from ..core.exceptions import (
13
+ APIError,
14
+ AuthenticationError,
15
+ ContentPolicyError,
16
+ RateLimitError,
17
+ )
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ class ImagenClient:
23
+ """Client for Imagen 3/4/Ultra API."""
24
+
25
+ def __init__(self, api_key: str, timeout: int = 60):
26
+ """
27
+ Initialize Imagen client.
28
+
29
+ Args:
30
+ api_key: Gemini/Google API key
31
+ timeout: Request timeout in seconds
32
+ """
33
+ self.api_key = api_key
34
+ self.timeout = timeout
35
+ self.base_url = IMAGEN_API_BASE
36
+ self.client = httpx.AsyncClient(timeout=timeout)
37
+
38
+ async def generate_image(
39
+ self,
40
+ prompt: str,
41
+ *,
42
+ model: str = "imagen-4-ultra",
43
+ number_of_images: int = 1,
44
+ aspect_ratio: str = "1:1",
45
+ output_format: str = "image/png",
46
+ person_generation: str = "allow_adult",
47
+ negative_prompt: str | None = None,
48
+ seed: int | None = None,
49
+ **kwargs: Any,
50
+ ) -> dict[str, Any]:
51
+ """
52
+ Generate images using Imagen API.
53
+
54
+ Args:
55
+ prompt: Text prompt for image generation
56
+ model: Imagen model to use (imagen-3, imagen-4, imagen-4-ultra)
57
+ number_of_images: Number of images to generate (1-4)
58
+ aspect_ratio: Image aspect ratio
59
+ output_format: Output MIME type (image/jpeg or image/png)
60
+ person_generation: Person generation policy
61
+ negative_prompt: Optional negative prompt
62
+ seed: Optional seed for reproducibility
63
+ **kwargs: Additional parameters
64
+
65
+ Returns:
66
+ Dict with 'images' key containing list of base64-encoded images
67
+
68
+ Raises:
69
+ APIError: If the API request fails
70
+ """
71
+ model_id = IMAGEN_MODELS.get(model, model)
72
+ url = f"{self.base_url}/{model_id}:predict"
73
+
74
+ # Build request body according to Imagen API
75
+ request_body: dict[str, Any] = {
76
+ "instances": [
77
+ {
78
+ "prompt": prompt
79
+ }
80
+ ],
81
+ "parameters": {
82
+ "outputMimeType": output_format,
83
+ "sampleCount": number_of_images,
84
+ "personGeneration": person_generation,
85
+ "aspectRatio": aspect_ratio
86
+ }
87
+ }
88
+
89
+ # Add optional parameters
90
+ if negative_prompt:
91
+ request_body["instances"][0]["negativePrompt"] = negative_prompt
92
+
93
+ if seed is not None:
94
+ request_body["parameters"]["seed"] = seed
95
+
96
+ headers = {
97
+ "Content-Type": "application/json",
98
+ }
99
+
100
+ try:
101
+ logger.debug(f"Sending request to {url}")
102
+ # Add API key as query parameter
103
+ response = await self.client.post(
104
+ f"{url}?key={self.api_key}",
105
+ json=request_body,
106
+ headers=headers
107
+ )
108
+ response.raise_for_status()
109
+ data = response.json()
110
+
111
+ # Extract images from predictions
112
+ images = self._extract_images(data)
113
+
114
+ if not images:
115
+ raise APIError("No image data found in Imagen API response")
116
+
117
+ return {
118
+ "images": images,
119
+ "model": model,
120
+ "response": data
121
+ }
122
+
123
+ except httpx.HTTPStatusError as e:
124
+ self._handle_http_error(e)
125
+ except Exception as e:
126
+ logger.error(f"Imagen API request failed: {e}")
127
+ raise APIError(f"Imagen API request failed: {e}")
128
+
129
+ def _extract_images(self, response_data: dict[str, Any]) -> list[str]:
130
+ """Extract base64 image data from Imagen API response."""
131
+ images = []
132
+
133
+ try:
134
+ predictions = response_data.get("predictions", [])
135
+ for prediction in predictions:
136
+ # Imagen returns base64 data in bytesBase64Encoded field
137
+ image_data = prediction.get("bytesBase64Encoded")
138
+ if image_data:
139
+ images.append(image_data)
140
+ except Exception as e:
141
+ logger.warning(f"Error extracting images from response: {e}")
142
+
143
+ return images
144
+
145
+ def _handle_http_error(self, error: httpx.HTTPStatusError) -> None:
146
+ """Handle HTTP errors and raise appropriate exceptions."""
147
+ status_code = error.response.status_code
148
+ error_text = error.response.text
149
+
150
+ logger.error(f"API request failed with status {status_code}: {error_text}")
151
+
152
+ if status_code == 401 or status_code == 403:
153
+ raise AuthenticationError(
154
+ "Authentication failed. Please check your API key.",
155
+ status_code=status_code
156
+ )
157
+ elif status_code == 429:
158
+ raise RateLimitError(
159
+ "Rate limit exceeded. Please try again later.",
160
+ status_code=status_code
161
+ )
162
+ elif status_code == 400 and "SAFETY" in error_text.upper():
163
+ raise ContentPolicyError(
164
+ "Content was blocked by safety filters. Please modify your prompt.",
165
+ status_code=status_code
166
+ )
167
+ else:
168
+ raise APIError(
169
+ f"API request failed with status {status_code}: {error_text}",
170
+ status_code=status_code
171
+ )
172
+
173
+ async def close(self) -> None:
174
+ """Close the HTTP client."""
175
+ await self.client.aclose()
@@ -0,0 +1,140 @@
1
+ """
2
+ Prompt enhancement service using Gemini Flash.
3
+ Automatically optimizes prompts for better image generation results.
4
+ """
5
+
6
+ import logging
7
+ from typing import Any
8
+
9
+ from .gemini_client import GeminiClient
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ PROMPT_ENHANCEMENT_SYSTEM_INSTRUCTION = """You are an expert prompt engineer for AI image generation models. Your task is to enhance user prompts to produce the best possible results.
15
+
16
+ Follow these guidelines:
17
+ 1. Preserve the user's core intent and subject matter
18
+ 2. Add specific, professional details about:
19
+ - Composition (framing, perspective, angle)
20
+ - Lighting (type, quality, direction, mood)
21
+ - Materials and textures
22
+ - Atmosphere and mood
23
+ - Artistic style (if appropriate)
24
+ 3. Use photographic and cinematic terminology when relevant
25
+ 4. Be hyper-specific rather than generic
26
+ 5. For portraits: describe features, expressions, clothing
27
+ 6. For scenes: describe environment, weather, time of day
28
+ 7. Keep prompts concise but detailed (aim for 100-300 words)
29
+ 8. Output ONLY the enhanced prompt, no explanations"""
30
+
31
+
32
+ class PromptEnhancer:
33
+ """Service for enhancing image generation prompts."""
34
+
35
+ def __init__(self, gemini_client: GeminiClient):
36
+ """
37
+ Initialize prompt enhancer.
38
+
39
+ Args:
40
+ gemini_client: Gemini client for text generation
41
+ """
42
+ self.gemini_client = gemini_client
43
+
44
+ async def enhance_prompt(
45
+ self,
46
+ original_prompt: str,
47
+ *,
48
+ context: dict[str, Any] | None = None,
49
+ ) -> dict[str, str]:
50
+ """
51
+ Enhance a prompt for better image generation.
52
+
53
+ Args:
54
+ original_prompt: Original user prompt
55
+ context: Optional context (features, image type, etc.)
56
+
57
+ Returns:
58
+ Dict with 'enhanced_prompt' and 'original_prompt'
59
+ """
60
+ # Build enhancement instruction
61
+ instruction = self._build_enhancement_instruction(original_prompt, context)
62
+
63
+ try:
64
+ enhanced = await self.gemini_client.generate_text(
65
+ prompt=instruction,
66
+ system_instruction=PROMPT_ENHANCEMENT_SYSTEM_INSTRUCTION,
67
+ model="gemini-flash-latest"
68
+ )
69
+
70
+ # Clean up the enhanced prompt
71
+ enhanced = enhanced.strip()
72
+
73
+ logger.info(f"Enhanced prompt: {len(original_prompt)} -> {len(enhanced)} chars")
74
+
75
+ return {
76
+ "original_prompt": original_prompt,
77
+ "enhanced_prompt": enhanced,
78
+ }
79
+
80
+ except Exception as e:
81
+ logger.warning(f"Prompt enhancement failed, using original: {e}")
82
+ return {
83
+ "original_prompt": original_prompt,
84
+ "enhanced_prompt": original_prompt,
85
+ }
86
+
87
+ def _build_enhancement_instruction(
88
+ self,
89
+ prompt: str,
90
+ context: dict[str, Any] | None
91
+ ) -> str:
92
+ """Build the instruction for prompt enhancement."""
93
+ instruction_parts = [f"Enhance this image generation prompt:\n\n{prompt}"]
94
+
95
+ if context:
96
+ # Add context hints
97
+ if context.get("is_editing"):
98
+ instruction_parts.append("\nContext: This is for image editing/modification")
99
+
100
+ if context.get("maintain_character_consistency"):
101
+ instruction_parts.append(
102
+ "\nIMPORTANT: Describe the character with specific, consistent features "
103
+ "for use across multiple generations"
104
+ )
105
+
106
+ if context.get("blend_images"):
107
+ instruction_parts.append(
108
+ "\nContext: Multiple images will be blended. Describe how elements "
109
+ "should be composed naturally together"
110
+ )
111
+
112
+ if context.get("use_world_knowledge"):
113
+ instruction_parts.append(
114
+ "\nContext: Include accurate real-world details for historical figures, "
115
+ "landmarks, or factual scenarios"
116
+ )
117
+
118
+ if context.get("aspect_ratio"):
119
+ ratio = context["aspect_ratio"]
120
+ if ratio in ["16:9", "21:9"]:
121
+ instruction_parts.append("\nFormat: Wide landscape composition")
122
+ elif ratio in ["9:16", "2:3", "3:4"]:
123
+ instruction_parts.append("\nFormat: Vertical/portrait composition")
124
+
125
+ return "\n".join(instruction_parts)
126
+
127
+
128
+ async def create_prompt_enhancer(api_key: str, timeout: int = 30) -> PromptEnhancer:
129
+ """
130
+ Factory function to create prompt enhancer.
131
+
132
+ Args:
133
+ api_key: Gemini API key
134
+ timeout: Request timeout
135
+
136
+ Returns:
137
+ PromptEnhancer instance
138
+ """
139
+ gemini_client = GeminiClient(api_key=api_key, timeout=timeout)
140
+ return PromptEnhancer(gemini_client)
src/tools/__init__.py ADDED
@@ -0,0 +1,11 @@
1
+ """Tools module for Ultimate Gemini MCP."""
2
+
3
+ from .batch_generate import batch_generate_images, register_batch_generate_tool
4
+ from .generate_image import generate_image_tool, register_generate_image_tool
5
+
6
+ __all__ = [
7
+ "generate_image_tool",
8
+ "register_generate_image_tool",
9
+ "batch_generate_images",
10
+ "register_batch_generate_tool",
11
+ ]
@@ -0,0 +1,159 @@
1
+ """
2
+ Batch image generation tool for processing multiple prompts efficiently.
3
+ """
4
+
5
+ import asyncio
6
+ import json
7
+ import logging
8
+ from typing import Any
9
+
10
+ from ..config import MAX_BATCH_SIZE, get_settings
11
+ from ..core import validate_batch_size, validate_prompts_list
12
+ from .generate_image import generate_image_tool
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ async def batch_generate_images(
18
+ prompts: list[str],
19
+ model: str | None = None,
20
+ enhance_prompt: bool = True,
21
+ aspect_ratio: str = "1:1",
22
+ output_format: str = "png",
23
+ batch_size: int | None = None,
24
+ **shared_params: Any,
25
+ ) -> dict[str, Any]:
26
+ """
27
+ Generate multiple images from a list of prompts.
28
+
29
+ Args:
30
+ prompts: List of text prompts
31
+ model: Model to use for all images
32
+ enhance_prompt: Enhance all prompts
33
+ aspect_ratio: Aspect ratio for all images
34
+ output_format: Output format for all images
35
+ batch_size: Number of images to process in parallel (default: from config)
36
+ **shared_params: Additional parameters shared across all generations
37
+
38
+ Returns:
39
+ Dict with batch results
40
+ """
41
+ # Validate inputs
42
+ validate_prompts_list(prompts)
43
+
44
+ settings = get_settings()
45
+ if batch_size is None:
46
+ batch_size = settings.api.max_batch_size
47
+
48
+ validate_batch_size(batch_size, MAX_BATCH_SIZE)
49
+
50
+ # Prepare results
51
+ results = {
52
+ "success": True,
53
+ "total_prompts": len(prompts),
54
+ "batch_size": batch_size,
55
+ "completed": 0,
56
+ "failed": 0,
57
+ "results": []
58
+ }
59
+
60
+ # Process prompts in batches
61
+ for i in range(0, len(prompts), batch_size):
62
+ batch = prompts[i:i + batch_size]
63
+ logger.info(f"Processing batch {i // batch_size + 1}: {len(batch)} prompts")
64
+
65
+ # Create tasks for parallel processing
66
+ tasks = [
67
+ generate_image_tool(
68
+ prompt=prompt,
69
+ model=model,
70
+ enhance_prompt=enhance_prompt,
71
+ aspect_ratio=aspect_ratio,
72
+ output_format=output_format,
73
+ number_of_images=1,
74
+ **shared_params
75
+ )
76
+ for prompt in batch
77
+ ]
78
+
79
+ # Execute batch
80
+ batch_results = await asyncio.gather(*tasks, return_exceptions=True)
81
+
82
+ # Process results
83
+ for j, result in enumerate(batch_results):
84
+ prompt_index = i + j
85
+
86
+ if isinstance(result, Exception):
87
+ logger.error(f"Failed to generate image for prompt {prompt_index}: {result}")
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
+ })
95
+ else:
96
+ results["completed"] += 1
97
+ results["results"].append({
98
+ "prompt_index": prompt_index,
99
+ "prompt": batch[j],
100
+ **result
101
+ })
102
+
103
+ return results
104
+
105
+
106
+ def register_batch_generate_tool(mcp_server: Any) -> None:
107
+ """Register batch_generate tool with MCP server."""
108
+
109
+ @mcp_server.tool()
110
+ async def batch_generate(
111
+ prompts: list[str],
112
+ model: str | None = None,
113
+ enhance_prompt: bool = True,
114
+ aspect_ratio: str = "1:1",
115
+ output_format: str = "png",
116
+ batch_size: int | None = None,
117
+ person_generation: str = "allow_adult",
118
+ negative_prompt: str | None = None,
119
+ ) -> str:
120
+ """
121
+ Generate multiple images from a list of prompts efficiently.
122
+
123
+ Processes prompts in parallel batches for optimal performance.
124
+ All images share the same generation settings.
125
+
126
+ Args:
127
+ prompts: List of text descriptions for image generation
128
+ model: Model to use for all images (default: gemini-2.5-flash-image)
129
+ enhance_prompt: Enhance all prompts automatically (default: True)
130
+ aspect_ratio: Aspect ratio for all images (default: 1:1)
131
+ output_format: Image format for all images (default: png)
132
+ batch_size: Parallel batch size (default: from config)
133
+ person_generation: Person policy for Imagen models (default: allow_adult)
134
+ negative_prompt: Negative prompt for Imagen models (optional)
135
+
136
+ Returns:
137
+ JSON string with batch results including individual image paths
138
+ """
139
+ try:
140
+ result = await batch_generate_images(
141
+ prompts=prompts,
142
+ model=model,
143
+ enhance_prompt=enhance_prompt,
144
+ aspect_ratio=aspect_ratio,
145
+ output_format=output_format,
146
+ batch_size=batch_size,
147
+ person_generation=person_generation,
148
+ negative_prompt=negative_prompt,
149
+ )
150
+
151
+ return json.dumps(result, indent=2)
152
+
153
+ except Exception as e:
154
+ 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)