clean up unused tools
This commit is contained in:
213
TOOL_USAGE_GUIDE.md
Normal file
213
TOOL_USAGE_GUIDE.md
Normal 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 이미지 편집을 경험해보세요! 🎨✨
|
||||||
@@ -169,300 +169,13 @@ class ToolHandlers:
|
|||||||
return self._move_temp_to_generated(temp_file_path, base_name, index, extension)
|
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:
|
Args:
|
||||||
arguments: Tool arguments
|
arguments: Tool arguments
|
||||||
@@ -610,7 +323,7 @@ class ToolHandlers:
|
|||||||
successful = sum(1 for r in results if r.get("success"))
|
successful = sum(1 for r in results if r.get("success"))
|
||||||
failed = len(results) - successful
|
failed = len(results) - successful
|
||||||
|
|
||||||
text = f"📦 Batch Edit Complete\n"
|
text = f"📦 GPT Batch Edit Complete\n"
|
||||||
text += f"🎲 Seed: {seed}\n"
|
text += f"🎲 Seed: {seed}\n"
|
||||||
text += f"📁 Base name: {base_name}\n"
|
text += f"📁 Base name: {base_name}\n"
|
||||||
text += f"✅ Successful: {successful}/{len(results)}\n"
|
text += f"✅ Successful: {successful}/{len(results)}\n"
|
||||||
@@ -627,7 +340,7 @@ class ToolHandlers:
|
|||||||
return [TextContent(type="text", text=text)]
|
return [TextContent(type="text", text=text)]
|
||||||
|
|
||||||
except Exception as e:
|
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()
|
self._reset_seed()
|
||||||
return [TextContent(
|
return [TextContent(
|
||||||
type="text",
|
type="text",
|
||||||
@@ -734,9 +447,9 @@ class ToolHandlers:
|
|||||||
text=f"❌ Mask creation error: {str(e)}"
|
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
|
- Read file from INPUT_PATH directory specified by input_image_name
|
||||||
- If file size > MAX_IMAGE_SIZE_MB, convert to WebP lossy format
|
- If file size > MAX_IMAGE_SIZE_MB, convert to WebP lossy format
|
||||||
- Save converted file to generated_images as {base_name}_000.png
|
- Save converted file to generated_images as {base_name}_000.png
|
||||||
@@ -972,15 +685,15 @@ class ToolHandlers:
|
|||||||
return contents
|
return contents
|
||||||
|
|
||||||
except Exception as e:
|
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(
|
return [TextContent(
|
||||||
type="text",
|
type="text",
|
||||||
text=f"❌ File-based image edit error: {str(e)}"
|
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
|
- 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
|
- 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
|
- 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
|
return contents
|
||||||
|
|
||||||
except Exception as e:
|
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(
|
return [TextContent(
|
||||||
type="text",
|
type="text",
|
||||||
text=f"❌ File-based masked edit error: {str(e)}"
|
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)}"
|
|
||||||
)]
|
|
||||||
|
|||||||
@@ -58,22 +58,16 @@ class GPTEditMCPServer:
|
|||||||
logger.info(f"Tool called: {name} with arguments: {safe_args}")
|
logger.info(f"Tool called: {name} with arguments: {safe_args}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if name == "edit_image":
|
if name == "gpt_batch_edit":
|
||||||
return await self.handlers.handle_edit_image(arguments)
|
return await self.handlers.handle_gpt_batch_edit(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)
|
|
||||||
elif name == "validate_image":
|
elif name == "validate_image":
|
||||||
return await self.handlers.handle_validate_image(arguments)
|
return await self.handlers.handle_validate_image(arguments)
|
||||||
elif name == "create_mask_from_alpha":
|
elif name == "create_mask_from_alpha":
|
||||||
return await self.handlers.handle_create_mask_from_alpha(arguments)
|
return await self.handlers.handle_create_mask_from_alpha(arguments)
|
||||||
elif name == "edit_image_from_file":
|
elif name == "gpt_edit_image":
|
||||||
return await self.handlers.handle_edit_image_from_file(arguments)
|
return await self.handlers.handle_gpt_edit_image(arguments)
|
||||||
elif name == "edit_with_mask_from_file":
|
elif name == "gpt_edit_image_with_mask":
|
||||||
return await self.handlers.handle_edit_with_mask_from_file(arguments)
|
return await self.handlers.handle_gpt_edit_image_with_mask(arguments)
|
||||||
elif name == "move_temp_to_output":
|
|
||||||
return await self.handlers.handle_move_temp_to_output(arguments)
|
|
||||||
else:
|
else:
|
||||||
error_msg = f"Unknown tool: {name}"
|
error_msg = f"Unknown tool: {name}"
|
||||||
logger.error(error_msg)
|
logger.error(error_msg)
|
||||||
@@ -92,23 +86,7 @@ class GPTEditMCPServer:
|
|||||||
logger.debug("list_prompts called")
|
logger.debug("list_prompts called")
|
||||||
prompts = [
|
prompts = [
|
||||||
Prompt(
|
Prompt(
|
||||||
name="edit_image",
|
name="gpt_edit_image_with_mask",
|
||||||
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",
|
|
||||||
description="Edit an image with a mask",
|
description="Edit an image with a mask",
|
||||||
arguments=[
|
arguments=[
|
||||||
{
|
{
|
||||||
@@ -129,17 +107,17 @@ class GPTEditMCPServer:
|
|||||||
]
|
]
|
||||||
),
|
),
|
||||||
Prompt(
|
Prompt(
|
||||||
name="optimize_and_edit",
|
name="gpt_batch_edit",
|
||||||
description="Automatically optimize and edit an image",
|
description="Edit multiple images with batch processing (for automation/Claude Code)",
|
||||||
arguments=[
|
arguments=[
|
||||||
{
|
{
|
||||||
"name": "image_path",
|
"name": "image_paths",
|
||||||
"description": "Path to the image to edit",
|
"description": "Comma-separated list of image paths to edit",
|
||||||
"required": True
|
"required": True
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "edit_description",
|
"name": "edit_description",
|
||||||
"description": "Description of how to edit the image",
|
"description": "Description of how to edit the images",
|
||||||
"required": True
|
"required": True
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -153,25 +131,7 @@ class GPTEditMCPServer:
|
|||||||
"""Get a specific prompt"""
|
"""Get a specific prompt"""
|
||||||
logger.debug(f"get_prompt called for: {name}")
|
logger.debug(f"get_prompt called for: {name}")
|
||||||
|
|
||||||
if name == "edit_image":
|
if name == "gpt_edit_image_with_mask":
|
||||||
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 not arguments or "image_path" not in arguments or "mask_path" not in arguments or "edit_description" not in arguments:
|
if not arguments or "image_path" not in arguments or "mask_path" not in arguments or "edit_description" not in arguments:
|
||||||
return [PromptMessage(
|
return [PromptMessage(
|
||||||
role="user",
|
role="user",
|
||||||
@@ -189,13 +149,13 @@ class GPTEditMCPServer:
|
|||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
|
|
||||||
elif name == "optimize_and_edit":
|
elif name == "gpt_batch_edit":
|
||||||
if not arguments or "image_path" not in arguments or "edit_description" not in arguments:
|
if not arguments or "image_paths" not in arguments or "edit_description" not in arguments:
|
||||||
return [PromptMessage(
|
return [PromptMessage(
|
||||||
role="user",
|
role="user",
|
||||||
content=TextContent(
|
content=TextContent(
|
||||||
type="text",
|
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",
|
role="user",
|
||||||
content=TextContent(
|
content=TextContent(
|
||||||
type="text",
|
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']}"
|
||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
|
|
||||||
|
|||||||
@@ -7,81 +7,15 @@ from mcp.types import Tool
|
|||||||
class MCPToolDefinitions:
|
class MCPToolDefinitions:
|
||||||
"""MCP tool definitions for GPTEdit"""
|
"""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
|
@staticmethod
|
||||||
def get_edit_with_mask_tool() -> Tool:
|
def get_gpt_batch_edit_tool() -> Tool:
|
||||||
"""Get edit_with_mask tool definition"""
|
"""Get gpt_batch_edit tool definition"""
|
||||||
return Tool(
|
return Tool(
|
||||||
name="edit_with_mask",
|
name="gpt_batch_edit",
|
||||||
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",
|
|
||||||
description="Edit multiple images with the same or different prompts (max 16 images)",
|
description="Edit multiple images with the same or different prompts (max 16 images)",
|
||||||
inputSchema={
|
inputSchema={
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@@ -165,10 +99,10 @@ class MCPToolDefinitions:
|
|||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_edit_image_from_file_tool() -> Tool:
|
def get_gpt_edit_image_tool() -> Tool:
|
||||||
"""Get edit_image_from_file tool definition (for file paths only)"""
|
"""Get gpt_edit_image tool definition (for file paths only)"""
|
||||||
return Tool(
|
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)",
|
description="Edit an image file by providing the file path (alternative to base64 input)",
|
||||||
inputSchema={
|
inputSchema={
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@@ -203,10 +137,10 @@ class MCPToolDefinitions:
|
|||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_edit_with_mask_from_file_tool() -> Tool:
|
def get_gpt_edit_image_with_mask_tool() -> Tool:
|
||||||
"""Get edit_with_mask_from_file tool definition (for file paths only)"""
|
"""Get gpt_edit_image_with_mask tool definition (for file paths only)"""
|
||||||
return Tool(
|
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)",
|
description="Edit an image with mask by providing file paths (alternative to base64 input)",
|
||||||
inputSchema={
|
inputSchema={
|
||||||
"type": "object",
|
"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
|
@staticmethod
|
||||||
def get_all_tools() -> List[Tool]:
|
def get_all_tools() -> List[Tool]:
|
||||||
"""Get all available tools"""
|
"""Get all available tools"""
|
||||||
return [
|
return [
|
||||||
# Main editing tools
|
# File-based tools (recommended - highest priority)
|
||||||
MCPToolDefinitions.get_edit_image_tool(),
|
MCPToolDefinitions.get_gpt_edit_image_tool(), # ⭐⭐⭐ 최우선
|
||||||
MCPToolDefinitions.get_edit_with_mask_tool(),
|
MCPToolDefinitions.get_gpt_edit_image_with_mask_tool(), # ⭐⭐⭐ 최우선
|
||||||
MCPToolDefinitions.get_batch_edit_tool(),
|
# Utility tools (important - medium priority)
|
||||||
# File-based tools (recommended)
|
MCPToolDefinitions.get_validate_image_tool(), # ⭐⭐ 중요
|
||||||
MCPToolDefinitions.get_edit_image_from_file_tool(),
|
MCPToolDefinitions.get_create_mask_from_alpha_tool(), # ⭐⭐ 중요
|
||||||
MCPToolDefinitions.get_edit_with_mask_from_file_tool(),
|
# Advanced/automation tool (special purpose - low priority)
|
||||||
# Utility tools
|
MCPToolDefinitions.get_gpt_batch_edit_tool() # ⭐ 특수 목적용 (Claude Code 등)
|
||||||
MCPToolDefinitions.get_validate_image_tool(),
|
|
||||||
MCPToolDefinitions.get_create_mask_from_alpha_tool(),
|
|
||||||
MCPToolDefinitions.get_move_temp_to_output_tool()
|
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user