mcpcn-office-powerpoint-mcp-server 2.0.9__tar.gz → 2.1.1__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 (31) hide show
  1. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/PKG-INFO +1 -1
  2. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/pyproject.toml +1 -1
  3. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/tools/content_tools.py +33 -23
  4. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/.gitignore +0 -0
  5. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/Dockerfile +0 -0
  6. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/LICENSE +0 -0
  7. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/README.md +0 -0
  8. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/__init__.py +0 -0
  9. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/mcp-config.json +0 -0
  10. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/ppt_mcp_server.py +0 -0
  11. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/requirements.txt +0 -0
  12. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/setup_mcp.py +0 -0
  13. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/slide_layout_templates.json +0 -0
  14. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/smithery.yaml +0 -0
  15. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/tools/__init__.py +0 -0
  16. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/tools/chart_tools.py +0 -0
  17. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/tools/connector_tools.py +0 -0
  18. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/tools/hyperlink_tools.py +0 -0
  19. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/tools/master_tools.py +0 -0
  20. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/tools/presentation_tools.py +0 -0
  21. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/tools/professional_tools.py +0 -0
  22. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/tools/structural_tools.py +0 -0
  23. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/tools/template_tools.py +0 -0
  24. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/tools/transition_tools.py +0 -0
  25. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/utils/__init__.py +0 -0
  26. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/utils/content_utils.py +0 -0
  27. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/utils/core_utils.py +0 -0
  28. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/utils/design_utils.py +0 -0
  29. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/utils/presentation_utils.py +0 -0
  30. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/utils/template_utils.py +0 -0
  31. {mcpcn_office_powerpoint_mcp_server-2.0.9 → mcpcn_office_powerpoint_mcp_server-2.1.1}/utils/validation_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcpcn-office-powerpoint-mcp-server
3
- Version: 2.0.9
3
+ Version: 2.1.1
4
4
  Summary: MCP Server for PowerPoint manipulation using python-pptx - Consolidated Edition
5
5
  Project-URL: Homepage, https://github.com/GongRzhe/Office-PowerPoint-MCP-Server.git
6
6
  Project-URL: Bug Tracker, https://github.com/GongRzhe/Office-PowerPoint-MCP-Server.git/issues
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "mcpcn-office-powerpoint-mcp-server"
7
- version = "2.0.9"
7
+ version = "2.1.1"
8
8
  description = "MCP Server for PowerPoint manipulation using python-pptx - Consolidated Edition"
9
9
  readme = "README.md"
10
10
  license = {file = "LICENSE"}
@@ -333,6 +333,9 @@ def register_content_tools(app: FastMCP, presentations: Dict, get_current_presen
333
333
 
334
334
  try:
335
335
  if operation == "add":
336
+ # Auto-detect URL even if source_type is not explicitly "url"
337
+ if isinstance(image_source, str) and (image_source.startswith("http://") or image_source.startswith("https://")):
338
+ source_type = "url"
336
339
  # Add new textbox
337
340
  shape = ppt_utils.add_textbox(
338
341
  slide, left, top, width, height, text,
@@ -504,7 +507,7 @@ def register_content_tools(app: FastMCP, presentations: Dict, get_current_presen
504
507
  统一的图片处理工具(添加/增强)。
505
508
 
506
509
  功能
507
- - operation="add":将图片插入到指定幻灯片位置,支持本地文件或 Base64 图片源。
510
+ - operation="add":将图片插入到指定幻灯片位置,支持本地文件或 Base64 图片源或图片地址。
508
511
  - operation="enhance":对已有图片文件进行画质增强与风格化处理,输出增强后的图片路径。
509
512
 
510
513
  参数
@@ -513,9 +516,10 @@ def register_content_tools(app: FastMCP, presentations: Dict, get_current_presen
513
516
  - image_source: str —
514
517
  * 当 source_type="file":本地图片文件路径。
515
518
  * 当 source_type="base64":图片的 Base64 字符串。
516
- - source_type: str — "file" "base64"。
517
- * add 支持 "file" "base64"。
518
- * enhance 仅支持 "file"(不接受 base64)。
519
+ * source_type="url":图片的 http/https 地址。
520
+ - source_type: str "file"、"base64" "url"。
521
+ * add 支持 "file"、"base64"、"url"(仅允许 http/https)。
522
+ * enhance 仅支持 "file"(不接受 base64 或 url)。
519
523
  - left, top: float — 插入位置(英寸)。
520
524
  - width, height: Optional[float] — 插入尺寸(英寸)。可只提供一项以按比例缩放;都不提供则按图片原始尺寸。
521
525
  - enhancement_style: Optional[str] — "presentation" 或 "custom"。当 operation="add" 且需要自动增强时可用;"presentation" 走预设的专业增强流程。
@@ -615,59 +619,63 @@ def register_content_tools(app: FastMCP, presentations: Dict, get_current_presen
615
619
  elif source_type == "url":
616
620
  # Handle image URL (http/https)
617
621
  try:
618
- parsed = urllib.parse.urlparse(image_source)
622
+ # Normalize and percent-encode URL path/query to support spaces and non-ASCII characters
623
+ parsed = urllib.parse.urlsplit(image_source)
619
624
  if parsed.scheme not in ("http", "https"):
620
625
  return {"error": f"Unsupported URL scheme: {parsed.scheme}. Only http/https allowed."}
626
+ encoded_path = urllib.parse.quote(parsed.path or "", safe="/%")
627
+ # Re-encode query preserving keys and multiple values
628
+ qsl = urllib.parse.parse_qsl(parsed.query or "", keep_blank_values=True)
629
+ encoded_query = urllib.parse.urlencode(qsl, doseq=True)
630
+ encoded_url = urllib.parse.urlunsplit((parsed.scheme, parsed.netloc, encoded_path, encoded_query, parsed.fragment))
621
631
 
622
632
  # Download helper using requests if available, else urllib
623
633
  content_type = None
624
634
  temp_path = None
635
+ image_exts = (".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".tif", ".tiff")
625
636
 
626
637
  if requests is not None:
627
- with requests.get(image_source, stream=True) as resp:
638
+ with requests.get(encoded_url, stream=True) as resp:
628
639
  if resp.status_code != 200:
629
640
  return {"error": f"Failed to download image. HTTP {resp.status_code}"}
630
- content_type = resp.headers.get("Content-Type", "")
641
+ content_type = resp.headers.get("Content-Type", "") or ""
631
642
 
632
- if not content_type.startswith("image/"):
633
- return {"error": f"URL content is not an image (Content-Type: {content_type or 'unknown'})"}
634
-
635
- # Determine suffix
643
+ # Determine suffix and allow fallback by URL extension if Content-Type missing or not image/*
636
644
  suffix = ".png"
645
+ is_image = content_type.startswith("image/")
637
646
  try:
638
- main_type = content_type.split(";")[0].strip()
647
+ main_type = (content_type.split(";")[0].strip() if content_type else "")
639
648
  if "/" in main_type:
640
649
  ext = main_type.split("/")[1].lower()
641
- # Basic normalization
642
650
  if ext in ("jpeg", "pjpeg"):
643
651
  suffix = ".jpg"
644
652
  elif ext in ("png", "gif", "bmp", "webp", "tiff"):
645
653
  suffix = f".{ext}"
646
654
  except Exception:
647
655
  pass
648
- # Fallback to URL path extension
649
656
  if suffix == ".png":
650
657
  path_ext = os.path.splitext(parsed.path or "")[1].lower()
651
- if path_ext in (".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".tif", ".tiff"):
658
+ if path_ext in image_exts:
652
659
  suffix = path_ext
653
660
 
661
+ if not is_image and suffix not in image_exts:
662
+ return {"error": f"URL content is not an image (Content-Type: {content_type or 'unknown'})"}
663
+
654
664
  with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as temp_file:
655
665
  temp_path = temp_file.name
656
- total = 0
657
666
  for chunk in resp.iter_content(chunk_size=8192):
658
667
  if not chunk:
659
668
  continue
660
669
  temp_file.write(chunk)
661
670
  else:
662
- req = urllib.request.Request(image_source, headers={"User-Agent": "Mozilla/5.0"})
671
+ req = urllib.request.Request(encoded_url, headers={"User-Agent": "Mozilla/5.0"})
663
672
  with urllib.request.urlopen(req) as resp:
664
- content_type = resp.headers.get("Content-Type", "")
665
- if not content_type.startswith("image/"):
666
- return {"error": f"URL content is not an image (Content-Type: {content_type or 'unknown'})"}
673
+ content_type = resp.headers.get("Content-Type", "") or ""
667
674
 
668
675
  suffix = ".png"
676
+ is_image = content_type.startswith("image/")
669
677
  try:
670
- main_type = content_type.split(";")[0].strip()
678
+ main_type = (content_type.split(";")[0].strip() if content_type else "")
671
679
  if "/" in main_type:
672
680
  ext = main_type.split("/")[1].lower()
673
681
  if ext in ("jpeg", "pjpeg"):
@@ -678,12 +686,14 @@ def register_content_tools(app: FastMCP, presentations: Dict, get_current_presen
678
686
  pass
679
687
  if suffix == ".png":
680
688
  path_ext = os.path.splitext(parsed.path or "")[1].lower()
681
- if path_ext in (".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".tif", ".tiff"):
689
+ if path_ext in image_exts:
682
690
  suffix = path_ext
683
691
 
692
+ if not is_image and suffix not in image_exts:
693
+ return {"error": f"URL content is not an image (Content-Type: {content_type or 'unknown'})"}
694
+
684
695
  with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as temp_file:
685
696
  temp_path = temp_file.name
686
- total = 0
687
697
  while True:
688
698
  chunk = resp.read(8192)
689
699
  if not chunk: