RafaelJaime commited on
Commit
195f21b
·
2 Parent(s): efbd483 705282e

Merge branch 'main' of https://huggingface.co/spaces/AgentsGuards/agents-guard-mcp

Browse files
mcp_server.py CHANGED
@@ -1,15 +1,18 @@
1
  from mcp.server.fastmcp import FastMCP
2
  from src.utils.change_format import change_format
3
- from src.utils.image_helpers import remove_background_from_url
4
  from src.utils.visualize_image import visualize_base64_image
5
  from src.utils.generate_image import generate_image
6
  from src.utils.apply_filter import apply_filter
7
  from src.utils.add_text import add_text_to_image
8
  from src.utils.watermark import add_watermark, remove_watermark
 
 
9
 
10
  mcp = FastMCP("Youtube Service")
11
 
12
  mcp.add_tool(remove_background_from_url)
 
13
  mcp.add_tool(change_format)
14
  mcp.add_tool(visualize_base64_image)
15
  mcp.add_tool(generate_image)
@@ -17,6 +20,7 @@ mcp.add_tool(apply_filter)
17
  mcp.add_tool(add_text_to_image)
18
  mcp.add_tool(add_watermark)
19
  mcp.add_tool(remove_watermark)
 
20
 
21
  if __name__ == "__main__":
22
  mcp.run()
 
1
  from mcp.server.fastmcp import FastMCP
2
  from src.utils.change_format import change_format
3
+ from src.utils.remove_background import remove_background_from_url
4
  from src.utils.visualize_image import visualize_base64_image
5
  from src.utils.generate_image import generate_image
6
  from src.utils.apply_filter import apply_filter
7
  from src.utils.add_text import add_text_to_image
8
  from src.utils.watermark import add_watermark, remove_watermark
9
+ from src.utils.describe import describe_image
10
+ from src.utils.compress import compress_image
11
 
12
  mcp = FastMCP("Youtube Service")
13
 
14
  mcp.add_tool(remove_background_from_url)
15
+ mcp.add_tool(describe_image)
16
  mcp.add_tool(change_format)
17
  mcp.add_tool(visualize_base64_image)
18
  mcp.add_tool(generate_image)
 
20
  mcp.add_tool(add_text_to_image)
21
  mcp.add_tool(add_watermark)
22
  mcp.add_tool(remove_watermark)
23
+ mcp.add_tool(compress_image)
24
 
25
  if __name__ == "__main__":
26
  mcp.run()
src/utils/compress.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from PIL import Image
2
+ import os
3
+ from typing import Literal, Optional
4
+
5
+ def compress_image(
6
+ input_path: str,
7
+ output_path: str,
8
+ quality: int = 85,
9
+ format: Literal["JPEG", "PNG", "WEBP"] = "JPEG",
10
+ max_width: Optional[int] = None,
11
+ max_height: Optional[int] = None
12
+ ) -> str:
13
+ """
14
+ Compress an image file.
15
+
16
+ Args:
17
+ input_path: Path to input image
18
+ output_path: Path for compressed output
19
+ quality: Compression quality 1-95 (for JPEG/WEBP)
20
+ format: Output format
21
+ max_width: Maximum width (optional)
22
+ max_height: Maximum height (optional)
23
+ """
24
+ try:
25
+ if not os.path.splitext(output_path)[1]:
26
+ extension_map = {"JPEG": ".jpg", "PNG": ".png", "WEBP": ".webp"}
27
+ output_path = output_path + extension_map[format]
28
+
29
+ with Image.open(input_path) as img:
30
+ if format == "JPEG" and img.mode in ("RGBA", "P"):
31
+ img = img.convert("RGB")
32
+
33
+ if max_width or max_height:
34
+ img.thumbnail((max_width or img.width, max_height or img.height), Image.Resampling.LANCZOS)
35
+
36
+ save_kwargs = {"format": format, "optimize": True}
37
+ if format in ["JPEG", "WEBP"]:
38
+ save_kwargs["quality"] = quality
39
+
40
+ img.save(output_path, **save_kwargs)
41
+
42
+ original_size = os.path.getsize(input_path) / 1024 / 1024
43
+ compressed_size = os.path.getsize(output_path) / 1024 / 1024
44
+ reduction = (1 - compressed_size/original_size) * 100
45
+
46
+ return f"✅ Compressed successfully!\nOriginal: {original_size:.2f}MB → Compressed: {compressed_size:.2f}MB\nReduction: {reduction:.1f}%"
47
+
48
+ except Exception as e:
49
+ return f"❌ Error: {str(e)}"
src/utils/describe.py ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import base64
3
+ import requests
4
+ from pathlib import Path
5
+ from openai import OpenAI
6
+ from urllib.parse import urlparse
7
+
8
+ def describe_image(image_path: str) -> str:
9
+ """
10
+ Generate a description of the image at the given path or URL.
11
+
12
+ Args:
13
+ image_path: Path to local image file OR URL to image
14
+
15
+ Returns:
16
+ A string description of the image """
17
+
18
+ # Check if API key is available
19
+ api_key = os.getenv("NEBIUS_API_KEY")
20
+ if not api_key:
21
+ return "Error: NEBIUS_API_KEY environment variable not set"
22
+
23
+ try:
24
+ # Determine if it's a URL or local file path
25
+ parsed = urlparse(image_path)
26
+ is_url = bool(parsed.scheme and parsed.netloc)
27
+
28
+ if is_url:
29
+ # Handle URL
30
+ print(f"📡 Downloading image from URL: {image_path}")
31
+ response = requests.get(image_path, timeout=30)
32
+ response.raise_for_status()
33
+ image_data = response.content
34
+
35
+ # Determine content type from response headers
36
+ content_type = response.headers.get('content-type', '')
37
+ if 'image' not in content_type:
38
+ return f"Error: URL does not appear to contain an image. Content-Type: {content_type}"
39
+
40
+ else:
41
+ # Handle local file
42
+ image_path = Path(image_path)
43
+
44
+ if not image_path.exists():
45
+ return f"Error: Local file not found: {image_path}"
46
+
47
+ # Check if it's an image file
48
+ valid_extensions = {'.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp'}
49
+ if image_path.suffix.lower() not in valid_extensions:
50
+ return f"Error: Unsupported file type '{image_path.suffix}'. Supported: {valid_extensions}"
51
+
52
+ print(f"📁 Reading local image: {image_path}")
53
+ with open(image_path, "rb") as f:
54
+ image_data = f.read()
55
+
56
+ # Encode image to base64
57
+ base64_image = base64.b64encode(image_data).decode('utf-8')
58
+
59
+ # Create OpenAI client
60
+ client = OpenAI(
61
+ base_url="https://api.studio.nebius.com/v1/",
62
+ api_key=api_key
63
+ )
64
+
65
+ # Make API call with proper vision format
66
+ response = client.chat.completions.create(
67
+ model="mistralai/Mistral-Small-3.1-24B-Instruct-2503",
68
+ messages=[
69
+ {
70
+ "role": "system",
71
+ "content": "You are a helpful assistant that provides detailed descriptions of images. Focus on the main subjects, colors, composition, and any notable details."
72
+ },
73
+ {
74
+ "role": "user",
75
+ "content": [
76
+ {
77
+ "type": "text",
78
+ "text": "Please provide a detailed description of this image."
79
+ },
80
+ {
81
+ "type": "image_url",
82
+ "image_url": {
83
+ "url": f"data:image/jpeg;base64,{base64_image}"
84
+ }
85
+ }
86
+ ]
87
+ }
88
+ ],
89
+ max_tokens=500
90
+ )
91
+
92
+ description = response.choices[0].message.content.strip()
93
+ return description
94
+
95
+ except requests.RequestException as e:
96
+ return f"Error downloading image from URL: {str(e)}"
97
+ except FileNotFoundError:
98
+ return f"Error: File not found: {image_path}"
99
+ except Exception as e:
100
+ error_msg = str(e)
101
+
102
+ if "vision" in error_msg.lower() or "image" in error_msg.lower():
103
+ return f"Error: This model may not support vision capabilities. Try a vision-enabled model. Details: {error_msg}"
104
+ elif "401" in error_msg or "unauthorized" in error_msg.lower():
105
+ return "Error: Invalid API key or insufficient permissions"
106
+ elif "rate" in error_msg.lower() or "quota" in error_msg.lower():
107
+ return f"Error: API rate limit or quota exceeded: {error_msg}"
108
+ else:
109
+ return f"Error processing image: {error_msg}"
src/utils/{image_helpers.py → remove_background.py} RENAMED
File without changes