google-workspace-mcp 1.0.0__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.
- google_workspace_mcp/__init__.py +3 -0
- google_workspace_mcp/__main__.py +43 -0
- google_workspace_mcp/app.py +8 -0
- google_workspace_mcp/auth/__init__.py +7 -0
- google_workspace_mcp/auth/gauth.py +62 -0
- google_workspace_mcp/config.py +60 -0
- google_workspace_mcp/prompts/__init__.py +3 -0
- google_workspace_mcp/prompts/calendar.py +36 -0
- google_workspace_mcp/prompts/drive.py +18 -0
- google_workspace_mcp/prompts/gmail.py +65 -0
- google_workspace_mcp/prompts/slides.py +40 -0
- google_workspace_mcp/resources/__init__.py +13 -0
- google_workspace_mcp/resources/calendar.py +79 -0
- google_workspace_mcp/resources/drive.py +93 -0
- google_workspace_mcp/resources/gmail.py +58 -0
- google_workspace_mcp/resources/sheets_resources.py +92 -0
- google_workspace_mcp/resources/slides.py +421 -0
- google_workspace_mcp/services/__init__.py +21 -0
- google_workspace_mcp/services/base.py +73 -0
- google_workspace_mcp/services/calendar.py +256 -0
- google_workspace_mcp/services/docs_service.py +388 -0
- google_workspace_mcp/services/drive.py +454 -0
- google_workspace_mcp/services/gmail.py +676 -0
- google_workspace_mcp/services/sheets_service.py +466 -0
- google_workspace_mcp/services/slides.py +959 -0
- google_workspace_mcp/tools/__init__.py +7 -0
- google_workspace_mcp/tools/calendar.py +229 -0
- google_workspace_mcp/tools/docs_tools.py +277 -0
- google_workspace_mcp/tools/drive.py +221 -0
- google_workspace_mcp/tools/gmail.py +344 -0
- google_workspace_mcp/tools/sheets_tools.py +322 -0
- google_workspace_mcp/tools/slides.py +478 -0
- google_workspace_mcp/utils/__init__.py +1 -0
- google_workspace_mcp/utils/markdown_slides.py +504 -0
- google_workspace_mcp-1.0.0.dist-info/METADATA +547 -0
- google_workspace_mcp-1.0.0.dist-info/RECORD +38 -0
- google_workspace_mcp-1.0.0.dist-info/WHEEL +4 -0
- google_workspace_mcp-1.0.0.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,478 @@
|
|
1
|
+
"""
|
2
|
+
Slides tools for Google Slides operations.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import logging
|
6
|
+
from typing import Any
|
7
|
+
|
8
|
+
from google_workspace_mcp.app import mcp # Import from central app module
|
9
|
+
from google_workspace_mcp.services.slides import SlidesService
|
10
|
+
|
11
|
+
logger = logging.getLogger(__name__)
|
12
|
+
|
13
|
+
|
14
|
+
# --- Slides Tool Functions --- #
|
15
|
+
|
16
|
+
|
17
|
+
@mcp.tool(
|
18
|
+
name="get_presentation",
|
19
|
+
description="Get a presentation by ID with its metadata and content.",
|
20
|
+
)
|
21
|
+
async def get_presentation(presentation_id: str) -> dict[str, Any]:
|
22
|
+
"""
|
23
|
+
Get presentation information including all slides and content.
|
24
|
+
|
25
|
+
Args:
|
26
|
+
presentation_id: The ID of the presentation.
|
27
|
+
|
28
|
+
Returns:
|
29
|
+
Presentation data dictionary or raises error.
|
30
|
+
"""
|
31
|
+
logger.info(f"Executing get_presentation tool with ID: '{presentation_id}'")
|
32
|
+
if not presentation_id or not presentation_id.strip():
|
33
|
+
raise ValueError("Presentation ID is required")
|
34
|
+
|
35
|
+
slides_service = SlidesService()
|
36
|
+
result = slides_service.get_presentation(presentation_id=presentation_id)
|
37
|
+
|
38
|
+
if isinstance(result, dict) and result.get("error"):
|
39
|
+
raise ValueError(result.get("message", "Error getting presentation"))
|
40
|
+
|
41
|
+
# Return raw service result
|
42
|
+
return result
|
43
|
+
|
44
|
+
|
45
|
+
@mcp.tool(
|
46
|
+
name="get_slides",
|
47
|
+
description="Retrieves all slides from a presentation with their elements and notes.",
|
48
|
+
)
|
49
|
+
async def get_slides(presentation_id: str) -> dict[str, Any]:
|
50
|
+
"""
|
51
|
+
Retrieves all slides from a presentation.
|
52
|
+
|
53
|
+
Args:
|
54
|
+
presentation_id: The ID of the presentation.
|
55
|
+
|
56
|
+
Returns:
|
57
|
+
A dictionary containing the list of slides or an error message.
|
58
|
+
"""
|
59
|
+
logger.info(f"Executing get_slides tool from presentation: '{presentation_id}'")
|
60
|
+
if not presentation_id or not presentation_id.strip():
|
61
|
+
raise ValueError("Presentation ID is required")
|
62
|
+
|
63
|
+
slides_service = SlidesService()
|
64
|
+
slides = slides_service.get_slides(presentation_id=presentation_id)
|
65
|
+
|
66
|
+
if isinstance(slides, dict) and slides.get("error"):
|
67
|
+
raise ValueError(slides.get("message", "Error getting slides"))
|
68
|
+
|
69
|
+
if not slides:
|
70
|
+
return {"message": "No slides found in this presentation."}
|
71
|
+
|
72
|
+
# Return raw service result
|
73
|
+
return {"count": len(slides), "slides": slides}
|
74
|
+
|
75
|
+
|
76
|
+
@mcp.tool(
|
77
|
+
name="create_presentation",
|
78
|
+
description="Creates a new Google Slides presentation with the specified title.",
|
79
|
+
)
|
80
|
+
async def create_presentation(
|
81
|
+
title: str,
|
82
|
+
) -> dict[str, Any]:
|
83
|
+
"""
|
84
|
+
Create a new presentation.
|
85
|
+
|
86
|
+
Args:
|
87
|
+
title: The title for the new presentation.
|
88
|
+
|
89
|
+
Returns:
|
90
|
+
Created presentation data or raises error.
|
91
|
+
"""
|
92
|
+
logger.info(f"Executing create_presentation with title: '{title}'")
|
93
|
+
if not title or not title.strip():
|
94
|
+
raise ValueError("Presentation title cannot be empty")
|
95
|
+
|
96
|
+
slides_service = SlidesService()
|
97
|
+
result = slides_service.create_presentation(title=title)
|
98
|
+
|
99
|
+
if isinstance(result, dict) and result.get("error"):
|
100
|
+
raise ValueError(result.get("message", "Error creating presentation"))
|
101
|
+
|
102
|
+
return result
|
103
|
+
|
104
|
+
|
105
|
+
@mcp.tool(
|
106
|
+
name="create_slide",
|
107
|
+
description="Adds a new slide to a Google Slides presentation with a specified layout.",
|
108
|
+
)
|
109
|
+
async def create_slide(
|
110
|
+
presentation_id: str,
|
111
|
+
layout: str = "TITLE_AND_BODY",
|
112
|
+
) -> dict[str, Any]:
|
113
|
+
"""
|
114
|
+
Add a new slide to a presentation.
|
115
|
+
|
116
|
+
Args:
|
117
|
+
presentation_id: The ID of the presentation.
|
118
|
+
layout: The layout for the new slide (e.g., TITLE_AND_BODY, TITLE_ONLY, BLANK).
|
119
|
+
|
120
|
+
Returns:
|
121
|
+
Response data confirming slide creation or raises error.
|
122
|
+
"""
|
123
|
+
logger.info(f"Executing create_slide in presentation '{presentation_id}' with layout '{layout}'")
|
124
|
+
if not presentation_id or not presentation_id.strip():
|
125
|
+
raise ValueError("Presentation ID cannot be empty")
|
126
|
+
# Optional: Validate layout against known predefined layouts?
|
127
|
+
|
128
|
+
slides_service = SlidesService()
|
129
|
+
result = slides_service.create_slide(presentation_id=presentation_id, layout=layout)
|
130
|
+
|
131
|
+
if isinstance(result, dict) and result.get("error"):
|
132
|
+
raise ValueError(result.get("message", "Error creating slide"))
|
133
|
+
|
134
|
+
return result
|
135
|
+
|
136
|
+
|
137
|
+
@mcp.tool(
|
138
|
+
name="add_text_to_slide",
|
139
|
+
description="Adds text to a specified slide in a Google Slides presentation.",
|
140
|
+
)
|
141
|
+
async def add_text_to_slide(
|
142
|
+
presentation_id: str,
|
143
|
+
slide_id: str,
|
144
|
+
text: str,
|
145
|
+
shape_type: str = "TEXT_BOX",
|
146
|
+
position_x: float = 100.0,
|
147
|
+
position_y: float = 100.0,
|
148
|
+
size_width: float = 400.0,
|
149
|
+
size_height: float = 100.0,
|
150
|
+
) -> dict[str, Any]:
|
151
|
+
"""
|
152
|
+
Add text to a slide by creating a text box.
|
153
|
+
|
154
|
+
Args:
|
155
|
+
presentation_id: The ID of the presentation.
|
156
|
+
slide_id: The ID of the slide.
|
157
|
+
text: The text content to add.
|
158
|
+
shape_type: Type of shape (default TEXT_BOX). Must be 'TEXT_BOX'.
|
159
|
+
position_x: X coordinate for position (default 100.0 PT).
|
160
|
+
position_y: Y coordinate for position (default 100.0 PT).
|
161
|
+
size_width: Width of the text box (default 400.0 PT).
|
162
|
+
size_height: Height of the text box (default 100.0 PT).
|
163
|
+
|
164
|
+
Returns:
|
165
|
+
Response data confirming text addition or raises error.
|
166
|
+
"""
|
167
|
+
logger.info(f"Executing add_text_to_slide on slide '{slide_id}'")
|
168
|
+
if not presentation_id or not slide_id or text is None:
|
169
|
+
raise ValueError("Presentation ID, Slide ID, and Text are required")
|
170
|
+
|
171
|
+
# Validate shape_type
|
172
|
+
valid_shape_types = {"TEXT_BOX"}
|
173
|
+
if shape_type not in valid_shape_types:
|
174
|
+
raise ValueError(f"Invalid shape_type '{shape_type}' provided. Must be one of {valid_shape_types}.")
|
175
|
+
|
176
|
+
slides_service = SlidesService()
|
177
|
+
result = slides_service.add_text(
|
178
|
+
presentation_id=presentation_id,
|
179
|
+
slide_id=slide_id,
|
180
|
+
text=text,
|
181
|
+
shape_type=shape_type,
|
182
|
+
position=(position_x, position_y),
|
183
|
+
size=(size_width, size_height),
|
184
|
+
)
|
185
|
+
|
186
|
+
if isinstance(result, dict) and result.get("error"):
|
187
|
+
raise ValueError(result.get("message", "Error adding text to slide"))
|
188
|
+
|
189
|
+
return result
|
190
|
+
|
191
|
+
|
192
|
+
@mcp.tool(
|
193
|
+
name="add_formatted_text_to_slide",
|
194
|
+
description="Adds rich-formatted text (with bold, italic, etc.) to a slide.",
|
195
|
+
)
|
196
|
+
async def add_formatted_text_to_slide(
|
197
|
+
presentation_id: str,
|
198
|
+
slide_id: str,
|
199
|
+
text: str,
|
200
|
+
position_x: float = 100.0,
|
201
|
+
position_y: float = 100.0,
|
202
|
+
size_width: float = 400.0,
|
203
|
+
size_height: float = 100.0,
|
204
|
+
) -> dict[str, Any]:
|
205
|
+
"""
|
206
|
+
Add formatted text to a slide with markdown-style formatting.
|
207
|
+
|
208
|
+
Args:
|
209
|
+
presentation_id: The ID of the presentation.
|
210
|
+
slide_id: The ID of the slide.
|
211
|
+
text: The text content with formatting (use ** for bold, * for italic).
|
212
|
+
position_x: X coordinate for position (default 100.0 PT).
|
213
|
+
position_y: Y coordinate for position (default 100.0 PT).
|
214
|
+
size_width: Width of the text box (default 400.0 PT).
|
215
|
+
size_height: Height of the text box (default 100.0 PT).
|
216
|
+
|
217
|
+
Returns:
|
218
|
+
Response data confirming text addition or raises error.
|
219
|
+
"""
|
220
|
+
logger.info(f"Executing add_formatted_text_to_slide on slide '{slide_id}'")
|
221
|
+
if not presentation_id or not slide_id or text is None:
|
222
|
+
raise ValueError("Presentation ID, Slide ID, and Text are required")
|
223
|
+
|
224
|
+
slides_service = SlidesService()
|
225
|
+
result = slides_service.add_formatted_text(
|
226
|
+
presentation_id=presentation_id,
|
227
|
+
slide_id=slide_id,
|
228
|
+
formatted_text=text,
|
229
|
+
position=(position_x, position_y),
|
230
|
+
size=(size_width, size_height),
|
231
|
+
)
|
232
|
+
|
233
|
+
if isinstance(result, dict) and result.get("error"):
|
234
|
+
raise ValueError(result.get("message", "Error adding formatted text to slide"))
|
235
|
+
|
236
|
+
return result
|
237
|
+
|
238
|
+
|
239
|
+
@mcp.tool(
|
240
|
+
name="add_bulleted_list_to_slide",
|
241
|
+
description="Adds a bulleted list to a slide in a Google Slides presentation.",
|
242
|
+
)
|
243
|
+
async def add_bulleted_list_to_slide(
|
244
|
+
presentation_id: str,
|
245
|
+
slide_id: str,
|
246
|
+
items: list[str],
|
247
|
+
position_x: float = 100.0,
|
248
|
+
position_y: float = 100.0,
|
249
|
+
size_width: float = 400.0,
|
250
|
+
size_height: float = 200.0,
|
251
|
+
) -> dict[str, Any]:
|
252
|
+
"""
|
253
|
+
Add a bulleted list to a slide.
|
254
|
+
|
255
|
+
Args:
|
256
|
+
presentation_id: The ID of the presentation.
|
257
|
+
slide_id: The ID of the slide.
|
258
|
+
items: List of bullet point text items.
|
259
|
+
position_x: X coordinate for position (default 100.0 PT).
|
260
|
+
position_y: Y coordinate for position (default 100.0 PT).
|
261
|
+
size_width: Width of the text box (default 400.0 PT).
|
262
|
+
size_height: Height of the text box (default 200.0 PT).
|
263
|
+
|
264
|
+
Returns:
|
265
|
+
Response data confirming list addition or raises error.
|
266
|
+
"""
|
267
|
+
logger.info(f"Executing add_bulleted_list_to_slide on slide '{slide_id}'")
|
268
|
+
if not presentation_id or not slide_id or not items:
|
269
|
+
raise ValueError("Presentation ID, Slide ID, and Items are required")
|
270
|
+
|
271
|
+
slides_service = SlidesService()
|
272
|
+
result = slides_service.add_bulleted_list(
|
273
|
+
presentation_id=presentation_id,
|
274
|
+
slide_id=slide_id,
|
275
|
+
items=items,
|
276
|
+
position=(position_x, position_y),
|
277
|
+
size=(size_width, size_height),
|
278
|
+
)
|
279
|
+
|
280
|
+
if isinstance(result, dict) and result.get("error"):
|
281
|
+
raise ValueError(result.get("message", "Error adding bulleted list to slide"))
|
282
|
+
|
283
|
+
return result
|
284
|
+
|
285
|
+
|
286
|
+
@mcp.tool(
|
287
|
+
name="add_table_to_slide",
|
288
|
+
description="Adds a table to a slide in a Google Slides presentation.",
|
289
|
+
)
|
290
|
+
async def add_table_to_slide(
|
291
|
+
presentation_id: str,
|
292
|
+
slide_id: str,
|
293
|
+
rows: int,
|
294
|
+
columns: int,
|
295
|
+
data: list[list[str]],
|
296
|
+
position_x: float = 100.0,
|
297
|
+
position_y: float = 100.0,
|
298
|
+
size_width: float = 400.0,
|
299
|
+
size_height: float = 200.0,
|
300
|
+
) -> dict[str, Any]:
|
301
|
+
"""
|
302
|
+
Add a table to a slide.
|
303
|
+
|
304
|
+
Args:
|
305
|
+
presentation_id: The ID of the presentation.
|
306
|
+
slide_id: The ID of the slide.
|
307
|
+
rows: Number of rows in the table.
|
308
|
+
columns: Number of columns in the table.
|
309
|
+
data: 2D array of strings containing table data.
|
310
|
+
position_x: X coordinate for position (default 100.0 PT).
|
311
|
+
position_y: Y coordinate for position (default 100.0 PT).
|
312
|
+
size_width: Width of the table (default 400.0 PT).
|
313
|
+
size_height: Height of the table (default 200.0 PT).
|
314
|
+
|
315
|
+
Returns:
|
316
|
+
Response data confirming table addition or raises error.
|
317
|
+
"""
|
318
|
+
logger.info(f"Executing add_table_to_slide on slide '{slide_id}'")
|
319
|
+
if not presentation_id or not slide_id:
|
320
|
+
raise ValueError("Presentation ID and Slide ID are required")
|
321
|
+
|
322
|
+
if rows < 1 or columns < 1:
|
323
|
+
raise ValueError("Rows and columns must be positive integers")
|
324
|
+
|
325
|
+
if len(data) > rows or any(len(row) > columns for row in data):
|
326
|
+
raise ValueError("Data dimensions exceed specified table size")
|
327
|
+
|
328
|
+
slides_service = SlidesService()
|
329
|
+
result = slides_service.add_table(
|
330
|
+
presentation_id=presentation_id,
|
331
|
+
slide_id=slide_id,
|
332
|
+
rows=rows,
|
333
|
+
columns=columns,
|
334
|
+
data=data,
|
335
|
+
position=(position_x, position_y),
|
336
|
+
size=(size_width, size_height),
|
337
|
+
)
|
338
|
+
|
339
|
+
if isinstance(result, dict) and result.get("error"):
|
340
|
+
raise ValueError(result.get("message", "Error adding table to slide"))
|
341
|
+
|
342
|
+
return result
|
343
|
+
|
344
|
+
|
345
|
+
@mcp.tool(
|
346
|
+
name="add_slide_notes",
|
347
|
+
description="Adds presenter notes to a slide in a Google Slides presentation.",
|
348
|
+
)
|
349
|
+
async def add_slide_notes(
|
350
|
+
presentation_id: str,
|
351
|
+
slide_id: str,
|
352
|
+
notes: str,
|
353
|
+
) -> dict[str, Any]:
|
354
|
+
"""
|
355
|
+
Add presenter notes to a slide.
|
356
|
+
|
357
|
+
Args:
|
358
|
+
presentation_id: The ID of the presentation.
|
359
|
+
slide_id: The ID of the slide.
|
360
|
+
notes: The notes content to add.
|
361
|
+
|
362
|
+
Returns:
|
363
|
+
Response data confirming notes addition or raises error.
|
364
|
+
"""
|
365
|
+
logger.info(f"Executing add_slide_notes on slide '{slide_id}'")
|
366
|
+
if not presentation_id or not slide_id or not notes:
|
367
|
+
raise ValueError("Presentation ID, Slide ID, and Notes are required")
|
368
|
+
|
369
|
+
slides_service = SlidesService()
|
370
|
+
result = slides_service.add_slide_notes(
|
371
|
+
presentation_id=presentation_id,
|
372
|
+
slide_id=slide_id,
|
373
|
+
notes_text=notes,
|
374
|
+
)
|
375
|
+
|
376
|
+
if isinstance(result, dict) and result.get("error"):
|
377
|
+
raise ValueError(result.get("message", "Error adding notes to slide"))
|
378
|
+
|
379
|
+
return result
|
380
|
+
|
381
|
+
|
382
|
+
@mcp.tool(
|
383
|
+
name="duplicate_slide",
|
384
|
+
description="Duplicates a slide in a Google Slides presentation.",
|
385
|
+
)
|
386
|
+
async def duplicate_slide(
|
387
|
+
presentation_id: str,
|
388
|
+
slide_id: str,
|
389
|
+
insert_at_index: int | None = None,
|
390
|
+
) -> dict[str, Any]:
|
391
|
+
"""
|
392
|
+
Duplicate a slide in a presentation.
|
393
|
+
|
394
|
+
Args:
|
395
|
+
presentation_id: The ID of the presentation.
|
396
|
+
slide_id: The ID of the slide to duplicate.
|
397
|
+
insert_at_index: Optional index where to insert the duplicated slide.
|
398
|
+
|
399
|
+
Returns:
|
400
|
+
Response data with the new slide ID or raises error.
|
401
|
+
"""
|
402
|
+
logger.info(f"Executing duplicate_slide for slide '{slide_id}'")
|
403
|
+
if not presentation_id or not slide_id:
|
404
|
+
raise ValueError("Presentation ID and Slide ID are required")
|
405
|
+
|
406
|
+
slides_service = SlidesService()
|
407
|
+
result = slides_service.duplicate_slide(
|
408
|
+
presentation_id=presentation_id,
|
409
|
+
slide_id=slide_id,
|
410
|
+
insert_at_index=insert_at_index,
|
411
|
+
)
|
412
|
+
|
413
|
+
if isinstance(result, dict) and result.get("error"):
|
414
|
+
raise ValueError(result.get("message", "Error duplicating slide"))
|
415
|
+
|
416
|
+
return result
|
417
|
+
|
418
|
+
|
419
|
+
@mcp.tool(
|
420
|
+
name="delete_slide",
|
421
|
+
description="Deletes a slide from a Google Slides presentation.",
|
422
|
+
)
|
423
|
+
async def delete_slide(
|
424
|
+
presentation_id: str,
|
425
|
+
slide_id: str,
|
426
|
+
) -> dict[str, Any]:
|
427
|
+
"""
|
428
|
+
Delete a slide from a presentation.
|
429
|
+
|
430
|
+
Args:
|
431
|
+
presentation_id: The ID of the presentation.
|
432
|
+
slide_id: The ID of the slide to delete.
|
433
|
+
|
434
|
+
Returns:
|
435
|
+
Response data confirming slide deletion or raises error.
|
436
|
+
"""
|
437
|
+
logger.info(f"Executing delete_slide: slide '{slide_id}' from presentation '{presentation_id}'")
|
438
|
+
if not presentation_id or not slide_id:
|
439
|
+
raise ValueError("Presentation ID and Slide ID are required")
|
440
|
+
|
441
|
+
slides_service = SlidesService()
|
442
|
+
result = slides_service.delete_slide(presentation_id=presentation_id, slide_id=slide_id)
|
443
|
+
|
444
|
+
if isinstance(result, dict) and result.get("error"):
|
445
|
+
raise ValueError(result.get("message", "Error deleting slide"))
|
446
|
+
|
447
|
+
return result
|
448
|
+
|
449
|
+
|
450
|
+
@mcp.tool(
|
451
|
+
name="create_presentation_from_markdown",
|
452
|
+
description="Creates a Google Slides presentation from structured Markdown content with enhanced formatting support using markdowndeck.",
|
453
|
+
)
|
454
|
+
async def create_presentation_from_markdown(
|
455
|
+
title: str,
|
456
|
+
markdown_content: str,
|
457
|
+
) -> dict[str, Any]:
|
458
|
+
"""
|
459
|
+
Create a Google Slides presentation from Markdown using the markdowndeck library.
|
460
|
+
|
461
|
+
Args:
|
462
|
+
title: The title for the new presentation.
|
463
|
+
markdown_content: Markdown content structured for slides.
|
464
|
+
|
465
|
+
Returns:
|
466
|
+
Created presentation data or raises error.
|
467
|
+
"""
|
468
|
+
logger.info(f"Executing create_presentation_from_markdown with title '{title}'")
|
469
|
+
if not title or not title.strip() or not markdown_content or not markdown_content.strip():
|
470
|
+
raise ValueError("Title and markdown content are required")
|
471
|
+
|
472
|
+
slides_service = SlidesService()
|
473
|
+
result = slides_service.create_presentation_from_markdown(title=title, markdown_content=markdown_content)
|
474
|
+
|
475
|
+
if isinstance(result, dict) and result.get("error"):
|
476
|
+
raise ValueError(result.get("message", "Error creating presentation from Markdown"))
|
477
|
+
|
478
|
+
return result
|
@@ -0,0 +1 @@
|
|
1
|
+
"""Utility functions for google-workspace-mcp."""
|