hyperlink_tools.py•5.92 kB
"""
Hyperlink management tools for PowerPoint MCP Server.
Implements hyperlink operations for text shapes and runs.
"""
from typing import Dict, List, Optional, Any
def register_hyperlink_tools(app, presentations, get_current_presentation_id, validate_parameters,
is_positive, is_non_negative, is_in_range, is_valid_rgb):
"""Register hyperlink management tools with the FastMCP app."""
@app.tool()
def manage_hyperlinks(
operation: str,
slide_index: int,
shape_index: int = None,
text: str = None,
url: str = None,
run_index: int = 0,
presentation_id: str = None
) -> Dict:
"""
Manage hyperlinks in text shapes and runs.
Args:
operation: Operation type ("add", "remove", "list", "update")
slide_index: Index of the slide (0-based)
shape_index: Index of the shape on the slide (0-based)
text: Text to make into hyperlink (for "add" operation)
url: URL for the hyperlink
run_index: Index of text run within the shape (0-based)
presentation_id: Optional presentation ID (uses current if not provided)
Returns:
Dictionary with operation results
"""
try:
# Get presentation
pres_id = presentation_id or get_current_presentation_id()
if pres_id not in presentations:
return {"error": "Presentation not found"}
pres = presentations[pres_id]
# Validate slide index
if not (0 <= slide_index < len(pres.slides)):
return {"error": f"Slide index {slide_index} out of range"}
slide = pres.slides[slide_index]
if operation == "list":
# List all hyperlinks in the slide
hyperlinks = []
for shape_idx, shape in enumerate(slide.shapes):
if hasattr(shape, 'text_frame') and shape.text_frame:
for para_idx, paragraph in enumerate(shape.text_frame.paragraphs):
for run_idx, run in enumerate(paragraph.runs):
if run.hyperlink.address:
hyperlinks.append({
"shape_index": shape_idx,
"paragraph_index": para_idx,
"run_index": run_idx,
"text": run.text,
"url": run.hyperlink.address
})
return {
"message": f"Found {len(hyperlinks)} hyperlinks on slide {slide_index}",
"hyperlinks": hyperlinks
}
# For other operations, validate shape index
if shape_index is None or not (0 <= shape_index < len(slide.shapes)):
return {"error": f"Shape index {shape_index} out of range"}
shape = slide.shapes[shape_index]
# Check if shape has text
if not hasattr(shape, 'text_frame') or not shape.text_frame:
return {"error": "Shape does not contain text"}
if operation == "add":
if not text or not url:
return {"error": "Both 'text' and 'url' are required for adding hyperlinks"}
# Add new text run with hyperlink
paragraph = shape.text_frame.paragraphs[0]
run = paragraph.add_run()
run.text = text
run.hyperlink.address = url
return {
"message": f"Added hyperlink '{text}' -> '{url}' to shape {shape_index}",
"text": text,
"url": url
}
elif operation == "update":
if not url:
return {"error": "URL is required for updating hyperlinks"}
# Update existing hyperlink
paragraphs = shape.text_frame.paragraphs
if run_index < len(paragraphs[0].runs):
run = paragraphs[0].runs[run_index]
old_url = run.hyperlink.address
run.hyperlink.address = url
return {
"message": f"Updated hyperlink from '{old_url}' to '{url}'",
"old_url": old_url,
"new_url": url,
"text": run.text
}
else:
return {"error": f"Run index {run_index} out of range"}
elif operation == "remove":
# Remove hyperlink from specific run
paragraphs = shape.text_frame.paragraphs
if run_index < len(paragraphs[0].runs):
run = paragraphs[0].runs[run_index]
old_url = run.hyperlink.address
run.hyperlink.address = None
return {
"message": f"Removed hyperlink '{old_url}' from text '{run.text}'",
"removed_url": old_url,
"text": run.text
}
else:
return {"error": f"Run index {run_index} out of range"}
else:
return {"error": f"Unsupported operation: {operation}. Use 'add', 'remove', 'list', or 'update'"}
except Exception as e:
return {"error": f"Failed to manage hyperlinks: {str(e)}"}