media-agent-mcp 2.6.3__tar.gz → 2.6.5__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 (56) hide show
  1. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/PKG-INFO +1 -1
  2. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/pyproject.toml +1 -1
  3. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/async_server.py +53 -1
  4. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/server.py +60 -1
  5. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp.egg-info/PKG-INFO +1 -1
  6. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/README.md +0 -0
  7. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/setup.cfg +0 -0
  8. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/__init__.py +0 -0
  9. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/ai_models/__init__.py +0 -0
  10. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/ai_models/omni_human.py +0 -0
  11. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/ai_models/openaiedit.py +0 -0
  12. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/ai_models/seed16.py +0 -0
  13. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/ai_models/seedance.py +0 -0
  14. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/ai_models/seededit.py +0 -0
  15. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/ai_models/seedream.py +0 -0
  16. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/ai_models/tts.py +0 -0
  17. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/async_wrapper.py +0 -0
  18. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/audio/combiner.py +0 -0
  19. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/audio/speed_controller.py +0 -0
  20. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/audio/tts.py +0 -0
  21. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/be/README.md +0 -0
  22. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/be/__init__.py +0 -0
  23. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/be/__pycache__/__init__.cpython-312.pyc +0 -0
  24. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/be/__pycache__/app.cpython-312.pyc +0 -0
  25. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/be/__pycache__/routes_media.cpython-312.pyc +0 -0
  26. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/be/__pycache__/routes_omni.cpython-312.pyc +0 -0
  27. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/be/__pycache__/routes_subtitles.cpython-312.pyc +0 -0
  28. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/be/__pycache__/utils.cpython-312.pyc +0 -0
  29. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/be/app.py +0 -0
  30. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/be/fonts/en/EduNSWACTCursive-VariableFont_wght.ttf +0 -0
  31. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/be/fonts/en/MozillaText-VariableFont_wght.ttf +0 -0
  32. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/be/fonts/en/Roboto_Condensed-Regular.ttf +0 -0
  33. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/be/fonts/zh/MaShanZheng-Regular.ttf +0 -0
  34. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/be/fonts/zh/NotoSerifSC-VariableFont_wght.ttf +0 -0
  35. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/be/fonts/zh/ZCOOLXiaoWei-Regular.ttf +0 -0
  36. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/be/pyproject.toml +0 -0
  37. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/be/routes_media.py +0 -0
  38. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/be/routes_subtitles.py +0 -0
  39. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/be/utils.py +0 -0
  40. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/be/uv.lock +0 -0
  41. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/install_tools/__init__.py +0 -0
  42. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/install_tools/installer.py +0 -0
  43. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/media_selectors/__init__.py +0 -0
  44. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/media_selectors/image_selector.py +0 -0
  45. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/media_selectors/video_selector.py +0 -0
  46. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/storage/__init__.py +0 -0
  47. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/storage/tos_client.py +0 -0
  48. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/video/__init__.py +0 -0
  49. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/video/processor.py +0 -0
  50. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/video/stack.py +0 -0
  51. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp/video/subtitle.py +0 -0
  52. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp.egg-info/SOURCES.txt +0 -0
  53. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp.egg-info/dependency_links.txt +0 -0
  54. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp.egg-info/entry_points.txt +0 -0
  55. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/src/media_agent_mcp.egg-info/requires.txt +0 -0
  56. {media_agent_mcp-2.6.3 → media_agent_mcp-2.6.5}/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: 2.6.3
3
+ Version: 2.6.5
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "media-agent-mcp"
3
- version = "2.6.3"
3
+ version = "2.6.5"
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"
@@ -24,6 +24,7 @@ from typing import List, Optional
24
24
  import json
25
25
  from dotenv import load_dotenv
26
26
  import uvicorn
27
+ import anyio
27
28
  from functools import wraps
28
29
 
29
30
  def async_retry(max_retries=3, delay=2):
@@ -80,6 +81,57 @@ from media_agent_mcp.async_wrapper import (
80
81
  logging.basicConfig(level=logging.INFO)
81
82
  logger = logging.getLogger(__name__)
82
83
 
84
+ # Swallow ClosedResourceError from AnyIO (e.g., SSE client disconnected)
85
+ class IgnoreClosedResourceErrorMiddleware:
86
+ def __init__(self, app):
87
+ self.app = app
88
+
89
+ async def __call__(self, scope, receive, send):
90
+ try:
91
+ await self.app(scope, receive, send)
92
+ except anyio.ClosedResourceError:
93
+ logger.warning("SSE client disconnected (ClosedResourceError). Ignoring.")
94
+ return
95
+
96
+ # Enhanced middleware that supports reconnection for SSE clients
97
+ class ReconnectableSSEMiddleware:
98
+ def __init__(self, app):
99
+ self.app = app
100
+ self.connections = {} # 存储活跃连接
101
+
102
+ async def __call__(self, scope, receive, send):
103
+ # 为每个连接生成唯一ID
104
+ connection_id = scope.get('client', ('unknown', 0))[0] + ':' + str(scope.get('client', ('unknown', 0))[1])
105
+
106
+ # 包装send函数以跟踪连接状态
107
+ original_send = send
108
+
109
+ async def wrapped_send(message):
110
+ if message.get('type') == 'http.response.start':
111
+ # 记录新连接
112
+ self.connections[connection_id] = {'active': True}
113
+ logger.info(f"New SSE connection established: {connection_id}")
114
+ elif message.get('type') == 'http.response.body' and message.get('more_body', False) is False:
115
+ # 连接结束
116
+ if connection_id in self.connections:
117
+ self.connections[connection_id]['active'] = False
118
+ logger.info(f"SSE connection closed normally: {connection_id}")
119
+
120
+ # 调用原始send
121
+ await original_send(message)
122
+
123
+ try:
124
+ # 使用包装后的send函数
125
+ await self.app(scope, receive, wrapped_send)
126
+ except anyio.ClosedResourceError:
127
+ # 客户端断开连接
128
+ if connection_id in self.connections:
129
+ self.connections[connection_id]['active'] = False
130
+
131
+ logger.warning(f"SSE client disconnected (ClosedResourceError): {connection_id}. Client can reconnect.")
132
+ # 不抛出异常,允许客户端重连
133
+ return
134
+
83
135
  # Initialize FastMCP server (will be configured in main function)
84
136
  load_dotenv()
85
137
  mcp = FastMCP("Media-Agent-MCP-Async")
@@ -524,7 +576,7 @@ def main():
524
576
  mcp.settings.port = args.port
525
577
  # Use uvicorn to run SSE app with extended keep-alive timeout (5 minutes)
526
578
  uvicorn.run(
527
- mcp.sse_app(),
579
+ ReconnectableSSEMiddleware(mcp.sse_app()),
528
580
  host=args.host,
529
581
  port=args.port,
530
582
  timeout_keep_alive=300
@@ -20,6 +20,8 @@ from typing import Optional, Dict, Any
20
20
  import json
21
21
  from dotenv import load_dotenv
22
22
  import uvicorn
23
+ import anyio
24
+ import uuid
23
25
 
24
26
  from mcp.server.fastmcp import FastMCP
25
27
 
@@ -39,6 +41,63 @@ from media_agent_mcp.media_selectors.video_selector import select_best_video
39
41
  logging.basicConfig(level=logging.INFO)
40
42
  logger = logging.getLogger(__name__)
41
43
 
44
+ # Swallow ClosedResourceError from AnyIO (e.g., SSE client disconnected)
45
+ class IgnoreClosedResourceErrorMiddleware:
46
+ def __init__(self, app):
47
+ self.app = app
48
+
49
+ async def __call__(self, scope, receive, send):
50
+ try:
51
+ await self.app(scope, receive, send)
52
+ except anyio.ClosedResourceError:
53
+ logger.warning("SSE client disconnected (ClosedResourceError). Ignoring.")
54
+ return
55
+
56
+ # Enhanced middleware that supports reconnection for SSE clients
57
+ class ReconnectableSSEMiddleware:
58
+ def __init__(self, app):
59
+ self.app = app
60
+ self.connections = {}
61
+
62
+ async def __call__(self, scope, receive, send):
63
+ # Generate a unique connection ID
64
+ connection_id = str(uuid.uuid4())
65
+
66
+ # Store connection info
67
+ if scope["type"] == "http":
68
+ self.connections[connection_id] = {
69
+ "status": "active",
70
+ "path": scope.get("path", ""),
71
+ "created_at": anyio.current_time()
72
+ }
73
+
74
+ # Wrap the send function to track connection state
75
+ original_send = send
76
+
77
+ async def wrapped_send(message):
78
+ if message.get("type") == "http.response.body" and message.get("more_body", False) is False:
79
+ # Connection is closing normally
80
+ if connection_id in self.connections:
81
+ self.connections[connection_id]["status"] = "closed"
82
+ return await original_send(message)
83
+
84
+ try:
85
+ # Use the wrapped send function
86
+ await self.app(scope, receive, wrapped_send)
87
+ except anyio.ClosedResourceError:
88
+ # Client disconnected, but we'll allow reconnection
89
+ if connection_id in self.connections:
90
+ self.connections[connection_id]["status"] = "disconnected"
91
+ logger.warning(f"SSE client disconnected (ClosedResourceError). Connection {connection_id[:8]} marked for reconnection.")
92
+ return
93
+ finally:
94
+ # Clean up connection tracking after some time
95
+ if connection_id in self.connections:
96
+ self.connections.pop(connection_id, None)
97
+ else:
98
+ # For non-HTTP connections, just pass through
99
+ await self.app(scope, receive, send)
100
+
42
101
  # Initialize FastMCP server (will be configured in main function)
43
102
  load_dotenv()
44
103
  mcp = FastMCP("Media-Agent-MCP")
@@ -577,7 +636,7 @@ def main():
577
636
  # Configure and run the server
578
637
  if args.transport == 'sse':
579
638
  # SSE transport
580
- uvicorn.run(mcp.create_sse_app(), host=args.host, port=args.port)
639
+ uvicorn.run(ReconnectableSSEMiddleware(mcp.create_sse_app()), host=args.host, port=args.port)
581
640
  else:
582
641
  # STDIO transport (default)
583
642
  mcp.run()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: media-agent-mcp
3
- Version: 2.6.3
3
+ Version: 2.6.5
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