media-agent-mcp 0.3.11__tar.gz → 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. {media_agent_mcp-0.3.11 → media_agent_mcp-0.4.0}/PKG-INFO +2 -1
  2. {media_agent_mcp-0.3.11 → media_agent_mcp-0.4.0}/pyproject.toml +2 -1
  3. media_agent_mcp-0.4.0/src/media_agent_mcp/ai_models/openaiedit.py +89 -0
  4. {media_agent_mcp-0.3.11 → media_agent_mcp-0.4.0}/src/media_agent_mcp/ai_models/seededit.py +52 -3
  5. {media_agent_mcp-0.3.11 → media_agent_mcp-0.4.0}/src/media_agent_mcp/async_server.py +26 -17
  6. {media_agent_mcp-0.3.11 → media_agent_mcp-0.4.0}/src/media_agent_mcp/async_wrapper.py +17 -10
  7. {media_agent_mcp-0.3.11 → media_agent_mcp-0.4.0}/src/media_agent_mcp/media_selectors/image_selector.py +3 -0
  8. {media_agent_mcp-0.3.11 → media_agent_mcp-0.4.0}/src/media_agent_mcp/media_selectors/video_selector.py +3 -0
  9. {media_agent_mcp-0.3.11 → media_agent_mcp-0.4.0}/src/media_agent_mcp.egg-info/PKG-INFO +2 -1
  10. {media_agent_mcp-0.3.11 → media_agent_mcp-0.4.0}/src/media_agent_mcp.egg-info/SOURCES.txt +1 -0
  11. {media_agent_mcp-0.3.11 → media_agent_mcp-0.4.0}/src/media_agent_mcp.egg-info/requires.txt +1 -0
  12. {media_agent_mcp-0.3.11 → media_agent_mcp-0.4.0}/README.md +0 -0
  13. {media_agent_mcp-0.3.11 → media_agent_mcp-0.4.0}/setup.cfg +0 -0
  14. {media_agent_mcp-0.3.11 → media_agent_mcp-0.4.0}/src/media_agent_mcp/__init__.py +0 -0
  15. {media_agent_mcp-0.3.11 → media_agent_mcp-0.4.0}/src/media_agent_mcp/ai_models/__init__.py +0 -0
  16. {media_agent_mcp-0.3.11 → media_agent_mcp-0.4.0}/src/media_agent_mcp/ai_models/seed16.py +0 -0
  17. {media_agent_mcp-0.3.11 → media_agent_mcp-0.4.0}/src/media_agent_mcp/ai_models/seedance.py +0 -0
  18. {media_agent_mcp-0.3.11 → media_agent_mcp-0.4.0}/src/media_agent_mcp/ai_models/seedream.py +0 -0
  19. {media_agent_mcp-0.3.11 → media_agent_mcp-0.4.0}/src/media_agent_mcp/media_selectors/__init__.py +0 -0
  20. {media_agent_mcp-0.3.11 → media_agent_mcp-0.4.0}/src/media_agent_mcp/server.py +0 -0
  21. {media_agent_mcp-0.3.11 → media_agent_mcp-0.4.0}/src/media_agent_mcp/storage/__init__.py +0 -0
  22. {media_agent_mcp-0.3.11 → media_agent_mcp-0.4.0}/src/media_agent_mcp/storage/tos_client.py +0 -0
  23. {media_agent_mcp-0.3.11 → media_agent_mcp-0.4.0}/src/media_agent_mcp/video/__init__.py +0 -0
  24. {media_agent_mcp-0.3.11 → media_agent_mcp-0.4.0}/src/media_agent_mcp/video/processor.py +0 -0
  25. {media_agent_mcp-0.3.11 → media_agent_mcp-0.4.0}/src/media_agent_mcp.egg-info/dependency_links.txt +0 -0
  26. {media_agent_mcp-0.3.11 → media_agent_mcp-0.4.0}/src/media_agent_mcp.egg-info/entry_points.txt +0 -0
  27. {media_agent_mcp-0.3.11 → media_agent_mcp-0.4.0}/src/media_agent_mcp.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: media-agent-mcp
3
- Version: 0.3.11
3
+ Version: 0.4.0
4
4
  Summary: A Model Context Protocol server for media processing with AI tools
5
5
  Author-email: Media Agent Team <team@mediaagent.com>
6
6
  Keywords: mcp,ai,media,video,image,processing
@@ -22,6 +22,7 @@ Requires-Dist: numpy>=1.24.0
22
22
  Requires-Dist: python-dotenv>=1.0.0
23
23
  Requires-Dist: volcengine-python-sdk>=1.0.0
24
24
  Requires-Dist: volcengine>=1.0.194
25
+ Requires-Dist: openai>=1.97.1
25
26
 
26
27
  # Media Agent MCP
27
28
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "media-agent-mcp"
3
- version = "0.3.11"
3
+ version = "0.4.0"
4
4
  description = "A Model Context Protocol server for media processing with AI tools"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -27,6 +27,7 @@ dependencies = [
27
27
  "python-dotenv>=1.0.0",
28
28
  "volcengine-python-sdk>=1.0.0",
29
29
  "volcengine>=1.0.194",
30
+ "openai>=1.97.1",
30
31
  ]
31
32
 
32
33
  [project.scripts]
@@ -0,0 +1,89 @@
1
+ import os
2
+ from typing import Dict, Any
3
+ import openai
4
+ import requests
5
+ from PIL import Image
6
+ from io import BytesIO
7
+ import tempfile
8
+ import os
9
+ from urllib.parse import urlparse
10
+
11
+ def openaiedit(image_url: str, prompt: str, size: str = "1024x1024") -> Dict[str, Any]:
12
+ """
13
+ Perform image editing using the OpenAI Images API.
14
+
15
+ :param image_url: URL of the input image.
16
+ :param prompt: The editing prompt.
17
+ :param size: The size of the generated images. Must be one of "256x256", "512x512", or "1024x1024".
18
+ :return: JSON response with status, data (image URL), and message.
19
+ """
20
+ try:
21
+ client = openai.OpenAI(
22
+ api_key='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'
23
+ )
24
+
25
+ # Download the image
26
+ response = requests.get(image_url)
27
+ response.raise_for_status()
28
+
29
+ # Save image to a temporary file
30
+ parsed_url = urlparse(image_url)
31
+ file_ext = os.path.splitext(parsed_url.path)[1]
32
+ if not file_ext:
33
+ # Fallback if extension is not in URL path
34
+ content_type = response.headers.get('content-type')
35
+ if content_type and 'image' in content_type:
36
+ file_ext = '.' + content_type.split('/')[1]
37
+ else:
38
+ file_ext = '.png' # default
39
+
40
+ temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=file_ext)
41
+ try:
42
+ temp_file.write(response.content)
43
+ temp_file.close()
44
+
45
+ # Call OpenAI API with the local file
46
+ with open(temp_file.name, "rb") as f:
47
+ response = client.images.edit(
48
+ model="gpt-image-1",
49
+ image=f,
50
+ prompt=prompt,
51
+ n=1,
52
+ size=size
53
+ )
54
+ finally:
55
+ os.unlink(temp_file.name) # Clean up the temporary file
56
+
57
+ image_url = response.data[0].url
58
+
59
+ return {
60
+ "status": "success",
61
+ "data": {"image_url": image_url},
62
+ "message": "Image edited successfully."
63
+ }
64
+ except openai.APIError as e:
65
+ return {
66
+ "status": "error",
67
+ "data": None,
68
+ "message": f"OpenAI API Error: {e}"
69
+ }
70
+ except requests.RequestException as e:
71
+ return {
72
+ "status": "error",
73
+ "data": None,
74
+ "message": f"Error downloading image: {e}"
75
+ }
76
+ except Exception as e:
77
+ return {
78
+ "status": "error",
79
+ "data": None,
80
+ "message": f"An unexpected error occurred: {e}"
81
+ }
82
+
83
+ if __name__ == '__main__':
84
+ # Make sure to set your OPENAI_API_KEY environment variable
85
+ # For example: export OPENAI_API_KEY='your-api-key'
86
+ image_url = 'https://carey.tos-ap-southeast-1.bytepluses.com/Art%20Portrait/Art%20Portrait/Art%20Portrait/Art%20Portrait%20(1).jpg'
87
+ prompt = 'A cute baby sea otter cooking a meal'
88
+ result = openaiedit(image_url, prompt)
89
+ print(result)
@@ -1,7 +1,20 @@
1
1
  import os
2
+ import tempfile
2
3
  from typing import Dict, Any
4
+
5
+ import requests
3
6
  from volcengine.visual.VisualService import VisualService
4
7
 
8
+ try:
9
+ from ..storage.tos_client import upload_to_tos
10
+ except (ImportError, ValueError):
11
+ # This fallback is for running the script directly
12
+ import sys
13
+ import os
14
+
15
+ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')))
16
+ from media_agent_mcp.storage.tos_client import upload_to_tos
17
+
5
18
 
6
19
  def parse_seededit_response(response) -> Dict[str, Any]:
7
20
  """
@@ -44,7 +57,7 @@ def parse_seededit_response(response) -> Dict[str, Any]:
44
57
 
45
58
  def seededit(image_url, prompt, charactor_keep=False, return_url=True, scale=1, seed=-1) -> Dict[str, Any]:
46
59
  """
47
- Perform image editing using the VisualService.
60
+ Perform image editing using the VisualService, then upload the result to TOS.
48
61
 
49
62
  :param image_url: URL of the input image.
50
63
  :param prompt: The editing prompt.
@@ -52,7 +65,7 @@ def seededit(image_url, prompt, charactor_keep=False, return_url=True, scale=1,
52
65
  :param return_url: Whether to return image URL or base64 string.
53
66
  :param scale: Text influence scale (0.1-1.0).
54
67
  :param seed: Random seed for reproducibility.
55
- :return: JSON response with status, data (image URL), and message.
68
+ :return: JSON response with status, data (TOS URL), and message.
56
69
  """
57
70
  try:
58
71
  visual_service = VisualService()
@@ -81,7 +94,43 @@ def seededit(image_url, prompt, charactor_keep=False, return_url=True, scale=1,
81
94
  }
82
95
 
83
96
  response = visual_service.cv_process(form)
84
- return parse_seededit_response(response)
97
+ parsed_response = parse_seededit_response(response)
98
+
99
+ if parsed_response['status'] == 'success':
100
+ generated_image_url = parsed_response['data']['image_url']
101
+
102
+ # Download the image from the URL
103
+ try:
104
+ image_response = requests.get(generated_image_url, stream=True)
105
+ image_response.raise_for_status() # Raise an exception for bad status codes
106
+ except requests.exceptions.RequestException as e:
107
+ return {"status": "error", "data": None, "message": f"Failed to download image: {e}"}
108
+
109
+ # Create a temporary file to save the image
110
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".jpg") as temp_file:
111
+ for chunk in image_response.iter_content(chunk_size=8192):
112
+ temp_file.write(chunk)
113
+ temp_file_path = temp_file.name
114
+
115
+ try:
116
+ # Upload the temporary file to TOS
117
+ tos_response = upload_to_tos(temp_file_path)
118
+ # The URL is in tos_response['data']['url']
119
+ # The final response should be compatible with other tools
120
+ if tos_response['status'] == 'success':
121
+ return {
122
+ "status": "success",
123
+ "data": {"image_url": tos_response['data']['url']},
124
+ "message": "Image edited and uploaded to TOS successfully"
125
+ }
126
+ else:
127
+ return tos_response
128
+ finally:
129
+ # Clean up the temporary file
130
+ os.remove(temp_file_path)
131
+ else:
132
+ return parsed_response
133
+
85
134
  except Exception as e:
86
135
  return {
87
136
  "status": "error",
@@ -48,16 +48,12 @@ from mcp.server.fastmcp import FastMCP
48
48
 
49
49
  # Import async wrappers
50
50
  from media_agent_mcp.async_wrapper import (
51
- async_video_concat_tool,
52
- async_video_last_frame_tool,
53
- async_seedream_generate_image_tool,
54
- async_seedance_generate_video_tool,
55
- async_seededit_tool,
56
- async_vlm_vision_task_tool,
57
- async_image_selector_tool,
58
- async_video_selector_tool,
59
- async_tos_save_content_tool,
60
- cleanup_executor
51
+ async_video_concat_tool, async_video_last_frame_tool,
52
+ async_seedream_generate_image_tool, async_seedance_generate_video_tool,
53
+ async_seededit_tool, async_vlm_vision_task_tool,
54
+ async_image_selector_tool, async_video_selector_tool,
55
+ async_tos_save_content_tool, cleanup_executor,
56
+ async_openaiedit_tool
61
57
  )
62
58
 
63
59
  # Configure logging
@@ -138,22 +134,35 @@ async def seedance_generate_video_tool(prompt: str, first_frame_image: str,
138
134
 
139
135
  @mcp.tool()
140
136
  @async_retry()
141
- async def seededit_tool(image_url: str, prompt: str, seed: int = -1,
142
- scale: float = 0.5, charactor_keep: bool = False) -> dict:
137
+ async def seededit_tool(image_url: str, prompt: str) -> dict:
143
138
  """
144
- Asynchronously edit an image using Seededit model.
139
+ Asynchronously edit an image using the OpenAI Images API.
145
140
 
146
141
  Args:
147
142
  image_url: Input image URL for editing
148
143
  prompt: Text prompt for image editing
149
- seed: Random seed for reproducibility (-1 for random)
150
- scale: Influence degree of text description (0-1)
151
- charactor_keep: whether to keep the main character in this image, if you wanna change the main character, please keep False
152
144
 
153
145
  Returns:
154
146
  Dictionary with status, data, and message
155
147
  """
156
- return await async_seededit_tool(image_url, prompt, seed, scale, charactor_keep)
148
+ return await async_seededit_tool(image_url, prompt)
149
+
150
+
151
+ @mcp.tool()
152
+ @async_retry()
153
+ async def openaiedit_tool(image_url: str, prompt: str, size: str = "1024x1024") -> dict:
154
+ """
155
+ Asynchronously edit an image using the OpenAI Images API.
156
+
157
+ Args:
158
+ image_url: Input image URL for editing
159
+ prompt: Text prompt for image editing
160
+ size: The size of the generated images. Must be one of "256x256", "512x512", or "1024x1024".
161
+
162
+ Returns:
163
+ Dictionary with status, data, and message
164
+ """
165
+ return await async_openaiedit_tool(image_url, prompt, size)
157
166
 
158
167
 
159
168
  @mcp.tool()
@@ -18,6 +18,7 @@ from media_agent_mcp.video import concat_videos, extract_last_frame
18
18
  from media_agent_mcp.ai_models.seedream import generate_image
19
19
  from media_agent_mcp.ai_models.seedance import generate_video
20
20
  from media_agent_mcp.ai_models.seededit import seededit
21
+ from media_agent_mcp.ai_models.openaiedit import openaiedit
21
22
  from media_agent_mcp.media_selectors.image_selector import select_best_image
22
23
  from media_agent_mcp.media_selectors.video_selector import select_best_video
23
24
 
@@ -128,16 +129,11 @@ def _sync_seedance_generate_video(prompt: str, first_frame_image: str,
128
129
 
129
130
  @async_wrapper
130
131
  @json_response_wrapper
131
- def _sync_seededit(image_url: str, prompt: str, seed: int = -1,
132
- scale: float = 0.5, charactor_keep: bool = False) -> str:
132
+ def _sync_seededit(image_url: str, prompt: str) -> str:
133
133
  """Synchronous image editing wrapper."""
134
134
  return seededit(
135
135
  image_url=image_url,
136
- prompt=prompt,
137
- charactor_keep=charactor_keep,
138
- return_url=True,
139
- scale=scale,
140
- seed=seed
136
+ prompt=prompt
141
137
  )
142
138
 
143
139
 
@@ -238,10 +234,10 @@ async def async_seedance_generate_video_tool(prompt: str, first_frame_image: str
238
234
  return await _sync_seedance_generate_video(prompt, first_frame_image, last_frame_image, duration, resolution)
239
235
 
240
236
 
241
- async def async_seededit_tool(image_url: str, prompt: str, seed: int = -1,
242
- scale: float = 0.5, charactor_keep: bool = False) -> str:
237
+ async def async_seededit_tool(image_url: str, prompt: str) -> str:
243
238
  """Async image editing tool."""
244
- return await _sync_seededit(image_url, prompt, seed, scale, charactor_keep)
239
+ result = await _sync_seededit(image_url, prompt)
240
+ return json.loads(result)
245
241
 
246
242
 
247
243
  async def async_vlm_vision_task_tool(messages: List) -> str:
@@ -258,6 +254,17 @@ async def async_video_selector_tool(video_paths: List[str], prompt: str) -> str:
258
254
  """Async video selector tool."""
259
255
  return await _sync_video_selector(video_paths, prompt)
260
256
 
257
+ @async_wrapper
258
+ @json_response_wrapper
259
+ def _sync_openaiedit(image_url: str, prompt: str, size: str = "1024x1024") -> str:
260
+ """Synchronous image editing wrapper for openaiedit."""
261
+ return openaiedit(image_url=image_url, prompt=prompt, size=size)
262
+
263
+ async def async_openaiedit_tool(image_url: str, prompt: str, size: str = "1024x1024") -> str:
264
+ """Async image editing tool for openaiedit."""
265
+ result = await _sync_openaiedit(image_url, prompt, size)
266
+ return json.loads(result)
267
+
261
268
 
262
269
  async def async_tos_save_content_tool(content: str, file_extension: str = "txt",
263
270
  object_key: Optional[str] = None) -> str:
@@ -76,6 +76,7 @@ def select_best_image(image_urls: List[str], prompt: str) -> dict:
76
76
  except Exception as e:
77
77
  logger.error(f"[VLM]Error selecting image: {e}")
78
78
  return {
79
+ "status": "error",
79
80
  "choice": None,
80
81
  "reason": f"Error selecting image: {str(e)}",
81
82
  "url": image_urls[0] if image_urls else None
@@ -94,6 +95,7 @@ def select_best_image(image_urls: List[str], prompt: str) -> dict:
94
95
  except Exception as e:
95
96
  logger.error(f"[VLM]Error parsing response: {e}")
96
97
  return {
98
+ "status": "error",
97
99
  "choice": None,
98
100
  "reason": f"Error parsing response: {str(e)}",
99
101
  "url": image_urls[0]
@@ -102,6 +104,7 @@ def select_best_image(image_urls: List[str], prompt: str) -> dict:
102
104
  except Exception as e:
103
105
  logger.error(f"Error selecting image: {e}")
104
106
  return {
107
+ "status": "error",
105
108
  "choice": None,
106
109
  "reason": f"Error selecting image: {str(e)}",
107
110
  "url": image_urls[0] if image_urls else None
@@ -116,6 +116,7 @@ def select_best_video(video_urls: List[str], prompt: str) -> dict:
116
116
  except Exception as e:
117
117
  logger.error(f"[VLM]Error selecting image: {e}")
118
118
  return {
119
+ "status": "error",
119
120
  "choice": None,
120
121
  "reason": f"Error selecting image: {str(e)}",
121
122
  "url": video_urls[0] if video_urls else None
@@ -134,6 +135,7 @@ def select_best_video(video_urls: List[str], prompt: str) -> dict:
134
135
  except Exception as e:
135
136
  logger.error(f"[VLM]Error parsing response: {e}")
136
137
  return {
138
+ "status": "error",
137
139
  "choice": None,
138
140
  "reason": f"Error parsing response: {str(e)}",
139
141
  "url": video_urls[0]
@@ -142,6 +144,7 @@ def select_best_video(video_urls: List[str], prompt: str) -> dict:
142
144
  except Exception as e:
143
145
  logger.error(f"Error selecting image: {e}")
144
146
  return {
147
+ "status": "error",
145
148
  "choice": None,
146
149
  "reason": f"Error selecting image: {str(e)}",
147
150
  "url": video_urls[0] if video_urls else None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: media-agent-mcp
3
- Version: 0.3.11
3
+ Version: 0.4.0
4
4
  Summary: A Model Context Protocol server for media processing with AI tools
5
5
  Author-email: Media Agent Team <team@mediaagent.com>
6
6
  Keywords: mcp,ai,media,video,image,processing
@@ -22,6 +22,7 @@ Requires-Dist: numpy>=1.24.0
22
22
  Requires-Dist: python-dotenv>=1.0.0
23
23
  Requires-Dist: volcengine-python-sdk>=1.0.0
24
24
  Requires-Dist: volcengine>=1.0.194
25
+ Requires-Dist: openai>=1.97.1
25
26
 
26
27
  # Media Agent MCP
27
28
 
@@ -11,6 +11,7 @@ src/media_agent_mcp.egg-info/entry_points.txt
11
11
  src/media_agent_mcp.egg-info/requires.txt
12
12
  src/media_agent_mcp.egg-info/top_level.txt
13
13
  src/media_agent_mcp/ai_models/__init__.py
14
+ src/media_agent_mcp/ai_models/openaiedit.py
14
15
  src/media_agent_mcp/ai_models/seed16.py
15
16
  src/media_agent_mcp/ai_models/seedance.py
16
17
  src/media_agent_mcp/ai_models/seededit.py
@@ -9,3 +9,4 @@ numpy>=1.24.0
9
9
  python-dotenv>=1.0.0
10
10
  volcengine-python-sdk>=1.0.0
11
11
  volcengine>=1.0.194
12
+ openai>=1.97.1