tomo2chin2 commited on
Commit
e4603a2
·
verified ·
1 Parent(s): 047f773

Upload 8 files

Browse files
Files changed (3) hide show
  1. app.py +152 -140
  2. mcp_client.py +75 -0
  3. requirements.txt +4 -2
app.py CHANGED
@@ -8,12 +8,12 @@ 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(
@@ -22,6 +22,9 @@ logging.basicConfig(
22
  )
23
  logger = logging.getLogger(__name__)
24
 
 
 
 
25
  # Gemini APIクライアントの初期化
26
  def get_gemini_client():
27
  api_key = os.environ.get("GEMINI_API_KEY")
@@ -119,90 +122,67 @@ def generate_image(prompt: str, previous_image: Optional[Image.Image] = None) ->
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():
@@ -210,57 +190,73 @@ def create_gradio_interface():
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(
@@ -271,17 +267,33 @@ def create_gradio_interface():
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(server_name="0.0.0.0", server_port=7860)
 
 
 
 
 
8
  import json
9
  import asyncio
10
  from typing import Dict, Any, List, Optional
 
 
 
 
11
  import logging
12
  import traceback
13
+ from fastapi import FastAPI
14
+ from fastapi.responses import JSONResponse
15
+ import uvicorn
16
+ from threading import Thread
17
 
18
  # ログ設定
19
  logging.basicConfig(
 
22
  )
23
  logger = logging.getLogger(__name__)
24
 
25
+ # FastAPIアプリケーションの初期化
26
+ app = FastAPI()
27
+
28
  # Gemini APIクライアントの初期化
29
  def get_gemini_client():
30
  api_key = os.environ.get("GEMINI_API_KEY")
 
122
  logger.error(traceback.format_exc())
123
  return None, f"エラーが発生しました: {str(e)}"
124
 
125
+ # MCPエンドポイント
126
+ @app.post("/mcp/list_tools")
127
+ async def mcp_list_tools():
128
+ """MCPツールのリストを返す"""
129
+ return {
130
+ "tools": [
131
+ {
132
+ "name": "generate_image",
133
+ "description": "Gemini 2.0 Flash Previewを使用して画像を生成します",
134
+ "inputSchema": {
135
+ "type": "object",
136
+ "properties": {
137
+ "prompt": {
138
+ "type": "string",
139
+ "description": "生成したい画像の説明"
140
+ }
141
+ },
142
+ "required": ["prompt"]
143
+ }
144
+ }
145
+ ]
146
+ }
147
+
148
+ @app.post("/mcp/call_tool")
149
+ async def mcp_call_tool(request: Dict[str, Any]):
150
+ """MCPツールを実行する"""
151
+ tool_name = request.get("name")
152
+ arguments = request.get("arguments", {})
153
 
154
+ if tool_name == "generate_image":
155
+ prompt = arguments.get("prompt", "")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
 
157
+ # 画像生成を実行
158
+ image, message = generate_image(prompt)
159
+
160
+ if image:
161
+ # 画像をbase64エンコード
162
+ buffered = io.BytesIO()
163
+ image.save(buffered, format="PNG")
164
+ img_str = base64.b64encode(buffered.getvalue()).decode()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
 
166
+ return JSONResponse({
167
+ "success": True,
168
+ "message": message,
169
+ "image_base64": img_str
170
+ })
171
+ else:
172
+ return JSONResponse({
173
+ "success": False,
174
+ "message": message
175
+ })
176
 
177
+ return JSONResponse({
178
+ "success": False,
179
+ "message": f"Unknown tool: {tool_name}"
180
+ }, status_code=400)
181
+
182
+ @app.get("/health")
183
+ async def health_check():
184
+ """ヘルスチェックエンドポイント"""
185
+ return {"status": "healthy"}
 
 
186
 
187
  # Gradioインターフェースの作成
188
  def create_gradio_interface():
 
190
  gr.Markdown("""
191
  # 画像生成MCPサーバー
192
 
193
+ このアプリケーションは主にClaudeCodeから利用するためのMCPサーバーです。
194
+ Gemini 2.0 Flash Previewを使用して画像を生成します。
 
195
 
196
+ ## ClaudeCodeでの使用方法
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
 
198
+ 以下のエンドポイントをMCP設定に追加してください:
199
+ ```
200
+ https://[your-space-name].hf.space/mcp/
201
+ ```
202
+ """)
203
 
204
+ with gr.Tab("画像生成テスト"):
205
+ with gr.Row():
206
+ with gr.Column():
207
+ prompt_input = gr.Textbox(
208
+ label="プロンプト",
209
+ placeholder="生成したい画像の説明を入力してください...",
210
+ lines=3
211
+ )
212
+
213
+ reference_image = gr.Image(
214
+ label="参考画像(オプション)",
215
+ type="pil"
216
+ )
217
+
218
+ generate_btn = gr.Button("画像を生成", variant="primary")
219
+
220
+ with gr.Column():
221
+ output_image = gr.Image(
222
+ label="生成された画像",
223
+ type="pil"
224
+ )
225
+
226
+ status_output = gr.Textbox(
227
+ label="ステータス",
228
+ interactive=False
229
+ )
230
 
231
+ with gr.Tab("MCPエンドポイント情報"):
232
+ gr.Markdown("""
233
+ ### エンドポイント一覧
234
+
235
+ - `POST /mcp/list_tools` - 利用可能なツールのリストを取得
236
+ - `POST /mcp/call_tool` - ツールを実行
237
+ - `GET /health` - ヘルスチェック
238
+
239
+ ### ツール: generate_image
240
+
241
+ **パラメータ:**
242
+ ```json
243
+ {
244
+ "name": "generate_image",
245
+ "arguments": {
246
+ "prompt": "生成したい画像の説明"
247
+ }
248
  }
249
+ ```
250
+
251
+ **レスポンス:**
252
+ ```json
253
+ {
254
+ "success": true,
255
+ "message": "画像生成に成功しました!",
256
+ "image_base64": "base64エンコードされた画像データ"
257
+ }
258
+ ```
259
+ """)
260
 
261
  # イベントハンドラ
262
  generate_btn.click(
 
267
 
268
  return demo
269
 
270
+ # FastAPIをバックグラウンドで起動
271
+ def run_fastapi():
272
+ uvicorn.run(app, host="0.0.0.0", port=7860)
273
+
274
  # メイン実行部分
275
  if __name__ == "__main__":
276
+ # APIキーチェック
277
+ if not os.environ.get("GEMINI_API_KEY"):
278
+ logger.error("GEMINI_API_KEY環境変数が設定されていません")
279
+ # Gradioで警告を表示
280
+ with gr.Blocks(title="エラー - 画像生成MCP") as demo:
281
+ gr.Markdown("""
282
+ # ⚠️ 設定エラー
283
+
284
+ GEMINI_API_KEY環境変数が設定されていません。
285
+
286
+ Hugging Face SpacesのSettings > Repository secretsで以下を設定してください:
287
+ - Name: `GEMINI_API_KEY`
288
+ - Value: あなたのGemini APIキー
289
+ """)
290
+ demo.launch(server_name="0.0.0.0", server_port=7860)
291
  else:
292
+ # GradioとFastAPIを統合
293
+ logger.info("MCPサーバーとGradio UIを起動中...")
294
  demo = create_gradio_interface()
295
+ # GradioにFastAPIアプリケーションをマウント
296
+ gr.mount_gradio_app(app, demo, path="/")
297
+
298
+ # 統合サーバーを起動
299
+ uvicorn.run(app, host="0.0.0.0", port=7860)
mcp_client.py ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ MCPクライアントのサンプルコード
3
+ ClaudeCodeからこのMCPサーバーを利用する際の参考実装
4
+ """
5
+
6
+ import requests
7
+ import base64
8
+ from PIL import Image
9
+ import io
10
+
11
+ class ImageGenMCPClient:
12
+ def __init__(self, base_url: str):
13
+ """
14
+ Args:
15
+ base_url: MCPサーバーのベースURL (例: "https://your-space.hf.space")
16
+ """
17
+ self.base_url = base_url.rstrip('/')
18
+
19
+ def list_tools(self):
20
+ """利用可能なツールのリストを取得"""
21
+ response = requests.post(f"{self.base_url}/mcp/list_tools")
22
+ return response.json()
23
+
24
+ def generate_image(self, prompt: str) -> tuple[Image.Image, str]:
25
+ """
26
+ 画像を生成する
27
+
28
+ Args:
29
+ prompt: 生成したい画像の説明
30
+
31
+ Returns:
32
+ 生成された画像とメッセージ
33
+ """
34
+ payload = {
35
+ "name": "generate_image",
36
+ "arguments": {
37
+ "prompt": prompt
38
+ }
39
+ }
40
+
41
+ response = requests.post(f"{self.base_url}/mcp/call_tool", json=payload)
42
+ result = response.json()
43
+
44
+ if result.get("success"):
45
+ # base64デコードして画像を取得
46
+ img_data = base64.b64decode(result["image_base64"])
47
+ image = Image.open(io.BytesIO(img_data))
48
+ return image, result["message"]
49
+ else:
50
+ return None, result["message"]
51
+
52
+ def health_check(self):
53
+ """ヘルスチェック"""
54
+ response = requests.get(f"{self.base_url}/health")
55
+ return response.json()
56
+
57
+ # 使用例
58
+ if __name__ == "__main__":
59
+ # MCPサーバーのURL
60
+ client = ImageGenMCPClient("https://your-space.hf.space")
61
+
62
+ # ヘルスチェック
63
+ print("Health check:", client.health_check())
64
+
65
+ # ツールリストの取得
66
+ tools = client.list_tools()
67
+ print("Available tools:", tools)
68
+
69
+ # 画像生成
70
+ image, message = client.generate_image("美しい夕日の風景")
71
+ if image:
72
+ print(f"Success: {message}")
73
+ image.save("generated_image.png")
74
+ else:
75
+ print(f"Error: {message}")
requirements.txt CHANGED
@@ -1,4 +1,6 @@
1
  gradio==5.31.0
2
  google-genai
3
- mcp
4
- pillow
 
 
 
1
  gradio==5.31.0
2
  google-genai
3
+ pillow
4
+ fastapi
5
+ uvicorn
6
+ python-multipart