tomo2chin2 commited on
Commit
3f4aae4
·
verified ·
1 Parent(s): 4dc437c

Upload 7 files

Browse files
Files changed (7) hide show
  1. .gitignore +50 -0
  2. CLAUDE.md +21 -0
  3. README.md +86 -4
  4. app.py +287 -0
  5. app.yaml +9 -0
  6. gemini_sample_code.txt +81 -0
  7. requirements.txt +7 -0
.gitignore ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual Environment
24
+ venv/
25
+ ENV/
26
+ env/
27
+ .env
28
+
29
+ # IDE
30
+ .vscode/
31
+ .idea/
32
+ *.swp
33
+ *.swo
34
+ *~
35
+
36
+ # OS
37
+ .DS_Store
38
+ Thumbs.db
39
+
40
+ # Project specific
41
+ *.log
42
+ *.png
43
+ *.jpg
44
+ *.jpeg
45
+ *.gif
46
+ *.bmp
47
+ generated_images/
48
+
49
+ # Hugging Face Spaces
50
+ .gradio/
CLAUDE.md ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## 重要な指示
6
+
7
+ - **常に日本語で会話すること**
8
+ - このプロジェクトはHugging Face Spacesで動作するGradio 5.31.0を使用したアプリケーションの開発が目的です。
9
+ - 開発するアプリケーションはClaudeCodeに使用させるための画像生成MCPサーバーです。画像生成にはgemini2.0flashを活用します。サンプルコードはgemini_sample_code.txt
10
+
11
+ ## Project Overview
12
+
13
+ ImageGenMCP - Hugging Face Spaces上で動作する画像生成アプリケーション(Gradio 5.31.0使用)
14
+
15
+ ## Development Commands
16
+
17
+ *To be updated once the project is initialized with package.json or other configuration files.*
18
+
19
+ ## Architecture
20
+
21
+ *To be documented once the codebase structure is established.*
README.md CHANGED
@@ -1,12 +1,94 @@
1
  ---
2
  title: ImageGenMCP
3
- emoji: 🌍
4
- colorFrom: green
5
  colorTo: purple
6
  sdk: gradio
7
- sdk_version: 5.32.0
8
  app_file: app.py
9
  pinned: false
 
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  title: ImageGenMCP
3
+ emoji: 🎨
4
+ colorFrom: blue
5
  colorTo: purple
6
  sdk: gradio
7
+ sdk_version: 5.31.0
8
  app_file: app.py
9
  pinned: false
10
+ license: mit
11
  ---
12
 
13
+ # ImageGenMCP - Image Generation MCP Server
14
+
15
+ A Gradio-based image generation application using Google's Gemini 2.0 Flash Preview model, designed to work as an MCP (Model Context Protocol) server for Claude Code.
16
+
17
+ ## Features
18
+
19
+ - 🎨 High-quality image generation using Gemini 2.0 Flash Preview
20
+ - 🖼️ Support for reference images to guide generation
21
+ - 🔧 MCP server integration for Claude Code
22
+ - 🌐 Web interface powered by Gradio 5.31.0
23
+ - 📝 Detailed logging for debugging
24
+
25
+ ## Setup
26
+
27
+ ### Environment Variables
28
+
29
+ Set your Gemini API key:
30
+
31
+ ```bash
32
+ export GEMINI_API_KEY="your-gemini-api-key"
33
+ ```
34
+
35
+ ### Local Development
36
+
37
+ 1. Install dependencies:
38
+ ```bash
39
+ pip install -r requirements.txt
40
+ ```
41
+
42
+ 2. Run the application:
43
+
44
+ **Web UI mode:**
45
+ ```bash
46
+ python app.py
47
+ ```
48
+
49
+ **MCP server mode:**
50
+ ```bash
51
+ python app.py --mcp
52
+ ```
53
+
54
+ ## Usage with Claude Code
55
+
56
+ Add this configuration to your Claude Code settings:
57
+
58
+ ```json
59
+ {
60
+ "mcpServers": {
61
+ "image-gen": {
62
+ "command": "python",
63
+ "args": ["/path/to/app.py", "--mcp"],
64
+ "env": {
65
+ "GEMINI_API_KEY": "your-api-key"
66
+ }
67
+ }
68
+ }
69
+ }
70
+ ```
71
+
72
+ ## API
73
+
74
+ ### MCP Tool: `generate_image`
75
+
76
+ **Description:** Generates images using Gemini 2.0 Flash Preview
77
+
78
+ **Parameters:**
79
+ - `prompt` (string, required): Description of the image to generate
80
+
81
+ **Returns:**
82
+ - `success` (boolean): Whether generation was successful
83
+ - `message` (string): Status message
84
+ - `image_base64` (string): Base64-encoded PNG image (if successful)
85
+
86
+ ## Requirements
87
+
88
+ - Python 3.8+
89
+ - Gemini API key with access to `gemini-2.0-flash-preview-image-generation` model
90
+ - Dependencies listed in `requirements.txt`
91
+
92
+ ## License
93
+
94
+ MIT
app.py ADDED
@@ -0,0 +1,287 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ import base64
4
+ import io
5
+ from PIL import Image
6
+ from google import genai
7
+ from google.genai import types
8
+ import json
9
+ import asyncio
10
+ from typing import Dict, Any, List, Optional
11
+ from mcp import Server, NotificationOptions
12
+ from mcp.server.models import InitializationOptions
13
+ import mcp.server.stdio
14
+ import mcp.types as types_mcp
15
+ import logging
16
+ import traceback
17
+
18
+ # ログ設定
19
+ logging.basicConfig(
20
+ level=logging.INFO,
21
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
22
+ )
23
+ logger = logging.getLogger(__name__)
24
+
25
+ # Gemini APIクライアントの初期化
26
+ def get_gemini_client():
27
+ api_key = os.environ.get("GEMINI_API_KEY")
28
+ if not api_key:
29
+ raise ValueError("GEMINI_API_KEY環境変数が設定されていません")
30
+ return genai.Client(api_key=api_key)
31
+
32
+ # 画像生成関数
33
+ def generate_image(prompt: str, previous_image: Optional[Image.Image] = None) -> tuple[Optional[Image.Image], str]:
34
+ """
35
+ Gemini 2.0 Flashを使用して画像を生成する
36
+
37
+ Args:
38
+ prompt: 生成したい画像の説明
39
+ previous_image: 参考にする前の画像(オプション)
40
+
41
+ Returns:
42
+ 生成された画像とステータスメッセージ
43
+ """
44
+ try:
45
+ logger.info(f"画像生成開始: プロンプト='{prompt[:50]}...', 参考画像={'あり' if previous_image else 'なし'}")
46
+ client = get_gemini_client()
47
+ model = "gemini-2.0-flash-preview-image-generation" # 画像生成対応モデル
48
+
49
+ # コンテンツの準備
50
+ contents = []
51
+
52
+ # ユーザープロンプトの追加
53
+ contents.append(
54
+ types.Content(
55
+ role="user",
56
+ parts=[types.Part.from_text(text=prompt)]
57
+ )
58
+ )
59
+
60
+ # 前の画像がある場合は追加
61
+ if previous_image:
62
+ # PILイメージをbase64に変換
63
+ buffered = io.BytesIO()
64
+ previous_image.save(buffered, format="PNG")
65
+ img_data = base64.b64encode(buffered.getvalue()).decode()
66
+
67
+ contents.append(
68
+ types.Content(
69
+ role="model",
70
+ parts=[
71
+ types.Part.from_bytes(
72
+ mime_type="image/png",
73
+ data=base64.b64decode(img_data)
74
+ )
75
+ ]
76
+ )
77
+ )
78
+
79
+ # 追加の指示
80
+ contents.append(
81
+ types.Content(
82
+ role="user",
83
+ parts=[types.Part.from_text(text="上記の画像を参考に、以下の要望に従って新しい画像を生成してください: " + prompt)]
84
+ )
85
+ )
86
+
87
+ # 生成設定
88
+ generate_content_config = types.GenerateContentConfig(
89
+ response_modalities=["IMAGE"],
90
+ # response_mime_typeは指定しない(Gemini 2.0 Flashの仕様に合わせる)
91
+ )
92
+
93
+ # 画像生成
94
+ logger.info("Gemini APIを呼び出し中...")
95
+ response = client.models.generate_content(
96
+ model=model,
97
+ contents=contents,
98
+ config=generate_content_config
99
+ )
100
+ logger.info("Gemini APIの呼び出し完了")
101
+
102
+ # レスポンスから画像を取得
103
+ if (response.candidates and
104
+ response.candidates[0].content and
105
+ response.candidates[0].content.parts and
106
+ response.candidates[0].content.parts[0].inline_data):
107
+
108
+ image_data = response.candidates[0].content.parts[0].inline_data.data
109
+ image = Image.open(io.BytesIO(image_data))
110
+
111
+ logger.info(f"画像生成成功: サイズ={image.size}")
112
+ return image, "画像生成に成功しました!"
113
+ else:
114
+ logger.warning("レスポンスに画像データが含まれていません")
115
+ return None, "画像の生成に失敗しました。レスポンスに画像データが含まれていません。"
116
+
117
+ except Exception as e:
118
+ logger.error(f"画像生成エラー: {str(e)}")
119
+ logger.error(traceback.format_exc())
120
+ return None, f"エラーが発生しました: {str(e)}"
121
+
122
+ # MCPサーバーの設定
123
+ class ImageGenMCPServer:
124
+ def __init__(self):
125
+ self.server = Server("image-gen-mcp")
126
+ self.setup_handlers()
127
+
128
+ def setup_handlers(self):
129
+ @self.server.list_tools()
130
+ async def handle_list_tools() -> list[types_mcp.Tool]:
131
+ return [
132
+ types_mcp.Tool(
133
+ name="generate_image",
134
+ description="Gemini 2.0 Flashを使用して画像を生成します",
135
+ inputSchema={
136
+ "type": "object",
137
+ "properties": {
138
+ "prompt": {
139
+ "type": "string",
140
+ "description": "生成したい画像の説明"
141
+ }
142
+ },
143
+ "required": ["prompt"]
144
+ }
145
+ )
146
+ ]
147
+
148
+ @self.server.call_tool()
149
+ async def handle_call_tool(
150
+ name: str,
151
+ arguments: Optional[Dict[str, Any]] = None
152
+ ) -> List[types_mcp.TextContent]:
153
+ if name == "generate_image":
154
+ prompt = arguments.get("prompt", "")
155
+
156
+ logger.info(f"MCPツール呼び出し: generate_image, プロンプト='{prompt[:50]}...'")
157
+
158
+ # 同期関数を非同期で実行
159
+ loop = asyncio.get_event_loop()
160
+ image, message = await loop.run_in_executor(
161
+ None, generate_image, prompt, None
162
+ )
163
+
164
+ if image:
165
+ # 画像をbase64エンコード
166
+ buffered = io.BytesIO()
167
+ image.save(buffered, format="PNG")
168
+ img_str = base64.b64encode(buffered.getvalue()).decode()
169
+ logger.info("MCPレスポンス: 成功")
170
+
171
+ return [
172
+ types_mcp.TextContent(
173
+ type="text",
174
+ text=json.dumps({
175
+ "success": True,
176
+ "message": message,
177
+ "image_base64": img_str
178
+ })
179
+ )
180
+ ]
181
+ else:
182
+ logger.warning(f"MCPレスポンス: 失敗 - {message}")
183
+ return [
184
+ types_mcp.TextContent(
185
+ type="text",
186
+ text=json.dumps({
187
+ "success": False,
188
+ "message": message
189
+ })
190
+ )
191
+ ]
192
+
193
+ raise ValueError(f"Unknown tool: {name}")
194
+
195
+ async def run(self):
196
+ logger.info("MCPサーバー起動中...")
197
+ async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
198
+ await self.server.run(
199
+ read_stream,
200
+ write_stream,
201
+ InitializationOptions(
202
+ server_name="image-gen-mcp",
203
+ server_version="0.1.0"
204
+ )
205
+ )
206
+
207
+ # Gradioインターフェースの作成
208
+ def create_gradio_interface():
209
+ with gr.Blocks(title="画像生成MCP - Gemini 2.0 Flash") as demo:
210
+ gr.Markdown("""
211
+ # 画像生成MCPサーバー
212
+
213
+ Gemini 2.0 Flashを使用した画像生成アプリケーションです。
214
+ ClaudeCodeから画像生成MCPツールとして利用できます。
215
+ """)
216
+
217
+ with gr.Row():
218
+ with gr.Column():
219
+ prompt_input = gr.Textbox(
220
+ label="プロンプト",
221
+ placeholder="生成したい画像の説明を入力してください...",
222
+ lines=3
223
+ )
224
+
225
+ reference_image = gr.Image(
226
+ label="参考画像(オプション)",
227
+ type="pil"
228
+ )
229
+
230
+ generate_btn = gr.Button("画像を生成", variant="primary")
231
+
232
+ with gr.Column():
233
+ output_image = gr.Image(
234
+ label="生成された画像",
235
+ type="pil"
236
+ )
237
+
238
+ status_output = gr.Textbox(
239
+ label="ステータス",
240
+ interactive=False
241
+ )
242
+
243
+ # MCPサーバー情報の表示
244
+ gr.Markdown("""
245
+ ### MCPサーバー情報
246
+
247
+ このアプリケーションはMCPサーバーとして動作します。
248
+ ClaudeCodeで使用する場合は、以下の設定を使用してください:
249
+
250
+ ```json
251
+ {
252
+ "mcpServers": {
253
+ "image-gen": {
254
+ "command": "python",
255
+ "args": ["app.py", "--mcp"],
256
+ "env": {
257
+ "GEMINI_API_KEY": "your-api-key-here"
258
+ }
259
+ }
260
+ }
261
+ }
262
+ ```
263
+ """)
264
+
265
+ # イベントハンドラ
266
+ generate_btn.click(
267
+ fn=generate_image,
268
+ inputs=[prompt_input, reference_image],
269
+ outputs=[output_image, status_output]
270
+ )
271
+
272
+ return demo
273
+
274
+ # メイン実行部分
275
+ if __name__ == "__main__":
276
+ import sys
277
+
278
+ if "--mcp" in sys.argv:
279
+ # MCPサーバーモードで実行
280
+ logger.info("MCP���ーバーモードで起動")
281
+ mcp_server = ImageGenMCPServer()
282
+ asyncio.run(mcp_server.run())
283
+ else:
284
+ # Gradio UIモードで実行
285
+ logger.info("Gradio UIモードで起動")
286
+ demo = create_gradio_interface()
287
+ demo.launch()
app.yaml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ title: ImageGenMCP
2
+ emoji: 🎨
3
+ colorFrom: blue
4
+ colorTo: purple
5
+ sdk: gradio
6
+ sdk_version: 5.31.0
7
+ app_file: app.py
8
+ pinned: false
9
+ license: mit
gemini_sample_code.txt ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # To run this code you need to install the following dependencies:
2
+ # pip install google-genai
3
+
4
+ import base64
5
+ import mimetypes
6
+ import os
7
+ from google import genai
8
+ from google.genai import types
9
+
10
+
11
+ def save_binary_file(file_name, data):
12
+ f = open(file_name, "wb")
13
+ f.write(data)
14
+ f.close()
15
+ print(f"File saved to to: {file_name}")
16
+
17
+
18
+ def generate():
19
+ client = genai.Client(
20
+ api_key=os.environ.get("GEMINI_API_KEY"),
21
+ )
22
+
23
+ model = "gemini-2.0-flash-preview-image-generation"
24
+ contents = [
25
+ types.Content(
26
+ role="user",
27
+ parts=[
28
+ types.Part.from_text(text="""筋トレ中の女性
29
+ """),
30
+ ],
31
+ ),
32
+ types.Content(
33
+ role="model",
34
+ parts=[
35
+ types.Part.from_bytes(
36
+ mime_type="image/png",
37
+ data=base64.b64decode(
38
+ """ <画像データが入ります> """
39
+ ),
40
+ ),
41
+ ],
42
+ ),
43
+ types.Content(
44
+ role="user",
45
+ parts=[
46
+ types.Part.from_text(text="""INSERT_INPUT_HERE"""),
47
+ ],
48
+ ),
49
+ ]
50
+ generate_content_config = types.GenerateContentConfig(
51
+ response_modalities=[
52
+ "IMAGE",
53
+ "TEXT",
54
+ ],
55
+ response_mime_type="text/plain",
56
+ )
57
+
58
+ file_index = 0
59
+ for chunk in client.models.generate_content_stream(
60
+ model=model,
61
+ contents=contents,
62
+ config=generate_content_config,
63
+ ):
64
+ if (
65
+ chunk.candidates is None
66
+ or chunk.candidates[0].content is None
67
+ or chunk.candidates[0].content.parts is None
68
+ ):
69
+ continue
70
+ if chunk.candidates[0].content.parts[0].inline_data and chunk.candidates[0].content.parts[0].inline_data.data:
71
+ file_name = f"ENTER_FILE_NAME_{file_index}"
72
+ file_index += 1
73
+ inline_data = chunk.candidates[0].content.parts[0].inline_data
74
+ data_buffer = inline_data.data
75
+ file_extension = mimetypes.guess_extension(inline_data.mime_type)
76
+ save_binary_file(f"{file_name}{file_extension}", data_buffer)
77
+ else:
78
+ print(chunk.text)
79
+
80
+ if __name__ == "__main__":
81
+ generate()
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ gradio==5.31.0
2
+ google-genai
3
+ mcp
4
+ uvicorn
5
+ fastapi
6
+ python-multipart
7
+ pillow