clean up unused tools

This commit is contained in:
2025-08-26 02:10:26 +09:00
parent fdfb72c9db
commit dc1d54a0fc
4 changed files with 264 additions and 558 deletions

213
TOOL_USAGE_GUIDE.md Normal file
View File

@@ -0,0 +1,213 @@
# 📋 GPT-Edit 도구 사용 가이드
gpt-edit 프로젝트는 OpenAI의 GPT-Image-1 모델을 사용한 AI 이미지 편집 도구 **총 5개**를 제공합니다.
## 🎯 도구 우선순위 및 사용 권장사항
### ⭐⭐⭐ **최우선 도구** (일반 사용자 추천)
#### 1. `gpt_edit_image`
**가장 간단하고 직관적인 이미지 편집**
- **사용법**: 이미지 파일을 `input_images/` 폴더에 업로드 → 파일명과 편집 지시사항 입력
- **특징**: 자동 최적화, 크기 관리, 완전 자동화
- **권장 대상**: 모든 사용자 (초보자부터 전문가까지)
```json
{
"input_image_name": "my_photo.jpg",
"prompt": "make the sky more dramatic with sunset colors",
"background": "transparent"
}
```
#### 2. `gpt_edit_image_with_mask`
**정밀한 선택적 영역 편집**
- **사용법**: 이미지 + 마스크 파일을 `input_images/`에 업로드 → 편집 지시
- **특징**: 마스크의 흰색 영역만 편집, 정교한 제어 가능
- **권장 대상**: 정밀한 편집이 필요한 사용자
```json
{
"input_image_name": "portrait.jpg",
"mask_image_name": "background_mask.png",
"prompt": "change background to mountain landscape"
}
```
### ⭐⭐ **중요 유틸리티 도구**
#### 3. `validate_image`
**이미지 파일 검증 및 정보 확인**
- **용도**: 편집 전 이미지 상태 확인, 최적 크기 제안
- **권장 시점**: 편집 전 사전 검사
#### 4. `create_mask_from_alpha`
**PNG 투명도로 마스크 생성**
- **용도**: PNG 이미지의 알파 채널을 마스크로 변환
- **권장 시점**: 투명 배경 이미지를 마스크로 활용할 때
### ⭐ **고급/자동화 도구** (특수 목적)
#### 5. `gpt_batch_edit`
**대량 이미지 일괄 편집 (Claude Code 전용 권장)**
## 🚀 `gpt_batch_edit` 상세 사용 가이드
### ✅ **이런 경우에 사용하세요:**
#### 📸 **전자상거래 대량 처리**
```python
# Claude Code에서 사용 예시
batch_config = {
"edits": [
{
"input_image_name": "product_001.jpg",
"prompt": "remove background, make it pure white",
"background": "opaque"
},
{
"input_image_name": "product_002.jpg",
"prompt": "remove background, make it pure white",
"background": "opaque"
}
# ... 최대 16개까지
]
}
```
#### 🎨 **소셜미디어 템플릿 자동 생성**
```python
# 같은 편집을 여러 이미지에 적용
social_templates = [
{"file": "template_mon.jpg", "text": "Monday Motivation"},
{"file": "template_tue.jpg", "text": "Tuesday Tips"},
{"file": "template_wed.jpg", "text": "Wednesday Wisdom"}
]
batch_edits = [
{
"input_image_name": template["file"],
"prompt": f"add bold text '{template['text']}' at center top"
}
for template in social_templates
]
```
#### 🏭 **마케팅 자료 대량 생성**
- 브랜드 로고 일괄 추가
- 워터마크 적용
- 크기/비율 표준화
### ❌ **이런 경우에는 개별 도구를 사용하세요:**
#### 🎭 **Claude Desktop 대화형 편집**
- 각 이미지마다 다른 편집이 필요한 경우
- 실험적이거나 창작적인 편집
- 단일 이미지 정밀 작업
#### 🔬 **정밀하거나 실험적 편집**
- 여러 번 시행착오가 필요한 작업
- 결과를 보고 다음 편집 방향을 정해야 하는 경우
### 🚧 **gpt_batch_edit 제약사항**
1. **최대 16개 제한**: 한 번에 최대 16개 이미지만 처리 가능
2. **순차 처리**: 병렬 처리 없음, 총 시간 = 개별 시간 × 이미지 수
3. **파일 사전 준비**: 모든 이미지를 `input_images/` 폴더에 미리 업로드 필요
4. **실패 시 영향**: 중간에 실패하면 전체 배치에 영향
### 💡 **Claude Code 활용 패턴**
#### 파일 준비 자동화
```python
import os
import shutil
# 원본 폴더에서 input_images로 자동 복사
def prepare_batch_files(source_dir, target_dir="input_images"):
os.makedirs(target_dir, exist_ok=True)
image_files = []
for filename in os.listdir(source_dir):
if filename.lower().endswith(('.jpg', '.jpeg', '.png', '.webp')):
shutil.copy2(
os.path.join(source_dir, filename),
os.path.join(target_dir, filename)
)
image_files.append(filename)
return image_files
```
#### 대량 처리 분할
```python
def create_batch_chunks(file_list, prompt, chunk_size=16):
\"\"\"파일 리스트를 16개씩 나누어 배치 생성\"\"\"
batches = []
for i in range(0, len(file_list), chunk_size):
chunk = file_list[i:i+chunk_size]
batch = {
"edits": [
{
"input_image_name": filename,
"prompt": prompt,
"background": "transparent"
}
for filename in chunk
]
}
batches.append(batch)
return batches
```
## 📊 **도구 선택 플로우차트**
```
이미지 편집이 필요한가요?
단일 이미지인가요? → YES → 마스크가 필요한가요?
↓ ↓ YES → gpt_edit_image_with_mask
NO ↓ NO → gpt_edit_image
여러 이미지 (2개 이상)인가요?
↓ YES
Claude Code를 사용하나요? & 같은 편집인가요?
↓ YES → gpt_batch_edit
↓ NO → gpt_edit_image (개별적으로 여러 번 사용)
```
## 🎯 **최적 사용 전략**
### 🥇 **초보자 권장**
1. `gpt_edit_image` 위주 사용
2. 필요시 `validate_image`로 사전 검사
### 🥈 **중급자 권장**
1. `gpt_edit_image` + `gpt_edit_image_with_mask` 조합
2. `create_mask_from_alpha`로 마스크 생성 활용
### 🥉 **고급 사용자 (Claude Code)**
1. 모든 도구 활용
2. `gpt_batch_edit`로 자동화 워크플로우 구성
3. 스크립트 기반 대량 처리
## 💾 **파일 저장 규칙**
모든 편집 결과는 다음 형식으로 저장됩니다:
```
generated_images/
├── gptimage1_123456_20250826_143022_000.png # 입력 파일 (추적용)
├── gptimage1_123456_20250826_143022_001.png # 편집 결과
└── gptimage1_123456_20250826_143022_001.json # 편집 파라미터
```
**Base Name 형식**: `gptimage1_{6자리시드}_{날짜}_{시간}`
- 시드는 세션별로 동일하게 유지
- 모든 관련 파일이 같은 base name 사용
- JSON 파일에 편집 과정 전체가 기록됨
---
이 가이드를 통해 상황에 맞는 최적의 도구를 선택하여 효율적인 AI 이미지 편집을 경험해보세요! 🎨✨

View File

@@ -169,300 +169,13 @@ class ToolHandlers:
return self._move_temp_to_generated(temp_file_path, base_name, index, extension)
async def handle_edit_image(self, arguments: Dict[str, Any]) -> List[TextContent | ImageContent]:
"""
Handle edit_image tool call
Args:
arguments: Tool arguments
Returns:
List of content items
"""
try:
# Check for input_image_b64 parameter
if 'input_image_b64' not in arguments:
return [TextContent(
type="text",
text="❌ input_image_b64 is required"
)]
# Get or generate image name
image_name = arguments.get('input_image_name', f'temp_image_{random.randint(1000, 9999)}.png')
# Save base64 image to temp directory with specified name
image_path = self._save_b64_to_temp_file(
arguments['input_image_b64'],
image_name
)
logger.info(f"Input image saved to temp: {image_name}")
# Validate the saved image
is_valid, size_mb, error_msg = validate_image_file(image_path, self.config.max_image_size_mb)
if not is_valid:
return [TextContent(
type="text",
text=f"❌ Image validation failed: {error_msg}"
)]
# Generate base name and copy temp file to generated_images
base_name = self.config.generate_base_name_simple()
# Copy temp file to generated_images as {base_name}_000.ext
input_generated_path = self._copy_temp_to_generated(image_path, base_name, 0)
logger.info(f"Input file copied to generated_images: {Path(input_generated_path).name}")
# Sanitize prompt
prompt = sanitize_prompt(arguments['prompt'])
# Create edit request (use generated path for processing)
request = ImageEditRequest(
image_path=input_generated_path, # Use generated path instead of temp
prompt=prompt,
background=arguments.get('background', 'transparent')
)
# Process edit
response = await self.client.edit_image(request)
if not response.success:
return [TextContent(
type="text",
text=f"❌ Edit failed: {response.error_message}"
)]
# Save output image as {base_name}_001.png
saved_path = None
json_path = None
if arguments.get('save_to_file', True):
output_path = self.config.get_output_path(base_name, 1, 'png')
if save_image(response.edited_image_data, str(output_path)):
saved_path = str(output_path)
# Save parameters as {base_name}_001.json
if self.config.save_parameters:
params_dict = {
"base_name": base_name,
"timestamp": datetime.now().isoformat(),
"prompt": request.prompt,
"background": request.background,
"input_image_name": image_name,
"input_temp_path": image_path,
"input_generated_path": input_generated_path,
"input_size": get_image_dimensions(input_generated_path),
"output_size": response.image_size,
"execution_time": response.execution_time,
"optimization": response.optimization_info if response.optimization_info else None,
"token_stats": response.token_stats if response.token_stats else None,
"config": {
"model": Config.MODEL,
"quality": Config.QUALITY,
"api_version": "gpt-image-1"
}
}
json_path = self.config.get_output_path(base_name, 1, 'json')
with open(json_path, 'w', encoding='utf-8') as f:
json.dump(params_dict, f, indent=2, ensure_ascii=False)
logger.info(f"Parameters saved to: {json_path}")
# Prepare response
contents = []
# Add text description
text = f"✅ Image edited successfully!\n"
text += f"📝 Input: {image_name}\n"
text += f"📁 Base name: {base_name}\n"
text += f"📐 Size: {response.image_size[0]}x{response.image_size[1]}\n"
text += f"⏱️ Processing time: {response.execution_time:.1f}s\n"
# Add optimization info if image was optimized
if response.optimization_info and response.optimization_info["optimized"]:
opt_info = response.optimization_info
text += f"\n🔄 Image was auto-optimized:\n"
text += f" Original: {opt_info['original_size_mb']:.2f}MB\n"
text += f" Optimized: {opt_info['final_size_mb']:.2f}MB\n"
text += f" Format: {opt_info['format_used']}\n"
if saved_path:
text += f"\n💾 Output: {Path(saved_path).name}"
text += f"\n📝 Input: {Path(input_generated_path).name}" # Show generated file
if json_path:
text += f"\n📋 Parameters: {Path(json_path).name}"
contents.append(TextContent(type="text", text=text))
# Add image preview
image_b64 = encode_image_base64(response.edited_image_data)
contents.append(ImageContent(
type="image",
data=image_b64,
mimeType="image/png"
))
# Reset seed for next session
self._reset_seed()
return contents
except Exception as e:
logger.error(f"Error in handle_edit_image: {e}", exc_info=True)
return [TextContent(
type="text",
text=f"❌ Unexpected error: {str(e)}"
)]
async def handle_edit_with_mask(self, arguments: Dict[str, Any]) -> List[TextContent | ImageContent]:
"""
Handle edit_with_mask tool call
Args:
arguments: Tool arguments
Returns:
List of content items
"""
try:
# Validate required parameters
if 'input_image_b64' not in arguments:
return [TextContent(
type="text",
text="❌ input_image_b64 is required"
)]
if 'input_mask_b64' not in arguments:
return [TextContent(
type="text",
text="❌ input_mask_b64 is required for masked editing"
)]
# Get or generate image names
image_name = arguments.get('input_image_name', f'temp_image_{random.randint(1000, 9999)}.png')
mask_name = arguments.get('mask_image_name', f'temp_mask_{random.randint(1000, 9999)}.png')
# Save base64 images to temp directory with specified names
image_path = self._save_b64_to_temp_file(
arguments['input_image_b64'],
image_name
)
logger.info(f"Input image saved to temp: {image_name}")
# Save base64 mask to temp directory
mask_path = self._save_b64_to_temp_file(
arguments['input_mask_b64'],
mask_name
)
logger.info(f"Mask image saved to temp: {mask_name}")
# Validate the saved images
is_valid, size_mb, error_msg = validate_image_file(image_path, self.config.max_image_size_mb)
if not is_valid:
return [TextContent(
type="text",
text=f"❌ Image validation failed: {error_msg}"
)]
# Generate base name and move temp files to generated_images
base_name = self.config.generate_base_name_simple()
# Copy temp files to generated_images
input_generated_path = self._copy_temp_to_generated(image_path, base_name, 0)
mask_generated_path = self._copy_temp_to_generated(mask_path, f"{base_name}_mask", 0, 'png') # Force PNG for mask
logger.info(f"Input file copied to generated_images: {Path(input_generated_path).name}")
logger.info(f"Mask file copied to generated_images: {Path(mask_generated_path).name}")
# Sanitize prompt
prompt = sanitize_prompt(arguments['prompt'])
# Create edit request with mask (use generated paths)
request = ImageEditRequest(
image_path=input_generated_path,
prompt=prompt,
mask_path=mask_generated_path,
background=arguments.get('background', 'transparent')
)
# Process edit
response = await self.client.edit_image(request)
if not response.success:
return [TextContent(
type="text",
text=f"❌ Masked edit failed: {response.error_message}"
)]
# Save output image
saved_path = None
json_path = None
if arguments.get('save_to_file', True):
output_path = self.config.get_output_path(base_name, 1, 'png')
if save_image(response.edited_image_data, str(output_path)):
saved_path = str(output_path)
# Save parameters
if self.config.save_parameters:
params_dict = {
"base_name": base_name,
"timestamp": datetime.now().isoformat(),
"prompt": request.prompt,
"background": request.background,
"input_image_name": image_name,
"mask_image_name": mask_name,
"input_temp_path": image_path,
"mask_temp_path": mask_path,
"input_generated_path": input_generated_path,
"mask_generated_path": mask_generated_path,
"input_size": get_image_dimensions(input_generated_path),
"output_size": response.image_size,
"execution_time": response.execution_time
}
json_path = self.config.get_output_path(base_name, 1, 'json')
with open(json_path, 'w', encoding='utf-8') as f:
json.dump(params_dict, f, indent=2, ensure_ascii=False)
# Prepare response
contents = []
text = f"✅ Image edited with mask successfully!\n"
text += f"📝 Input: {image_name}\n"
text += f"🎭 Mask: {mask_name}\n"
text += f"📁 Base name: {base_name}\n"
text += f"📐 Size: {response.image_size[0]}x{response.image_size[1]}\n"
text += f"⏱️ Processing time: {response.execution_time:.1f}s\n"
if saved_path:
text += f"\n💾 Output: {Path(saved_path).name}"
if json_path:
text += f"\n📋 Parameters: {Path(json_path).name}"
contents.append(TextContent(type="text", text=text))
# Add image preview
image_b64 = encode_image_base64(response.edited_image_data)
contents.append(ImageContent(
type="image",
data=image_b64,
mimeType="image/png"
))
# Reset seed for next session
self._reset_seed()
return contents
except Exception as e:
logger.error(f"Error in handle_edit_with_mask: {e}", exc_info=True)
return [TextContent(
type="text",
text=f"❌ Unexpected error: {str(e)}"
)]
async def handle_batch_edit(self, arguments: Dict[str, Any]) -> List[TextContent]:
async def handle_gpt_batch_edit(self, arguments: Dict[str, Any]) -> List[TextContent]:
"""
Handle batch_edit tool call
Handle gpt_batch_edit tool call
Args:
arguments: Tool arguments
@@ -610,7 +323,7 @@ class ToolHandlers:
successful = sum(1 for r in results if r.get("success"))
failed = len(results) - successful
text = f"📦 Batch Edit Complete\n"
text = f"📦 GPT Batch Edit Complete\n"
text += f"🎲 Seed: {seed}\n"
text += f"📁 Base name: {base_name}\n"
text += f"✅ Successful: {successful}/{len(results)}\n"
@@ -627,7 +340,7 @@ class ToolHandlers:
return [TextContent(type="text", text=text)]
except Exception as e:
logger.error(f"Error in handle_batch_edit: {e}", exc_info=True)
logger.error(f"Error in handle_gpt_batch_edit: {e}", exc_info=True)
self._reset_seed()
return [TextContent(
type="text",
@@ -734,9 +447,9 @@ class ToolHandlers:
text=f"❌ Mask creation error: {str(e)}"
)]
async def handle_edit_image_from_file(self, arguments: Dict[str, Any]) -> List[TextContent | ImageContent]:
async def handle_gpt_edit_image(self, arguments: Dict[str, Any]) -> List[TextContent | ImageContent]:
"""
Handle edit_image_from_file tool call
Handle gpt_edit_image tool call
- Read file from INPUT_PATH directory specified by input_image_name
- If file size > MAX_IMAGE_SIZE_MB, convert to WebP lossy format
- Save converted file to generated_images as {base_name}_000.png
@@ -972,15 +685,15 @@ class ToolHandlers:
return contents
except Exception as e:
logger.error(f"Error in handle_edit_image_from_file: {e}", exc_info=True)
logger.error(f"Error in handle_gpt_edit_image: {e}", exc_info=True)
return [TextContent(
type="text",
text=f"❌ File-based image edit error: {str(e)}"
)]
async def handle_edit_with_mask_from_file(self, arguments: Dict[str, Any]) -> List[TextContent | ImageContent]:
async def handle_gpt_edit_image_with_mask(self, arguments: Dict[str, Any]) -> List[TextContent | ImageContent]:
"""
Handle edit_with_mask_from_file tool call
Handle gpt_edit_image_with_mask tool call
- Read files from INPUT_PATH directory specified by input_image_name and mask_image_name
- If file size > MAX_IMAGE_SIZE_MB, convert to WebP lossy format
- Save converted files to generated_images as {base_name}_000.png and {base_name}_mask_000.png
@@ -1280,96 +993,10 @@ class ToolHandlers:
return contents
except Exception as e:
logger.error(f"Error in handle_edit_with_mask_from_file: {e}", exc_info=True)
logger.error(f"Error in handle_gpt_edit_image_with_mask: {e}", exc_info=True)
return [TextContent(
type="text",
text=f"❌ File-based masked edit error: {str(e)}"
)]
async def handle_move_temp_to_output(self, arguments: Dict[str, Any]) -> List[TextContent]:
"""Handle move_temp_to_output tool call
Args:
arguments: Tool arguments containing temp_file_name
Returns:
List of content items
"""
try:
temp_file_name = arguments.get('temp_file_name')
output_file_name = arguments.get('output_file_name')
copy_only = arguments.get('copy_only', False)
if not temp_file_name:
return [TextContent(
type="text",
text="❌ temp_file_name is required"
)]
# Get temp file path
temp_file_path = self.config.base_path / 'temp' / temp_file_name
# Check if temp file exists
if not Path(temp_file_path).exists():
return [TextContent(
type="text",
text=f"❌ Temp file not found: {temp_file_name}"
)]
# Generate output file name if not provided
if not output_file_name:
base_name = self.config.generate_base_name_simple()
file_ext = Path(temp_file_name).suffix[1:] or 'png'
output_file_name = f"{base_name}_001.{file_ext}"
# Ensure output directory exists
self.config.ensure_output_directory()
# Get output path
output_path = self.config.generated_images_path / output_file_name
# Move or copy file
try:
if copy_only:
import shutil
shutil.copy2(temp_file_path, output_path)
operation = "copied"
else:
# Move file
import shutil
shutil.move(temp_file_path, output_path)
operation = "moved"
# Verify operation was successful
if not output_path.exists():
raise RuntimeError(f"File {operation} verification failed")
logger.info(f"📁 File {operation}: {temp_file_name} -> {output_file_name}")
# Get file size for reporting
file_size_mb = output_path.stat().st_size / (1024 * 1024)
text = f"✅ File {operation} successfully!\n"
text += f"📁 From temp: {temp_file_name}\n"
text += f"📁 To output: {output_file_name}\n"
text += f"💾 Size: {file_size_mb:.2f}MB"
return [TextContent(type="text", text=text)]
except PermissionError as e:
return [TextContent(
type="text",
text=f"❌ Permission denied: {str(e)}"
)]
except shutil.Error as e:
return [TextContent(
type="text",
text=f"❌ File operation failed: {str(e)}"
)]
except Exception as e:
logger.error(f"Error in handle_move_temp_to_output: {e}", exc_info=True)
return [TextContent(
type="text",
text=f"❌ File move error: {str(e)}"
)]

View File

@@ -58,22 +58,16 @@ class GPTEditMCPServer:
logger.info(f"Tool called: {name} with arguments: {safe_args}")
try:
if name == "edit_image":
return await self.handlers.handle_edit_image(arguments)
elif name == "edit_with_mask":
return await self.handlers.handle_edit_with_mask(arguments)
elif name == "batch_edit":
return await self.handlers.handle_batch_edit(arguments)
if name == "gpt_batch_edit":
return await self.handlers.handle_gpt_batch_edit(arguments)
elif name == "validate_image":
return await self.handlers.handle_validate_image(arguments)
elif name == "create_mask_from_alpha":
return await self.handlers.handle_create_mask_from_alpha(arguments)
elif name == "edit_image_from_file":
return await self.handlers.handle_edit_image_from_file(arguments)
elif name == "edit_with_mask_from_file":
return await self.handlers.handle_edit_with_mask_from_file(arguments)
elif name == "move_temp_to_output":
return await self.handlers.handle_move_temp_to_output(arguments)
elif name == "gpt_edit_image":
return await self.handlers.handle_gpt_edit_image(arguments)
elif name == "gpt_edit_image_with_mask":
return await self.handlers.handle_gpt_edit_image_with_mask(arguments)
else:
error_msg = f"Unknown tool: {name}"
logger.error(error_msg)
@@ -92,23 +86,7 @@ class GPTEditMCPServer:
logger.debug("list_prompts called")
prompts = [
Prompt(
name="edit_image",
description="Edit an image with AI-powered instructions",
arguments=[
{
"name": "image_path",
"description": "Path to the image to edit",
"required": True
},
{
"name": "edit_description",
"description": "Description of how to edit the image",
"required": True
}
]
),
Prompt(
name="edit_with_mask_prompt",
name="gpt_edit_image_with_mask",
description="Edit an image with a mask",
arguments=[
{
@@ -129,17 +107,17 @@ class GPTEditMCPServer:
]
),
Prompt(
name="optimize_and_edit",
description="Automatically optimize and edit an image",
name="gpt_batch_edit",
description="Edit multiple images with batch processing (for automation/Claude Code)",
arguments=[
{
"name": "image_path",
"description": "Path to the image to edit",
"name": "image_paths",
"description": "Comma-separated list of image paths to edit",
"required": True
},
{
"name": "edit_description",
"description": "Description of how to edit the image",
"description": "Description of how to edit the images",
"required": True
}
]
@@ -153,25 +131,7 @@ class GPTEditMCPServer:
"""Get a specific prompt"""
logger.debug(f"get_prompt called for: {name}")
if name == "edit_image":
if not arguments or "image_path" not in arguments or "edit_description" not in arguments:
return [PromptMessage(
role="user",
content=TextContent(
type="text",
text="Please provide an image_path and edit_description"
)
)]
return [PromptMessage(
role="user",
content=TextContent(
type="text",
text=f"Edit the image at '{arguments['image_path']}' with the following instructions: {arguments['edit_description']}"
)
)]
elif name == "edit_with_mask_prompt":
if name == "gpt_edit_image_with_mask":
if not arguments or "image_path" not in arguments or "mask_path" not in arguments or "edit_description" not in arguments:
return [PromptMessage(
role="user",
@@ -189,13 +149,13 @@ class GPTEditMCPServer:
)
)]
elif name == "optimize_and_edit":
if not arguments or "image_path" not in arguments or "edit_description" not in arguments:
elif name == "gpt_batch_edit":
if not arguments or "image_paths" not in arguments or "edit_description" not in arguments:
return [PromptMessage(
role="user",
content=TextContent(
type="text",
text="Please provide an image_path and edit_description"
text="Please provide image_paths and edit_description"
)
)]
@@ -203,7 +163,7 @@ class GPTEditMCPServer:
role="user",
content=TextContent(
type="text",
text=f"Optimize and edit the image at '{arguments['image_path']}' with: {arguments['edit_description']}"
text=f"Batch edit the images at '{arguments['image_paths']}' with: {arguments['edit_description']}"
)
)]

View File

@@ -7,81 +7,15 @@ from mcp.types import Tool
class MCPToolDefinitions:
"""MCP tool definitions for GPTEdit"""
@staticmethod
def get_edit_image_tool() -> Tool:
"""Get edit_image tool definition"""
return Tool(
name="edit_image",
description="Edit an existing image using AI-powered editing with OpenAI GPT-Image-1",
inputSchema={
"type": "object",
"properties": {
"input_image_b64": {
"type": "string",
"description": "Base64 encoded input image data to edit (supports PNG, JPEG, WebP, etc.)"
},
"prompt": {
"type": "string",
"description": "Description of how to edit the image"
},
"background": {
"type": "string",
"enum": ["transparent", "opaque"],
"default": "transparent",
"description": "Background type for the edited image"
},
"save_to_file": {
"type": "boolean",
"default": True,
"description": "Whether to save the edited image to a file"
}
},
"required": ["input_image_b64", "prompt"]
}
)
@staticmethod
def get_edit_with_mask_tool() -> Tool:
"""Get edit_with_mask tool definition"""
def get_gpt_batch_edit_tool() -> Tool:
"""Get gpt_batch_edit tool definition"""
return Tool(
name="edit_with_mask",
description="Edit an image with a mask to specify which areas to modify",
inputSchema={
"type": "object",
"properties": {
"input_image_b64": {
"type": "string",
"description": "Base64 encoded input image data to edit (supports PNG, JPEG, WebP, etc.)"
},
"input_mask_b64": {
"type": "string",
"description": "Base64 encoded mask image (white areas will be edited)"
},
"prompt": {
"type": "string",
"description": "Description of how to edit the masked areas"
},
"background": {
"type": "string",
"enum": ["transparent", "opaque"],
"default": "transparent",
"description": "Background type for the edited image"
},
"save_to_file": {
"type": "boolean",
"default": True,
"description": "Whether to save the edited image to a file"
}
},
"required": ["input_image_b64", "input_mask_b64", "prompt"]
}
)
@staticmethod
def get_batch_edit_tool() -> Tool:
"""Get batch_edit tool definition"""
return Tool(
name="batch_edit",
name="gpt_batch_edit",
description="Edit multiple images with the same or different prompts (max 16 images)",
inputSchema={
"type": "object",
@@ -165,10 +99,10 @@ class MCPToolDefinitions:
)
@staticmethod
def get_edit_image_from_file_tool() -> Tool:
"""Get edit_image_from_file tool definition (for file paths only)"""
def get_gpt_edit_image_tool() -> Tool:
"""Get gpt_edit_image tool definition (for file paths only)"""
return Tool(
name="edit_image_from_file",
name="gpt_edit_image",
description="Edit an image file by providing the file path (alternative to base64 input)",
inputSchema={
"type": "object",
@@ -203,10 +137,10 @@ class MCPToolDefinitions:
)
@staticmethod
def get_edit_with_mask_from_file_tool() -> Tool:
"""Get edit_with_mask_from_file tool definition (for file paths only)"""
def get_gpt_edit_image_with_mask_tool() -> Tool:
"""Get gpt_edit_image_with_mask tool definition (for file paths only)"""
return Tool(
name="edit_with_mask_from_file",
name="gpt_edit_image_with_mask",
description="Edit an image with mask by providing file paths (alternative to base64 input)",
inputSchema={
"type": "object",
@@ -245,46 +179,18 @@ class MCPToolDefinitions:
)
@staticmethod
def get_move_temp_to_output_tool() -> Tool:
"""Get move_temp_to_output tool definition"""
return Tool(
name="move_temp_to_output",
description="Move file from temp directory to output (generated_images) directory",
inputSchema={
"type": "object",
"properties": {
"temp_file_name": {
"type": "string",
"description": "Name of the file in temp directory to move"
},
"output_file_name": {
"type": "string",
"description": "Optional: Desired name for the output file (will auto-generate if not provided)"
},
"copy_only": {
"type": "boolean",
"default": False,
"description": "If true, copy file instead of moving (keep original in temp)"
}
},
"required": ["temp_file_name"]
}
)
@staticmethod
def get_all_tools() -> List[Tool]:
"""Get all available tools"""
return [
# Main editing tools
MCPToolDefinitions.get_edit_image_tool(),
MCPToolDefinitions.get_edit_with_mask_tool(),
MCPToolDefinitions.get_batch_edit_tool(),
# File-based tools (recommended)
MCPToolDefinitions.get_edit_image_from_file_tool(),
MCPToolDefinitions.get_edit_with_mask_from_file_tool(),
# Utility tools
MCPToolDefinitions.get_validate_image_tool(),
MCPToolDefinitions.get_create_mask_from_alpha_tool(),
MCPToolDefinitions.get_move_temp_to_output_tool()
# File-based tools (recommended - highest priority)
MCPToolDefinitions.get_gpt_edit_image_tool(), # ⭐⭐⭐ 최우선
MCPToolDefinitions.get_gpt_edit_image_with_mask_tool(), # ⭐⭐⭐ 최우선
# Utility tools (important - medium priority)
MCPToolDefinitions.get_validate_image_tool(), # ⭐⭐ 중요
MCPToolDefinitions.get_create_mask_from_alpha_tool(), # ⭐⭐ 중요
# Advanced/automation tool (special purpose - low priority)
MCPToolDefinitions.get_gpt_batch_edit_tool() # ⭐ 특수 목적용 (Claude Code 등)
]