mcpcn-office-powerpoint-mcp-server 2.0.7__py3-none-any.whl
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.
- mcpcn_office_powerpoint_mcp_server-2.0.7.dist-info/METADATA +1023 -0
- mcpcn_office_powerpoint_mcp_server-2.0.7.dist-info/RECORD +25 -0
- mcpcn_office_powerpoint_mcp_server-2.0.7.dist-info/WHEEL +4 -0
- mcpcn_office_powerpoint_mcp_server-2.0.7.dist-info/entry_points.txt +2 -0
- mcpcn_office_powerpoint_mcp_server-2.0.7.dist-info/licenses/LICENSE +21 -0
- ppt_mcp_server.py +450 -0
- slide_layout_templates.json +3690 -0
- tools/__init__.py +28 -0
- tools/chart_tools.py +82 -0
- tools/connector_tools.py +91 -0
- tools/content_tools.py +593 -0
- tools/hyperlink_tools.py +138 -0
- tools/master_tools.py +114 -0
- tools/presentation_tools.py +212 -0
- tools/professional_tools.py +290 -0
- tools/structural_tools.py +373 -0
- tools/template_tools.py +521 -0
- tools/transition_tools.py +75 -0
- utils/__init__.py +69 -0
- utils/content_utils.py +579 -0
- utils/core_utils.py +55 -0
- utils/design_utils.py +689 -0
- utils/presentation_utils.py +217 -0
- utils/template_utils.py +1143 -0
- utils/validation_utils.py +323 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
ppt_mcp_server.py,sha256=IX9oqlVBRpaBIhWJcIwoUQpdAmeKowtBQatHYSEiDU0,14264
|
|
2
|
+
slide_layout_templates.json,sha256=ryA8zIZv6WBwjhBQaJXRWES3uqE7esqqFmXyiPEdauU,107256
|
|
3
|
+
tools/__init__.py,sha256=b53px-U7Ny4-FMnAdFLoJMtPxD7lt7DB8ydvcdATCz0,983
|
|
4
|
+
tools/chart_tools.py,sha256=vvXI53gm9_2mfNOiG-qCfwLdqqr2pe_uDXHN9adVsZk,3124
|
|
5
|
+
tools/connector_tools.py,sha256=8cUPAfTZ-5vvooLCVQIWfblMzcFe3cPdh_bG5UezykQ,3402
|
|
6
|
+
tools/content_tools.py,sha256=uoIg7dJNIJEO_NA5QZfDB7cxGGixN6BTOAaxg8Fnmk4,24996
|
|
7
|
+
tools/hyperlink_tools.py,sha256=EweAxbYGvOpNpzDxH5DnrFzGMQmnzNGGehEoCCx9d18,5920
|
|
8
|
+
tools/master_tools.py,sha256=05a7ZUEDN5a7ySwG97Sj4UJK7Y0pcCxang553Bh7iOM,4986
|
|
9
|
+
tools/presentation_tools.py,sha256=rnVRYOu4A0Sw97sf8qKBK6-q5dYnK1vO2XNAzN8je4o,7977
|
|
10
|
+
tools/professional_tools.py,sha256=1nC45Zx2zV-zbWUwqFhgcejIDyxNdbTfi1kymFi-v88,12109
|
|
11
|
+
tools/structural_tools.py,sha256=0MHdwkpYRj-BqkTF31uavtDoPviNQ4GSQofaQ66WlLg,14444
|
|
12
|
+
tools/template_tools.py,sha256=Iz9TIYF2R42FDPX2usU1xk327shgeVDFt3rgJ1aTch8,23516
|
|
13
|
+
tools/transition_tools.py,sha256=lpWw8dwziNzxvqPcVJsBer2XlkXBAYskkTh6gzwQMRk,3116
|
|
14
|
+
utils/__init__.py,sha256=9GRC7Ms2ZGNlZkZTaFicLO2iaPRcyNkESTaVLV911eg,1698
|
|
15
|
+
utils/content_utils.py,sha256=0nuOSUpx6D84zwbkQIkbAg0esgYtMdikq_WgxuqhoJo,19700
|
|
16
|
+
utils/core_utils.py,sha256=k4sZOdY5XesyrpVDInrBomL07QjuzYP4lnCkxNFwm2A,2113
|
|
17
|
+
utils/design_utils.py,sha256=vEstbqsBe202PYS4NvqdOLL3Aq5na7G2SBJnY-BYD_g,23761
|
|
18
|
+
utils/presentation_utils.py,sha256=9Y4F0twPvr0srjq53_Szkwxcn7dh06IWy3snRLaMuM8,6262
|
|
19
|
+
utils/template_utils.py,sha256=8iSJcJIgoOmi53TtlRnMwRLTDI84EfSW2PWj8EW3Z1k,44574
|
|
20
|
+
utils/validation_utils.py,sha256=0OiwFE1WZh05ZP-0hLzidyxKSUIJaJ3QrV0_s6FpxZk,11775
|
|
21
|
+
mcpcn_office_powerpoint_mcp_server-2.0.7.dist-info/METADATA,sha256=ZCHC_yMRsYUyAJ-5fKfQOEG8kkTcX6DhgbE09dRyKVY,35855
|
|
22
|
+
mcpcn_office_powerpoint_mcp_server-2.0.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
23
|
+
mcpcn_office_powerpoint_mcp_server-2.0.7.dist-info/entry_points.txt,sha256=4iWuiBR6HdlTqSJjCVyqyqVGpSGiD4ssw7LkEzmgvXw,75
|
|
24
|
+
mcpcn_office_powerpoint_mcp_server-2.0.7.dist-info/licenses/LICENSE,sha256=A-aSf9c0K2FKh7xtMkSr5Ot76NX8mrdLffsajnmZGKs,1064
|
|
25
|
+
mcpcn_office_powerpoint_mcp_server-2.0.7.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 GongRzhe
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
ppt_mcp_server.py
ADDED
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""
|
|
3
|
+
MCP Server for PowerPoint manipulation using python-pptx.
|
|
4
|
+
Consolidated version with 20 tools organized into multiple modules.
|
|
5
|
+
"""
|
|
6
|
+
import os
|
|
7
|
+
import argparse
|
|
8
|
+
from typing import Dict, Any
|
|
9
|
+
from mcp.server.fastmcp import FastMCP
|
|
10
|
+
|
|
11
|
+
# import utils # Currently unused
|
|
12
|
+
from tools import (
|
|
13
|
+
register_presentation_tools,
|
|
14
|
+
register_content_tools,
|
|
15
|
+
register_structural_tools,
|
|
16
|
+
register_professional_tools,
|
|
17
|
+
register_template_tools,
|
|
18
|
+
register_hyperlink_tools,
|
|
19
|
+
register_chart_tools,
|
|
20
|
+
register_connector_tools,
|
|
21
|
+
register_master_tools,
|
|
22
|
+
register_transition_tools
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# Initialize the FastMCP server
|
|
26
|
+
app = FastMCP(
|
|
27
|
+
name="ppt-mcp-server"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
# Global state to store presentations in memory
|
|
31
|
+
presentations = {}
|
|
32
|
+
current_presentation_id = None
|
|
33
|
+
|
|
34
|
+
# Template configuration
|
|
35
|
+
def get_template_search_directories():
|
|
36
|
+
"""
|
|
37
|
+
Get list of directories to search for templates.
|
|
38
|
+
Uses environment variable PPT_TEMPLATE_PATH if set, otherwise uses default directories.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
List of directories to search for templates
|
|
42
|
+
"""
|
|
43
|
+
template_env_path = os.environ.get('PPT_TEMPLATE_PATH')
|
|
44
|
+
|
|
45
|
+
if template_env_path:
|
|
46
|
+
# If environment variable is set, use it as the primary template directory
|
|
47
|
+
# Support multiple paths separated by colon (Unix) or semicolon (Windows)
|
|
48
|
+
import platform
|
|
49
|
+
separator = ';' if platform.system() == "Windows" else ':'
|
|
50
|
+
env_dirs = [path.strip() for path in template_env_path.split(separator) if path.strip()]
|
|
51
|
+
|
|
52
|
+
# Verify that the directories exist
|
|
53
|
+
valid_env_dirs = []
|
|
54
|
+
for dir_path in env_dirs:
|
|
55
|
+
expanded_path = os.path.expanduser(dir_path)
|
|
56
|
+
if os.path.exists(expanded_path) and os.path.isdir(expanded_path):
|
|
57
|
+
valid_env_dirs.append(expanded_path)
|
|
58
|
+
|
|
59
|
+
if valid_env_dirs:
|
|
60
|
+
# Add default fallback directories
|
|
61
|
+
return valid_env_dirs + ['.', './templates', './assets', './resources']
|
|
62
|
+
else:
|
|
63
|
+
print(f"Warning: PPT_TEMPLATE_PATH directories not found: {template_env_path}")
|
|
64
|
+
|
|
65
|
+
# Default search directories when no environment variable or invalid paths
|
|
66
|
+
return ['.', './templates', './assets', './resources']
|
|
67
|
+
|
|
68
|
+
# ---- Helper Functions ----
|
|
69
|
+
|
|
70
|
+
def get_current_presentation():
|
|
71
|
+
"""Get the current presentation object or raise an error if none is loaded."""
|
|
72
|
+
if current_presentation_id is None or current_presentation_id not in presentations:
|
|
73
|
+
raise ValueError("No presentation is currently loaded. Please create or open a presentation first.")
|
|
74
|
+
return presentations[current_presentation_id]
|
|
75
|
+
|
|
76
|
+
def get_current_presentation_id():
|
|
77
|
+
"""Get the current presentation ID."""
|
|
78
|
+
return current_presentation_id
|
|
79
|
+
|
|
80
|
+
def set_current_presentation_id(pres_id):
|
|
81
|
+
"""Set the current presentation ID."""
|
|
82
|
+
global current_presentation_id
|
|
83
|
+
current_presentation_id = pres_id
|
|
84
|
+
|
|
85
|
+
def validate_parameters(params):
|
|
86
|
+
"""
|
|
87
|
+
Validate parameters against constraints.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
params: Dictionary of parameter name: (value, constraints) pairs
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
(True, None) if all valid, or (False, error_message) if invalid
|
|
94
|
+
"""
|
|
95
|
+
for param_name, (value, constraints) in params.items():
|
|
96
|
+
for constraint_func, error_msg in constraints:
|
|
97
|
+
if not constraint_func(value):
|
|
98
|
+
return False, f"Parameter '{param_name}': {error_msg}"
|
|
99
|
+
return True, None
|
|
100
|
+
|
|
101
|
+
def is_positive(value):
|
|
102
|
+
"""Check if a value is positive."""
|
|
103
|
+
return value > 0
|
|
104
|
+
|
|
105
|
+
def is_non_negative(value):
|
|
106
|
+
"""Check if a value is non-negative."""
|
|
107
|
+
return value >= 0
|
|
108
|
+
|
|
109
|
+
def is_in_range(min_val, max_val):
|
|
110
|
+
"""Create a function that checks if a value is in a range."""
|
|
111
|
+
return lambda x: min_val <= x <= max_val
|
|
112
|
+
|
|
113
|
+
def is_in_list(valid_list):
|
|
114
|
+
"""Create a function that checks if a value is in a list."""
|
|
115
|
+
return lambda x: x in valid_list
|
|
116
|
+
|
|
117
|
+
def is_valid_rgb(color_list):
|
|
118
|
+
"""Check if a color list is a valid RGB tuple."""
|
|
119
|
+
if not isinstance(color_list, list) or len(color_list) != 3:
|
|
120
|
+
return False
|
|
121
|
+
return all(isinstance(c, int) and 0 <= c <= 255 for c in color_list)
|
|
122
|
+
|
|
123
|
+
def add_shape_direct(slide, shape_type: str, left: float, top: float, width: float, height: float) -> Any:
|
|
124
|
+
"""
|
|
125
|
+
Add an auto shape to a slide using direct integer values instead of enum objects.
|
|
126
|
+
|
|
127
|
+
This implementation provides a reliable alternative that bypasses potential
|
|
128
|
+
enum-related issues in the python-pptx library.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
slide: The slide object
|
|
132
|
+
shape_type: Shape type string (e.g., 'rectangle', 'oval', 'triangle')
|
|
133
|
+
left: Left position in inches
|
|
134
|
+
top: Top position in inches
|
|
135
|
+
width: Width in inches
|
|
136
|
+
height: Height in inches
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
The created shape
|
|
140
|
+
"""
|
|
141
|
+
from pptx.util import Inches
|
|
142
|
+
|
|
143
|
+
# Direct mapping of shape types to their integer values
|
|
144
|
+
# These values are directly from the MS Office VBA documentation
|
|
145
|
+
shape_type_map = {
|
|
146
|
+
'rectangle': 1,
|
|
147
|
+
'rounded_rectangle': 2,
|
|
148
|
+
'oval': 9,
|
|
149
|
+
'diamond': 4,
|
|
150
|
+
'triangle': 5, # This is ISOSCELES_TRIANGLE
|
|
151
|
+
'right_triangle': 6,
|
|
152
|
+
'pentagon': 56,
|
|
153
|
+
'hexagon': 10,
|
|
154
|
+
'heptagon': 11,
|
|
155
|
+
'octagon': 12,
|
|
156
|
+
'star': 12, # This is STAR_5_POINTS (value 12)
|
|
157
|
+
'arrow': 13,
|
|
158
|
+
'cloud': 35,
|
|
159
|
+
'heart': 21,
|
|
160
|
+
'lightning_bolt': 22,
|
|
161
|
+
'sun': 23,
|
|
162
|
+
'moon': 24,
|
|
163
|
+
'smiley_face': 17,
|
|
164
|
+
'no_symbol': 19,
|
|
165
|
+
'flowchart_process': 112,
|
|
166
|
+
'flowchart_decision': 114,
|
|
167
|
+
'flowchart_data': 115,
|
|
168
|
+
'flowchart_document': 119
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
# Check if shape type is valid before trying to use it
|
|
172
|
+
shape_type_lower = str(shape_type).lower()
|
|
173
|
+
if shape_type_lower not in shape_type_map:
|
|
174
|
+
available_shapes = ', '.join(sorted(shape_type_map.keys()))
|
|
175
|
+
raise ValueError(f"Unsupported shape type: '{shape_type}'. Available shape types: {available_shapes}")
|
|
176
|
+
|
|
177
|
+
# Get the integer value for the shape type
|
|
178
|
+
shape_value = shape_type_map[shape_type_lower]
|
|
179
|
+
|
|
180
|
+
# Create the shape using the direct integer value
|
|
181
|
+
try:
|
|
182
|
+
# The integer value is passed directly to add_shape
|
|
183
|
+
shape = slide.shapes.add_shape(
|
|
184
|
+
shape_value, Inches(left), Inches(top), Inches(width), Inches(height)
|
|
185
|
+
)
|
|
186
|
+
return shape
|
|
187
|
+
except Exception as e:
|
|
188
|
+
raise ValueError(f"Failed to create '{shape_type}' shape using direct value {shape_value}: {str(e)}")
|
|
189
|
+
|
|
190
|
+
# ---- Custom presentation management wrapper ----
|
|
191
|
+
|
|
192
|
+
class PresentationManager:
|
|
193
|
+
"""Wrapper to handle presentation state updates."""
|
|
194
|
+
|
|
195
|
+
def __init__(self, presentations_dict):
|
|
196
|
+
self.presentations = presentations_dict
|
|
197
|
+
|
|
198
|
+
def store_presentation(self, pres, pres_id):
|
|
199
|
+
"""Store a presentation and set it as current."""
|
|
200
|
+
self.presentations[pres_id] = pres
|
|
201
|
+
set_current_presentation_id(pres_id)
|
|
202
|
+
return pres_id
|
|
203
|
+
|
|
204
|
+
# ---- Register Tools ----
|
|
205
|
+
|
|
206
|
+
# Create presentation manager wrapper
|
|
207
|
+
presentation_manager = PresentationManager(presentations)
|
|
208
|
+
|
|
209
|
+
# Wrapper functions to handle state management
|
|
210
|
+
def create_presentation_wrapper(original_func):
|
|
211
|
+
"""Wrapper to handle presentation creation with state management."""
|
|
212
|
+
def wrapper(*args, **kwargs):
|
|
213
|
+
result = original_func(*args, **kwargs)
|
|
214
|
+
if "presentation_id" in result and result["presentation_id"] in presentations:
|
|
215
|
+
set_current_presentation_id(result["presentation_id"])
|
|
216
|
+
return result
|
|
217
|
+
return wrapper
|
|
218
|
+
|
|
219
|
+
def open_presentation_wrapper(original_func):
|
|
220
|
+
"""Wrapper to handle presentation opening with state management."""
|
|
221
|
+
def wrapper(*args, **kwargs):
|
|
222
|
+
result = original_func(*args, **kwargs)
|
|
223
|
+
if "presentation_id" in result and result["presentation_id"] in presentations:
|
|
224
|
+
set_current_presentation_id(result["presentation_id"])
|
|
225
|
+
return result
|
|
226
|
+
return wrapper
|
|
227
|
+
|
|
228
|
+
# Register all tool modules
|
|
229
|
+
register_presentation_tools(
|
|
230
|
+
app,
|
|
231
|
+
presentations,
|
|
232
|
+
get_current_presentation_id,
|
|
233
|
+
get_template_search_directories
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
register_content_tools(
|
|
237
|
+
app,
|
|
238
|
+
presentations,
|
|
239
|
+
get_current_presentation_id,
|
|
240
|
+
validate_parameters,
|
|
241
|
+
is_positive,
|
|
242
|
+
is_non_negative,
|
|
243
|
+
is_in_range,
|
|
244
|
+
is_valid_rgb
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
register_structural_tools(
|
|
248
|
+
app,
|
|
249
|
+
presentations,
|
|
250
|
+
get_current_presentation_id,
|
|
251
|
+
validate_parameters,
|
|
252
|
+
is_positive,
|
|
253
|
+
is_non_negative,
|
|
254
|
+
is_in_range,
|
|
255
|
+
is_valid_rgb,
|
|
256
|
+
add_shape_direct
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
register_professional_tools(
|
|
260
|
+
app,
|
|
261
|
+
presentations,
|
|
262
|
+
get_current_presentation_id
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
register_template_tools(
|
|
266
|
+
app,
|
|
267
|
+
presentations,
|
|
268
|
+
get_current_presentation_id
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
register_hyperlink_tools(
|
|
272
|
+
app,
|
|
273
|
+
presentations,
|
|
274
|
+
get_current_presentation_id,
|
|
275
|
+
validate_parameters,
|
|
276
|
+
is_positive,
|
|
277
|
+
is_non_negative,
|
|
278
|
+
is_in_range,
|
|
279
|
+
is_valid_rgb
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
register_chart_tools(
|
|
283
|
+
app,
|
|
284
|
+
presentations,
|
|
285
|
+
get_current_presentation_id,
|
|
286
|
+
validate_parameters,
|
|
287
|
+
is_positive,
|
|
288
|
+
is_non_negative,
|
|
289
|
+
is_in_range,
|
|
290
|
+
is_valid_rgb
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
register_connector_tools(
|
|
295
|
+
app,
|
|
296
|
+
presentations,
|
|
297
|
+
get_current_presentation_id,
|
|
298
|
+
validate_parameters,
|
|
299
|
+
is_positive,
|
|
300
|
+
is_non_negative,
|
|
301
|
+
is_in_range,
|
|
302
|
+
is_valid_rgb
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
register_master_tools(
|
|
306
|
+
app,
|
|
307
|
+
presentations,
|
|
308
|
+
get_current_presentation_id,
|
|
309
|
+
validate_parameters,
|
|
310
|
+
is_positive,
|
|
311
|
+
is_non_negative,
|
|
312
|
+
is_in_range,
|
|
313
|
+
is_valid_rgb
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
register_transition_tools(
|
|
317
|
+
app,
|
|
318
|
+
presentations,
|
|
319
|
+
get_current_presentation_id,
|
|
320
|
+
validate_parameters,
|
|
321
|
+
is_positive,
|
|
322
|
+
is_non_negative,
|
|
323
|
+
is_in_range,
|
|
324
|
+
is_valid_rgb
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
# ---- Additional Utility Tools ----
|
|
329
|
+
|
|
330
|
+
@app.tool()
|
|
331
|
+
def list_presentations() -> Dict:
|
|
332
|
+
"""List all loaded presentations."""
|
|
333
|
+
return {
|
|
334
|
+
"presentations": [
|
|
335
|
+
{
|
|
336
|
+
"id": pres_id,
|
|
337
|
+
"slide_count": len(pres.slides),
|
|
338
|
+
"is_current": pres_id == current_presentation_id
|
|
339
|
+
}
|
|
340
|
+
for pres_id, pres in presentations.items()
|
|
341
|
+
],
|
|
342
|
+
"current_presentation_id": current_presentation_id,
|
|
343
|
+
"total_presentations": len(presentations)
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
@app.tool()
|
|
347
|
+
def switch_presentation(presentation_id: str) -> Dict:
|
|
348
|
+
"""Switch to a different loaded presentation."""
|
|
349
|
+
if presentation_id not in presentations:
|
|
350
|
+
return {
|
|
351
|
+
"error": f"Presentation '{presentation_id}' not found. Available presentations: {list(presentations.keys())}"
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
global current_presentation_id
|
|
355
|
+
old_id = current_presentation_id
|
|
356
|
+
current_presentation_id = presentation_id
|
|
357
|
+
|
|
358
|
+
return {
|
|
359
|
+
"message": f"Switched from presentation '{old_id}' to '{presentation_id}'",
|
|
360
|
+
"previous_presentation_id": old_id,
|
|
361
|
+
"current_presentation_id": current_presentation_id
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
@app.tool()
|
|
365
|
+
def get_server_info() -> Dict:
|
|
366
|
+
"""Get information about the MCP server."""
|
|
367
|
+
return {
|
|
368
|
+
"name": "PowerPoint MCP Server - Enhanced Edition",
|
|
369
|
+
"version": "2.1.0",
|
|
370
|
+
"total_tools": 32, # Organized into 11 specialized modules
|
|
371
|
+
"loaded_presentations": len(presentations),
|
|
372
|
+
"current_presentation": current_presentation_id,
|
|
373
|
+
"features": [
|
|
374
|
+
"Presentation Management (7 tools)",
|
|
375
|
+
"Content Management (6 tools)",
|
|
376
|
+
"Template Operations (7 tools)",
|
|
377
|
+
"Structural Elements (4 tools)",
|
|
378
|
+
"Professional Design (3 tools)",
|
|
379
|
+
"Specialized Features (5 tools)"
|
|
380
|
+
],
|
|
381
|
+
"improvements": [
|
|
382
|
+
"32 specialized tools organized into 11 focused modules",
|
|
383
|
+
"68+ utility functions across 7 organized utility modules",
|
|
384
|
+
"Enhanced parameter handling and validation",
|
|
385
|
+
"Unified operation interfaces with comprehensive coverage",
|
|
386
|
+
"Advanced template system with auto-generation capabilities",
|
|
387
|
+
"Professional design tools with multiple effects and styling",
|
|
388
|
+
"Specialized features including hyperlinks, connectors, slide masters",
|
|
389
|
+
"Dynamic text sizing and intelligent wrapping",
|
|
390
|
+
"Advanced visual effects and styling",
|
|
391
|
+
"Content-aware optimization and validation",
|
|
392
|
+
"Complete PowerPoint lifecycle management",
|
|
393
|
+
"Modular architecture for better maintainability"
|
|
394
|
+
],
|
|
395
|
+
"new_enhanced_features": [
|
|
396
|
+
"Hyperlink Management - Add, update, remove, and list hyperlinks in text",
|
|
397
|
+
"Advanced Chart Data Updates - Replace chart data with new categories and series",
|
|
398
|
+
"Advanced Text Run Formatting - Apply formatting to specific text runs",
|
|
399
|
+
"Shape Connectors - Add connector lines and arrows between points",
|
|
400
|
+
"Slide Master Management - Access and manage slide masters and layouts",
|
|
401
|
+
"Slide Transitions - Basic transition management (placeholder for future)"
|
|
402
|
+
]
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
# ---- Main Function ----
|
|
406
|
+
def main(transport: str = "stdio", port: int = 8000):
|
|
407
|
+
if transport == "http":
|
|
408
|
+
import asyncio
|
|
409
|
+
# Set the port for HTTP transport
|
|
410
|
+
app.settings.port = port
|
|
411
|
+
# Start the FastMCP server with HTTP transport
|
|
412
|
+
try:
|
|
413
|
+
app.run(transport='streamable-http')
|
|
414
|
+
except asyncio.exceptions.CancelledError:
|
|
415
|
+
print("Server stopped by user.")
|
|
416
|
+
except KeyboardInterrupt:
|
|
417
|
+
print("Server stopped by user.")
|
|
418
|
+
except Exception as e:
|
|
419
|
+
print(f"Error starting server: {e}")
|
|
420
|
+
|
|
421
|
+
elif transport == "sse":
|
|
422
|
+
# Run the FastMCP server in SSE (Server Side Events) mode
|
|
423
|
+
app.run(transport='sse')
|
|
424
|
+
|
|
425
|
+
else:
|
|
426
|
+
# Run the FastMCP server
|
|
427
|
+
app.run(transport='stdio')
|
|
428
|
+
|
|
429
|
+
if __name__ == "__main__":
|
|
430
|
+
# Parse command line arguments
|
|
431
|
+
parser = argparse.ArgumentParser(description="MCP Server for PowerPoint manipulation using python-pptx")
|
|
432
|
+
|
|
433
|
+
parser.add_argument(
|
|
434
|
+
"-t",
|
|
435
|
+
"--transport",
|
|
436
|
+
type=str,
|
|
437
|
+
default="stdio",
|
|
438
|
+
choices=["stdio", "http", "sse"],
|
|
439
|
+
help="Transport method for the MCP server (default: stdio)"
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
parser.add_argument(
|
|
443
|
+
"-p",
|
|
444
|
+
"--port",
|
|
445
|
+
type=int,
|
|
446
|
+
default=8000,
|
|
447
|
+
help="Port to run the MCP server on (default: 8000)"
|
|
448
|
+
)
|
|
449
|
+
args = parser.parse_args()
|
|
450
|
+
main(args.transport, args.port)
|