first commit

This commit is contained in:
Matteo Benedetto
2025-10-02 13:40:11 +02:00
commit 77342c8680
12 changed files with 1078 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
# MCP Image Recognition Server Package
from .server import mcp
+109
View File
@@ -0,0 +1,109 @@
#!/usr/bin/env python3
"""
Post-installation script to automatically configure Kilocode MCP settings.
"""
import json
import os
import sys
from pathlib import Path
def get_kilocode_config_path():
"""Find the Kilocode MCP settings file."""
# Common paths for Kilocode/VSCodium
possible_paths = [
Path.home() / ".config/VSCodium/User/globalStorage/kilocode.kilo-code/settings/mcp_settings.json",
Path.home() / ".config/Code/User/globalStorage/kilocode.kilo-code/settings/mcp_settings.json",
Path.home() / "Library/Application Support/VSCodium/User/globalStorage/kilocode.kilo-code/settings/mcp_settings.json",
Path.home() / "Library/Application Support/Code/User/globalStorage/kilocode.kilo-code/settings/mcp_settings.json",
]
for path in possible_paths:
if path.exists():
return path
return None
def add_to_kilocode_config():
"""Add the image-recognition server to Kilocode's MCP settings."""
config_path = get_kilocode_config_path()
if not config_path:
print("⚠️ Kilocode MCP settings file not found.")
print(" You'll need to manually add the server configuration.")
print("\n Add this to your MCP settings:")
print(json.dumps({
"image-recognition": {
"command": "uvx",
"args": ["image-recognition-mcp"],
"env": {
"OPENAI_API_KEY": "your-openai-api-key-here"
}
}
}, indent=2))
return False
try:
# Read existing config
with open(config_path, 'r') as f:
config = json.load(f)
# Ensure mcpServers exists
if "mcpServers" not in config:
config["mcpServers"] = {}
# Check if already configured
if "image-recognition" in config["mcpServers"]:
print("✅ Image Recognition MCP server already configured in Kilocode")
return True
# Add our server
config["mcpServers"]["image-recognition"] = {
"command": "uvx",
"args": ["image-recognition-mcp"],
"env": {
"OPENAI_API_KEY": "your-openai-api-key-here"
}
}
# Backup original config
backup_path = config_path.with_suffix('.json.backup')
with open(backup_path, 'w') as f:
json.dump(config, f, indent=2)
# Write updated config
with open(config_path, 'w') as f:
json.dump(config, f, indent=2)
print("✅ Successfully added Image Recognition MCP server to Kilocode!")
print(f" Config file: {config_path}")
print(f" Backup saved: {backup_path}")
print("\n⚠️ Remember to:")
print(" 1. Replace 'your-openai-api-key-here' with your actual OpenAI API key")
print(" 2. Restart Kilocode to load the new server")
return True
except Exception as e:
print(f"❌ Error updating Kilocode config: {e}")
return False
def main():
"""Main entry point for the installer."""
print("🚀 Image Recognition MCP Server - Post-Install Configuration")
print("=" * 60)
if add_to_kilocode_config():
print("\n✨ Installation complete!")
else:
print("\n⚠️ Manual configuration required.")
print(" See PUBLISHING.md for instructions.")
return 0
if __name__ == "__main__":
sys.exit(main())
+136
View File
@@ -0,0 +1,136 @@
import base64
import io
import logging
import os
from typing import Optional
from PIL import Image
from fastmcp import FastMCP
import openai
# Configure logging
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
filename='/home/enne2/Sviluppo/tetris-sdl/mcp-image-server/image_server.log'
)
logger = logging.getLogger(__name__)
# Create FastMCP server instance
mcp = FastMCP("ImageRecognitionServer")
# Get OpenAI API key from environment
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY', '')
HAS_OPENAI = bool(OPENAI_API_KEY and OPENAI_API_KEY != 'your-openai-api-key-here')
if HAS_OPENAI:
openai.api_key = OPENAI_API_KEY
logger.info("OpenAI API key configured - AI descriptions enabled")
else:
logger.warning("No valid OpenAI API key - using basic image metadata only")
@mcp.tool()
def describe_image(image_data: str, mime_type: str = 'image/jpeg') -> str:
"""
Describe an image using base64 encoded image data
Args:
image_data: Base64 encoded image data
mime_type: MIME type of the image (default: image/jpeg)
Returns:
Detailed description of the image
"""
try:
logger.debug(f"Describing image - MIME type: {mime_type}")
# Decode base64 image
image_bytes = base64.b64decode(image_data)
image = Image.open(io.BytesIO(image_bytes))
# Log image details
logger.info(f"Image size: {image.size}, mode: {image.mode}")
# If OpenAI is available, use Vision API
if HAS_OPENAI:
try:
response = openai.chat.completions.create(
model="gpt-4o-mini",
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": "Describe this image in detail, including objects, colors, composition, and any text visible."},
{
"type": "image_url",
"image_url": {
"url": f"data:{mime_type};base64,{image_data}"
}
}
]
}
],
max_tokens=500
)
description = response.choices[0].message.content
logger.debug(f"OpenAI description: {description}")
return description
except Exception as e:
logger.error(f"OpenAI API error: {str(e)}", exc_info=True)
# Fall back to basic metadata
return f"OpenAI API error: {str(e)}\n\nBasic metadata:\n- Size: {image.size[0]}x{image.size[1]} pixels\n- Mode: {image.mode}\n- Format: {image.format or 'Unknown'}"
# Return basic metadata if no OpenAI
description = f"Image Analysis (Basic Metadata):\n- Size: {image.size[0]}x{image.size[1]} pixels\n- Mode: {image.mode}\n- Format: {image.format or 'Unknown'}\n\nNote: For AI-powered descriptions, configure OPENAI_API_KEY in MCP settings."
logger.debug(f"Returning basic description: {description}")
return description
except Exception as e:
logger.error(f"Error describing image: {str(e)}", exc_info=True)
return f"Error describing image: {str(e)}"
@mcp.tool()
def describe_image_from_file(file_path: str) -> str:
"""
Describe an image from a file path
Args:
file_path: Path to the image file
Returns:
Detailed description of the image
"""
try:
logger.debug(f"Describing image from file: {file_path}")
# Open the image file
with open(file_path, 'rb') as image_file:
# Encode image to base64
image_data = base64.b64encode(image_file.read()).decode('utf-8')
# Determine MIME type from file extension
mime_type = 'image/jpeg'
if file_path.lower().endswith('.png'):
mime_type = 'image/png'
elif file_path.lower().endswith('.gif'):
mime_type = 'image/gif'
elif file_path.lower().endswith('.webp'):
mime_type = 'image/webp'
# Use the describe_image function
return describe_image(image_data, mime_type)
except Exception as e:
logger.error(f"Error reading image file: {str(e)}", exc_info=True)
return f"Error reading image file: {str(e)}"
def main():
"""Main entry point for the MCP server."""
logger.info("Starting MCP Image Recognition Server")
mcp.run()
if __name__ == "__main__":
main()