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.

src/__init__.py ADDED
@@ -0,0 +1,16 @@
1
+ """
2
+ Ultimate Gemini MCP Server
3
+
4
+ A unified MCP server that combines the best features from:
5
+ - Gemini 2.5 Flash Image (with prompt enhancement)
6
+ - Imagen 3, 4, and 4-Ultra
7
+ - Advanced features: batch processing, editing, templates, and more
8
+ """
9
+
10
+ __version__ = "1.0.1"
11
+ __author__ = "Ultimate Gemini MCP"
12
+
13
+ from .config import get_settings
14
+ from .server import create_app, main
15
+
16
+ __all__ = ["create_app", "main", "get_settings", "__version__"]
src/config/__init__.py ADDED
@@ -0,0 +1,32 @@
1
+ """Configuration module for Ultimate Gemini MCP."""
2
+
3
+ from .constants import (
4
+ ALL_MODELS,
5
+ ASPECT_RATIOS,
6
+ DEFAULT_GEMINI_MODEL,
7
+ DEFAULT_IMAGEN_MODEL,
8
+ GEMINI_MODELS,
9
+ IMAGE_FORMATS,
10
+ IMAGEN_MODELS,
11
+ MAX_BATCH_SIZE,
12
+ MAX_IMAGES_PER_REQUEST,
13
+ PERSON_GENERATION_OPTIONS,
14
+ )
15
+ from .settings import APIConfig, ServerConfig, Settings, get_settings
16
+
17
+ __all__ = [
18
+ "ALL_MODELS",
19
+ "ASPECT_RATIOS",
20
+ "DEFAULT_GEMINI_MODEL",
21
+ "DEFAULT_IMAGEN_MODEL",
22
+ "GEMINI_MODELS",
23
+ "IMAGE_FORMATS",
24
+ "IMAGEN_MODELS",
25
+ "MAX_BATCH_SIZE",
26
+ "MAX_IMAGES_PER_REQUEST",
27
+ "PERSON_GENERATION_OPTIONS",
28
+ "APIConfig",
29
+ "ServerConfig",
30
+ "Settings",
31
+ "get_settings",
32
+ ]
@@ -0,0 +1,77 @@
1
+ """
2
+ Constants and model definitions for both Gemini and Imagen APIs.
3
+ """
4
+
5
+ from typing import Final
6
+
7
+ # API Endpoints
8
+ GEMINI_API_BASE: Final[str] = "https://generativelanguage.googleapis.com/v1beta"
9
+ IMAGEN_API_BASE: Final[str] = "https://generativelanguage.googleapis.com/v1beta"
10
+
11
+ # Gemini Models (generateContent API)
12
+ GEMINI_MODELS = {
13
+ "gemini-2.5-flash-image": "gemini-2.5-flash-image",
14
+ "gemini-flash-latest": "gemini-flash-latest", # For prompt enhancement (non-image)
15
+ }
16
+
17
+ # Imagen Models (predict API)
18
+ IMAGEN_MODELS = {
19
+ "imagen-4": "models/imagen-4.0-generate-001",
20
+ "imagen-4-fast": "models/imagen-4.0-fast-generate-001",
21
+ "imagen-4-ultra": "models/imagen-4.0-ultra-generate-001",
22
+ }
23
+
24
+ # All available models
25
+ ALL_MODELS = {**GEMINI_MODELS, **IMAGEN_MODELS}
26
+
27
+ # Default models
28
+ DEFAULT_GEMINI_MODEL = "gemini-2.5-flash-image"
29
+ DEFAULT_IMAGEN_MODEL = "imagen-4-ultra"
30
+ DEFAULT_ENHANCEMENT_MODEL = "gemini-flash-latest"
31
+
32
+ # Aspect ratios
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
41
+ "9:16", # Vertical mobile
42
+ "16:9", # Widescreen
43
+ "21:9", # Ultrawide
44
+ ]
45
+
46
+ # Image formats
47
+ IMAGE_FORMATS = {
48
+ "png": "image/png",
49
+ "jpeg": "image/jpeg",
50
+ "jpg": "image/jpeg",
51
+ "webp": "image/webp",
52
+ }
53
+
54
+ # Person generation options (Imagen API)
55
+ PERSON_GENERATION_OPTIONS = [
56
+ "dont_allow",
57
+ "allow_adult",
58
+ "allow_all",
59
+ ]
60
+
61
+ # Generation limits
62
+ MAX_IMAGES_PER_REQUEST = 4
63
+ MAX_BATCH_SIZE = 8
64
+ MAX_PROMPT_LENGTH = 8192
65
+ MAX_NEGATIVE_PROMPT_LENGTH = 1024
66
+
67
+ # File size limits
68
+ MAX_IMAGE_SIZE_MB = 20
69
+ MAX_IMAGE_SIZE_BYTES = MAX_IMAGE_SIZE_MB * 1024 * 1024
70
+
71
+ # Timeout settings (in seconds)
72
+ DEFAULT_TIMEOUT = 60
73
+ ENHANCEMENT_TIMEOUT = 30
74
+ BATCH_TIMEOUT = 120
75
+
76
+ # Output settings
77
+ DEFAULT_OUTPUT_DIR = "generated_images"
src/config/settings.py ADDED
@@ -0,0 +1,143 @@
1
+ """
2
+ Configuration settings for the Ultimate Gemini MCP server.
3
+ """
4
+
5
+ import os
6
+ from pathlib import Path
7
+ from typing import Optional
8
+
9
+ from pydantic import Field
10
+ from pydantic_settings import BaseSettings, SettingsConfigDict
11
+
12
+ from .constants import (
13
+ DEFAULT_ENHANCEMENT_MODEL,
14
+ DEFAULT_GEMINI_MODEL,
15
+ DEFAULT_IMAGEN_MODEL,
16
+ DEFAULT_OUTPUT_DIR,
17
+ DEFAULT_TIMEOUT,
18
+ MAX_BATCH_SIZE,
19
+ )
20
+
21
+
22
+ class ServerConfig(BaseSettings):
23
+ """Server configuration settings."""
24
+
25
+ model_config = SettingsConfigDict(
26
+ env_file=".env",
27
+ env_file_encoding="utf-8",
28
+ case_sensitive=False,
29
+ extra="ignore",
30
+ )
31
+
32
+ # Server settings
33
+ transport: str = Field(default="stdio", description="Transport mode: stdio or http")
34
+ host: str = Field(default="localhost", description="Host for HTTP transport")
35
+ port: int = Field(default=8000, description="Port for HTTP transport")
36
+
37
+ # Logging settings
38
+ log_level: str = Field(default="INFO", description="Logging level")
39
+ log_format: str = Field(default="standard", description="Log format: standard, json, detailed")
40
+
41
+ # Output settings
42
+ output_dir: str = Field(
43
+ default=DEFAULT_OUTPUT_DIR, description="Directory for generated images"
44
+ )
45
+
46
+ @classmethod
47
+ def from_env(cls) -> "ServerConfig":
48
+ """Load configuration from environment variables."""
49
+ return cls()
50
+
51
+
52
+ class APIConfig(BaseSettings):
53
+ """API configuration for Gemini and Imagen."""
54
+
55
+ model_config = SettingsConfigDict(
56
+ env_file=".env",
57
+ env_file_encoding="utf-8",
58
+ case_sensitive=False,
59
+ extra="ignore",
60
+ )
61
+
62
+ # API Keys
63
+ gemini_api_key: str = Field(
64
+ default="",
65
+ alias="GEMINI_API_KEY",
66
+ description="Gemini API key (also accepts GOOGLE_API_KEY)",
67
+ )
68
+
69
+ # Model settings
70
+ default_gemini_model: str = Field(
71
+ default=DEFAULT_GEMINI_MODEL, description="Default Gemini model"
72
+ )
73
+ default_imagen_model: str = Field(
74
+ default=DEFAULT_IMAGEN_MODEL, description="Default Imagen model"
75
+ )
76
+ enhancement_model: str = Field(
77
+ default=DEFAULT_ENHANCEMENT_MODEL, description="Model for prompt enhancement"
78
+ )
79
+
80
+ # Feature flags
81
+ enable_prompt_enhancement: bool = Field(
82
+ default=True, description="Enable automatic prompt enhancement"
83
+ )
84
+ enable_batch_processing: bool = Field(
85
+ default=True, description="Enable batch processing"
86
+ )
87
+
88
+ # Request settings
89
+ request_timeout: int = Field(default=DEFAULT_TIMEOUT, description="API request timeout")
90
+ max_batch_size: int = Field(
91
+ default=MAX_BATCH_SIZE, description="Maximum batch size for parallel requests"
92
+ )
93
+ max_retries: int = Field(default=3, description="Maximum number of retries for failed requests")
94
+
95
+ # Image settings
96
+ default_aspect_ratio: str = Field(default="1:1", description="Default aspect ratio")
97
+ default_output_format: str = Field(default="png", description="Default output format")
98
+
99
+ def __init__(self, **kwargs):
100
+ """Initialize API configuration with fallback for API key."""
101
+ super().__init__(**kwargs)
102
+ # Fallback to GOOGLE_API_KEY if GEMINI_API_KEY not set
103
+ if not self.gemini_api_key:
104
+ self.gemini_api_key = os.getenv("GOOGLE_API_KEY", "")
105
+
106
+ if not self.gemini_api_key:
107
+ raise ValueError(
108
+ "GEMINI_API_KEY or GOOGLE_API_KEY environment variable is required"
109
+ )
110
+
111
+ @classmethod
112
+ def from_env(cls) -> "APIConfig":
113
+ """Load API configuration from environment variables."""
114
+ return cls()
115
+
116
+
117
+ class Settings:
118
+ """Combined settings for the server."""
119
+
120
+ def __init__(self):
121
+ self.server = ServerConfig.from_env()
122
+ self.api = APIConfig.from_env()
123
+
124
+ # Ensure output directory exists
125
+ output_path = Path(self.server.output_dir)
126
+ output_path.mkdir(parents=True, exist_ok=True)
127
+
128
+ @property
129
+ def output_dir(self) -> Path:
130
+ """Get output directory as Path object."""
131
+ return Path(self.server.output_dir)
132
+
133
+
134
+ # Global settings instance (lazy initialization)
135
+ _settings: Optional[Settings] = None
136
+
137
+
138
+ def get_settings() -> Settings:
139
+ """Get or create global settings instance."""
140
+ global _settings
141
+ if _settings is None:
142
+ _settings = Settings()
143
+ return _settings
src/core/__init__.py ADDED
@@ -0,0 +1,55 @@
1
+ """Core modules for Ultimate Gemini MCP."""
2
+
3
+ from .exceptions import (
4
+ APIError,
5
+ AuthenticationError,
6
+ ConfigurationError,
7
+ ContentPolicyError,
8
+ FileOperationError,
9
+ ImageProcessingError,
10
+ RateLimitError,
11
+ UltimateGeminiError,
12
+ ValidationError,
13
+ )
14
+ from .validation import (
15
+ sanitize_filename,
16
+ validate_aspect_ratio,
17
+ validate_base64_image,
18
+ validate_batch_size,
19
+ validate_file_path,
20
+ validate_image_format,
21
+ validate_model,
22
+ validate_negative_prompt,
23
+ validate_number_of_images,
24
+ validate_person_generation,
25
+ validate_prompt,
26
+ validate_prompts_list,
27
+ validate_seed,
28
+ )
29
+
30
+ __all__ = [
31
+ # Exceptions
32
+ "UltimateGeminiError",
33
+ "ConfigurationError",
34
+ "ValidationError",
35
+ "APIError",
36
+ "AuthenticationError",
37
+ "RateLimitError",
38
+ "ContentPolicyError",
39
+ "ImageProcessingError",
40
+ "FileOperationError",
41
+ # Validation
42
+ "validate_prompt",
43
+ "validate_negative_prompt",
44
+ "validate_model",
45
+ "validate_aspect_ratio",
46
+ "validate_number_of_images",
47
+ "validate_image_format",
48
+ "validate_person_generation",
49
+ "validate_seed",
50
+ "validate_file_path",
51
+ "validate_base64_image",
52
+ "validate_prompts_list",
53
+ "validate_batch_size",
54
+ "sanitize_filename",
55
+ ]
src/core/exceptions.py ADDED
@@ -0,0 +1,60 @@
1
+ """
2
+ Custom exceptions for the Ultimate Gemini MCP server.
3
+ """
4
+
5
+
6
+ class UltimateGeminiError(Exception):
7
+ """Base exception for all Ultimate Gemini MCP errors."""
8
+
9
+ pass
10
+
11
+
12
+ class ConfigurationError(UltimateGeminiError):
13
+ """Raised when there's a configuration error."""
14
+
15
+ pass
16
+
17
+
18
+ class ValidationError(UltimateGeminiError):
19
+ """Raised when input validation fails."""
20
+
21
+ pass
22
+
23
+
24
+ class APIError(UltimateGeminiError):
25
+ """Raised when an API request fails."""
26
+
27
+ def __init__(self, message: str, status_code: int | None = None, response_data: dict | None = None):
28
+ super().__init__(message)
29
+ self.status_code = status_code
30
+ self.response_data = response_data
31
+
32
+
33
+ class AuthenticationError(APIError):
34
+ """Raised when API authentication fails."""
35
+
36
+ pass
37
+
38
+
39
+ class RateLimitError(APIError):
40
+ """Raised when API rate limit is exceeded."""
41
+
42
+ pass
43
+
44
+
45
+ class ContentPolicyError(APIError):
46
+ """Raised when content violates safety policies."""
47
+
48
+ pass
49
+
50
+
51
+ class ImageProcessingError(UltimateGeminiError):
52
+ """Raised when image processing fails."""
53
+
54
+ pass
55
+
56
+
57
+ class FileOperationError(UltimateGeminiError):
58
+ """Raised when file operations fail."""
59
+
60
+ pass
src/core/validation.py ADDED
@@ -0,0 +1,161 @@
1
+ """
2
+ Input validation utilities.
3
+ """
4
+
5
+ import base64
6
+ import re
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+ from ..config.constants import (
11
+ ALL_MODELS,
12
+ ASPECT_RATIOS,
13
+ IMAGE_FORMATS,
14
+ MAX_IMAGES_PER_REQUEST,
15
+ MAX_NEGATIVE_PROMPT_LENGTH,
16
+ MAX_PROMPT_LENGTH,
17
+ PERSON_GENERATION_OPTIONS,
18
+ )
19
+ from .exceptions import ValidationError
20
+
21
+
22
+ def validate_prompt(prompt: str) -> None:
23
+ """Validate prompt text."""
24
+ if not prompt or not prompt.strip():
25
+ raise ValidationError("Prompt cannot be empty")
26
+
27
+ if len(prompt) > MAX_PROMPT_LENGTH:
28
+ raise ValidationError(
29
+ f"Prompt too long: {len(prompt)} characters (max {MAX_PROMPT_LENGTH})"
30
+ )
31
+
32
+
33
+ def validate_negative_prompt(negative_prompt: str | None) -> None:
34
+ """Validate negative prompt text."""
35
+ if negative_prompt and len(negative_prompt) > MAX_NEGATIVE_PROMPT_LENGTH:
36
+ raise ValidationError(
37
+ f"Negative prompt too long: {len(negative_prompt)} characters "
38
+ f"(max {MAX_NEGATIVE_PROMPT_LENGTH})"
39
+ )
40
+
41
+
42
+ def validate_model(model: str) -> None:
43
+ """Validate model name."""
44
+ if model not in ALL_MODELS:
45
+ available = ", ".join(ALL_MODELS.keys())
46
+ raise ValidationError(f"Invalid model '{model}'. Available models: {available}")
47
+
48
+
49
+ def validate_aspect_ratio(aspect_ratio: str) -> None:
50
+ """Validate aspect ratio."""
51
+ if aspect_ratio not in ASPECT_RATIOS:
52
+ available = ", ".join(ASPECT_RATIOS)
53
+ raise ValidationError(
54
+ f"Invalid aspect ratio '{aspect_ratio}'. Available: {available}"
55
+ )
56
+
57
+
58
+ def validate_number_of_images(num: int) -> None:
59
+ """Validate number of images to generate."""
60
+ if not isinstance(num, int) or num < 1:
61
+ raise ValidationError(f"Number of images must be at least 1, got {num}")
62
+
63
+ if num > MAX_IMAGES_PER_REQUEST:
64
+ raise ValidationError(
65
+ f"Number of images exceeds maximum: {num} > {MAX_IMAGES_PER_REQUEST}"
66
+ )
67
+
68
+
69
+ def validate_image_format(format_str: str) -> None:
70
+ """Validate image format."""
71
+ if format_str.lower() not in IMAGE_FORMATS:
72
+ 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
+ )
85
+
86
+
87
+ def validate_seed(seed: int | None) -> None:
88
+ """Validate seed value."""
89
+ if seed is not None:
90
+ if not isinstance(seed, int):
91
+ raise ValidationError(f"Seed must be an integer, got {type(seed).__name__}")
92
+ if seed < 0:
93
+ raise ValidationError(f"Seed must be non-negative, got {seed}")
94
+
95
+
96
+ def validate_file_path(path: str) -> Path:
97
+ """Validate and return file path."""
98
+ try:
99
+ file_path = Path(path).resolve()
100
+ except Exception as e:
101
+ raise ValidationError(f"Invalid file path '{path}': {e}")
102
+
103
+ if not file_path.exists():
104
+ raise ValidationError(f"File does not exist: {file_path}")
105
+
106
+ if not file_path.is_file():
107
+ raise ValidationError(f"Path is not a file: {file_path}")
108
+
109
+ return file_path
110
+
111
+
112
+ def validate_base64_image(data: str) -> None:
113
+ """Validate base64-encoded image data."""
114
+ if not data:
115
+ raise ValidationError("Base64 image data cannot be empty")
116
+
117
+ try:
118
+ # Try to decode to verify it's valid base64
119
+ decoded = base64.b64decode(data, validate=True)
120
+ if len(decoded) == 0:
121
+ raise ValidationError("Decoded image data is empty")
122
+ except Exception as e:
123
+ raise ValidationError(f"Invalid base64 image data: {e}")
124
+
125
+
126
+ def validate_prompts_list(prompts: list[str]) -> None:
127
+ """Validate list of prompts for batch processing."""
128
+ if not isinstance(prompts, list):
129
+ raise ValidationError("Prompts must be a list")
130
+
131
+ if not prompts:
132
+ raise ValidationError("Prompts list cannot be empty")
133
+
134
+ for i, prompt in enumerate(prompts):
135
+ if not isinstance(prompt, str):
136
+ raise ValidationError(f"Prompt at index {i} must be a string")
137
+ try:
138
+ validate_prompt(prompt)
139
+ except ValidationError as e:
140
+ raise ValidationError(f"Invalid prompt at index {i}: {e}")
141
+
142
+
143
+ def sanitize_filename(filename: str) -> str:
144
+ """Sanitize filename to remove unsafe characters."""
145
+ # Remove or replace unsafe characters
146
+ safe_name = re.sub(r'[<>:"/\\|?*\x00-\x1f]', "_", filename)
147
+ # Remove leading/trailing dots and spaces
148
+ safe_name = safe_name.strip(". ")
149
+ # Ensure filename is not empty
150
+ if not safe_name:
151
+ safe_name = "image"
152
+ return safe_name
153
+
154
+
155
+ def validate_batch_size(size: int, max_size: int) -> None:
156
+ """Validate batch size."""
157
+ if not isinstance(size, int) or size < 1:
158
+ raise ValidationError(f"Batch size must be at least 1, got {size}")
159
+
160
+ if size > max_size:
161
+ raise ValidationError(f"Batch size exceeds maximum: {size} > {max_size}")