google-workspace-mcp 1.0.5__py3-none-any.whl → 1.2.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.
@@ -37,7 +37,11 @@ class SlidesService(BaseGoogleService):
37
37
  Presentation data dictionary or error information
38
38
  """
39
39
  try:
40
- return self.service.presentations().get(presentationId=presentation_id).execute()
40
+ return (
41
+ self.service.presentations()
42
+ .get(presentationId=presentation_id)
43
+ .execute()
44
+ )
41
45
  except Exception as e:
42
46
  return self.handle_api_error("get_presentation", e)
43
47
 
@@ -57,7 +61,9 @@ class SlidesService(BaseGoogleService):
57
61
  except Exception as e:
58
62
  return self.handle_api_error("create_presentation", e)
59
63
 
60
- def create_slide(self, presentation_id: str, layout: str = "TITLE_AND_BODY") -> dict[str, Any]:
64
+ def create_slide(
65
+ self, presentation_id: str, layout: str = "TITLE_AND_BODY"
66
+ ) -> dict[str, Any]:
61
67
  """
62
68
  Add a new slide to an existing presentation.
63
69
 
@@ -80,11 +86,17 @@ class SlidesService(BaseGoogleService):
80
86
  }
81
87
  ]
82
88
 
83
- logger.info(f"Sending API request to create slide: {json.dumps(requests[0], indent=2)}")
89
+ logger.info(
90
+ f"Sending API request to create slide: {json.dumps(requests[0], indent=2)}"
91
+ )
84
92
 
85
93
  # Execute the request
86
94
  response = (
87
- self.service.presentations().batchUpdate(presentationId=presentation_id, body={"requests": requests}).execute()
95
+ self.service.presentations()
96
+ .batchUpdate(
97
+ presentationId=presentation_id, body={"requests": requests}
98
+ )
99
+ .execute()
88
100
  )
89
101
 
90
102
  logger.info(f"API response: {json.dumps(response, indent=2)}")
@@ -161,12 +173,20 @@ class SlidesService(BaseGoogleService):
161
173
  },
162
174
  ]
163
175
 
164
- logger.info(f"Sending API request to create shape: {json.dumps(requests[0], indent=2)}")
165
- logger.info(f"Sending API request to insert text: {json.dumps(requests[1], indent=2)}")
176
+ logger.info(
177
+ f"Sending API request to create shape: {json.dumps(requests[0], indent=2)}"
178
+ )
179
+ logger.info(
180
+ f"Sending API request to insert text: {json.dumps(requests[1], indent=2)}"
181
+ )
166
182
 
167
183
  # Execute the request
168
184
  response = (
169
- self.service.presentations().batchUpdate(presentationId=presentation_id, body={"requests": requests}).execute()
185
+ self.service.presentations()
186
+ .batchUpdate(
187
+ presentationId=presentation_id, body={"requests": requests}
188
+ )
189
+ .execute()
170
190
  )
171
191
 
172
192
  logger.info(f"API response: {json.dumps(response, indent=2)}")
@@ -205,7 +225,9 @@ class SlidesService(BaseGoogleService):
205
225
  Response data or error information
206
226
  """
207
227
  try:
208
- logger.info(f"Adding formatted text to slide {slide_id}, position={position}, size={size}")
228
+ logger.info(
229
+ f"Adding formatted text to slide {slide_id}, position={position}, size={size}"
230
+ )
209
231
  logger.info(f"Text content: '{formatted_text[:100]}...'")
210
232
  logger.info(
211
233
  f"Checking for formatting: bold={'**' in formatted_text}, italic={'*' in formatted_text}, code={'`' in formatted_text}"
@@ -240,17 +262,23 @@ class SlidesService(BaseGoogleService):
240
262
  ]
241
263
 
242
264
  # Log the shape creation request
243
- logger.info(f"Sending API request to create shape: {json.dumps(create_requests[0], indent=2)}")
265
+ logger.info(
266
+ f"Sending API request to create shape: {json.dumps(create_requests[0], indent=2)}"
267
+ )
244
268
 
245
269
  # Execute creation request
246
270
  creation_response = (
247
271
  self.service.presentations()
248
- .batchUpdate(presentationId=presentation_id, body={"requests": create_requests})
272
+ .batchUpdate(
273
+ presentationId=presentation_id, body={"requests": create_requests}
274
+ )
249
275
  .execute()
250
276
  )
251
277
 
252
278
  # Log the response
253
- logger.info(f"API response for shape creation: {json.dumps(creation_response, indent=2)}")
279
+ logger.info(
280
+ f"API response for shape creation: {json.dumps(creation_response, indent=2)}"
281
+ )
254
282
 
255
283
  # Process the formatted text
256
284
  # First, remove formatting markers to get plain text
@@ -274,7 +302,9 @@ class SlidesService(BaseGoogleService):
274
302
  ]
275
303
 
276
304
  # Log the text insertion request
277
- logger.info(f"Sending API request to insert plain text: {json.dumps(text_request[0], indent=2)}")
305
+ logger.info(
306
+ f"Sending API request to insert plain text: {json.dumps(text_request[0], indent=2)}"
307
+ )
278
308
 
279
309
  # Execute text insertion
280
310
  text_response = (
@@ -287,7 +317,9 @@ class SlidesService(BaseGoogleService):
287
317
  )
288
318
 
289
319
  # Log the response
290
- logger.info(f"API response for plain text insertion: {json.dumps(text_response, indent=2)}")
320
+ logger.info(
321
+ f"API response for plain text insertion: {json.dumps(text_response, indent=2)}"
322
+ )
291
323
 
292
324
  # Now generate style requests if there's formatting to apply
293
325
  if "**" in formatted_text or "*" in formatted_text:
@@ -364,9 +396,13 @@ class SlidesService(BaseGoogleService):
364
396
  if style_requests:
365
397
  try:
366
398
  # Log the style requests
367
- logger.info(f"Sending API request to apply text styling with {len(style_requests)} style requests")
399
+ logger.info(
400
+ f"Sending API request to apply text styling with {len(style_requests)} style requests"
401
+ )
368
402
  for i, req in enumerate(style_requests):
369
- logger.info(f"Style request {i + 1}: {json.dumps(req, indent=2)}")
403
+ logger.info(
404
+ f"Style request {i + 1}: {json.dumps(req, indent=2)}"
405
+ )
370
406
 
371
407
  # Execute style requests
372
408
  style_response = (
@@ -379,9 +415,13 @@ class SlidesService(BaseGoogleService):
379
415
  )
380
416
 
381
417
  # Log the response
382
- logger.info(f"API response for text styling: {json.dumps(style_response, indent=2)}")
418
+ logger.info(
419
+ f"API response for text styling: {json.dumps(style_response, indent=2)}"
420
+ )
383
421
  except Exception as style_error:
384
- logger.warning(f"Failed to apply text styles: {str(style_error)}")
422
+ logger.warning(
423
+ f"Failed to apply text styles: {str(style_error)}"
424
+ )
385
425
  logger.exception("Style application error details")
386
426
 
387
427
  return {
@@ -440,7 +480,9 @@ class SlidesService(BaseGoogleService):
440
480
  },
441
481
  }
442
482
  }
443
- logger.info(f"Sending API request to create shape for bullet list: {json.dumps(log_data, indent=2)}")
483
+ logger.info(
484
+ f"Sending API request to create shape for bullet list: {json.dumps(log_data, indent=2)}"
485
+ )
444
486
 
445
487
  # Create requests
446
488
  requests = [
@@ -476,15 +518,23 @@ class SlidesService(BaseGoogleService):
476
518
  ]
477
519
 
478
520
  # Log the text insertion
479
- logger.info(f"Sending API request to insert bullet text: {json.dumps(requests[1], indent=2)}")
521
+ logger.info(
522
+ f"Sending API request to insert bullet text: {json.dumps(requests[1], indent=2)}"
523
+ )
480
524
 
481
525
  # Execute the request to create shape and insert text
482
526
  response = (
483
- self.service.presentations().batchUpdate(presentationId=presentation_id, body={"requests": requests}).execute()
527
+ self.service.presentations()
528
+ .batchUpdate(
529
+ presentationId=presentation_id, body={"requests": requests}
530
+ )
531
+ .execute()
484
532
  )
485
533
 
486
534
  # Log the response
487
- logger.info(f"API response for bullet list creation: {json.dumps(response, indent=2)}")
535
+ logger.info(
536
+ f"API response for bullet list creation: {json.dumps(response, indent=2)}"
537
+ )
488
538
 
489
539
  # Now add bullet formatting
490
540
  try:
@@ -493,14 +543,18 @@ class SlidesService(BaseGoogleService):
493
543
  {
494
544
  "createParagraphBullets": {
495
545
  "objectId": element_id,
496
- "textRange": {"type": "ALL"}, # Apply to all text in the shape
546
+ "textRange": {
547
+ "type": "ALL"
548
+ }, # Apply to all text in the shape
497
549
  "bulletPreset": "BULLET_DISC_CIRCLE_SQUARE",
498
550
  }
499
551
  }
500
552
  ]
501
553
 
502
554
  # Log the bullet formatting request
503
- logger.info(f"Sending API request to apply bullet formatting: {json.dumps(bullet_request[0], indent=2)}")
555
+ logger.info(
556
+ f"Sending API request to apply bullet formatting: {json.dumps(bullet_request[0], indent=2)}"
557
+ )
504
558
 
505
559
  bullet_response = (
506
560
  self.service.presentations()
@@ -512,9 +566,13 @@ class SlidesService(BaseGoogleService):
512
566
  )
513
567
 
514
568
  # Log the response
515
- logger.info(f"API response for bullet formatting: {json.dumps(bullet_response, indent=2)}")
569
+ logger.info(
570
+ f"API response for bullet formatting: {json.dumps(bullet_response, indent=2)}"
571
+ )
516
572
  except Exception as bullet_error:
517
- logger.warning(f"Failed to apply bullet formatting: {str(bullet_error)}")
573
+ logger.warning(
574
+ f"Failed to apply bullet formatting: {str(bullet_error)}"
575
+ )
518
576
  # No fallback here - the text is already added, just without bullets
519
577
 
520
578
  return {
@@ -527,7 +585,9 @@ class SlidesService(BaseGoogleService):
527
585
  except Exception as e:
528
586
  return self.handle_api_error("add_bulleted_list", e)
529
587
 
530
- def create_presentation_from_markdown(self, title: str, markdown_content: str) -> dict[str, Any]:
588
+ def create_presentation_from_markdown(
589
+ self, title: str, markdown_content: str
590
+ ) -> dict[str, Any]:
531
591
  """
532
592
  Create a Google Slides presentation from Markdown content using markdowndeck.
533
593
 
@@ -545,9 +605,13 @@ class SlidesService(BaseGoogleService):
545
605
  credentials = gauth.get_credentials()
546
606
 
547
607
  # Use markdowndeck to create the presentation
548
- result = create_presentation(markdown=markdown_content, title=title, credentials=credentials)
608
+ result = create_presentation(
609
+ markdown=markdown_content, title=title, credentials=credentials
610
+ )
549
611
 
550
- logger.info(f"Successfully created presentation with ID: {result.get('presentationId')}")
612
+ logger.info(
613
+ f"Successfully created presentation with ID: {result.get('presentationId')}"
614
+ )
551
615
 
552
616
  # The presentation data is already in the expected format from markdowndeck
553
617
  return result
@@ -568,7 +632,11 @@ class SlidesService(BaseGoogleService):
568
632
  """
569
633
  try:
570
634
  # Get the presentation with slide details
571
- presentation = self.service.presentations().get(presentationId=presentation_id).execute()
635
+ presentation = (
636
+ self.service.presentations()
637
+ .get(presentationId=presentation_id)
638
+ .execute()
639
+ )
572
640
 
573
641
  # Extract slide information
574
642
  slides = []
@@ -587,9 +655,13 @@ class SlidesService(BaseGoogleService):
587
655
  if "textElements" in element["shape"]["text"]:
588
656
  # Extract text content
589
657
  text_parts = []
590
- for text_element in element["shape"]["text"]["textElements"]:
658
+ for text_element in element["shape"]["text"][
659
+ "textElements"
660
+ ]:
591
661
  if "textRun" in text_element:
592
- text_parts.append(text_element["textRun"].get("content", ""))
662
+ text_parts.append(
663
+ text_element["textRun"].get("content", "")
664
+ )
593
665
  element_content = "".join(text_parts)
594
666
  elif "image" in element:
595
667
  element_type = "image"
@@ -597,9 +669,7 @@ class SlidesService(BaseGoogleService):
597
669
  element_content = element["image"]["contentUrl"]
598
670
  elif "table" in element:
599
671
  element_type = "table"
600
- element_content = (
601
- f"Table with {element['table'].get('rows', 0)} rows, {element['table'].get('columns', 0)} columns"
602
- )
672
+ element_content = f"Table with {element['table'].get('rows', 0)} rows, {element['table'].get('columns', 0)} columns"
603
673
 
604
674
  # Add to elements if we found content
605
675
  if element_type and element_content:
@@ -613,7 +683,10 @@ class SlidesService(BaseGoogleService):
613
683
 
614
684
  # Get speaker notes if present
615
685
  notes = ""
616
- if "slideProperties" in slide and "notesPage" in slide["slideProperties"]:
686
+ if (
687
+ "slideProperties" in slide
688
+ and "notesPage" in slide["slideProperties"]
689
+ ):
617
690
  notes_page = slide["slideProperties"]["notesPage"]
618
691
  if "pageElements" in notes_page:
619
692
  for element in notes_page["pageElements"]:
@@ -623,9 +696,13 @@ class SlidesService(BaseGoogleService):
623
696
  and "textElements" in element["shape"]["text"]
624
697
  ):
625
698
  note_parts = []
626
- for text_element in element["shape"]["text"]["textElements"]:
699
+ for text_element in element["shape"]["text"][
700
+ "textElements"
701
+ ]:
627
702
  if "textRun" in text_element:
628
- note_parts.append(text_element["textRun"].get("content", ""))
703
+ note_parts.append(
704
+ text_element["textRun"].get("content", "")
705
+ )
629
706
  if note_parts:
630
707
  notes = "".join(note_parts)
631
708
 
@@ -657,14 +734,22 @@ class SlidesService(BaseGoogleService):
657
734
  # Define the delete request
658
735
  requests = [{"deleteObject": {"objectId": slide_id}}]
659
736
 
660
- logger.info(f"Sending API request to delete slide: {json.dumps(requests[0], indent=2)}")
737
+ logger.info(
738
+ f"Sending API request to delete slide: {json.dumps(requests[0], indent=2)}"
739
+ )
661
740
 
662
741
  # Execute the request
663
742
  response = (
664
- self.service.presentations().batchUpdate(presentationId=presentation_id, body={"requests": requests}).execute()
743
+ self.service.presentations()
744
+ .batchUpdate(
745
+ presentationId=presentation_id, body={"requests": requests}
746
+ )
747
+ .execute()
665
748
  )
666
749
 
667
- logger.info(f"API response for slide deletion: {json.dumps(response, indent=2)}")
750
+ logger.info(
751
+ f"API response for slide deletion: {json.dumps(response, indent=2)}"
752
+ )
668
753
 
669
754
  return {
670
755
  "presentationId": presentation_id,
@@ -724,7 +809,9 @@ class SlidesService(BaseGoogleService):
724
809
  "height": {"magnitude": size[1], "unit": "PT"},
725
810
  }
726
811
 
727
- logger.info(f"Sending API request to create image: {json.dumps(create_image_request, indent=2)}")
812
+ logger.info(
813
+ f"Sending API request to create image: {json.dumps(create_image_request, indent=2)}"
814
+ )
728
815
 
729
816
  # Execute the request
730
817
  response = (
@@ -739,7 +826,9 @@ class SlidesService(BaseGoogleService):
739
826
  # Extract the image ID from the response
740
827
  if "replies" in response and len(response["replies"]) > 0:
741
828
  image_id = response["replies"][0].get("createImage", {}).get("objectId")
742
- logger.info(f"API response for image creation: {json.dumps(response, indent=2)}")
829
+ logger.info(
830
+ f"API response for image creation: {json.dumps(response, indent=2)}"
831
+ )
743
832
  return {
744
833
  "presentationId": presentation_id,
745
834
  "slideId": slide_id,
@@ -804,7 +893,9 @@ class SlidesService(BaseGoogleService):
804
893
  }
805
894
  }
806
895
 
807
- logger.info(f"Sending API request to create table: {json.dumps(create_table_request, indent=2)}")
896
+ logger.info(
897
+ f"Sending API request to create table: {json.dumps(create_table_request, indent=2)}"
898
+ )
808
899
 
809
900
  # Execute table creation
810
901
  response = (
@@ -816,7 +907,9 @@ class SlidesService(BaseGoogleService):
816
907
  .execute()
817
908
  )
818
909
 
819
- logger.info(f"API response for table creation: {json.dumps(response, indent=2)}")
910
+ logger.info(
911
+ f"API response for table creation: {json.dumps(response, indent=2)}"
912
+ )
820
913
 
821
914
  # Populate the table if data is provided
822
915
  if data:
@@ -841,7 +934,9 @@ class SlidesService(BaseGoogleService):
841
934
  )
842
935
 
843
936
  if text_requests:
844
- logger.info(f"Sending API request to populate table with {len(text_requests)} cell entries")
937
+ logger.info(
938
+ f"Sending API request to populate table with {len(text_requests)} cell entries"
939
+ )
845
940
  table_text_response = (
846
941
  self.service.presentations()
847
942
  .batchUpdate(
@@ -850,7 +945,9 @@ class SlidesService(BaseGoogleService):
850
945
  )
851
946
  .execute()
852
947
  )
853
- logger.info(f"API response for table population: {json.dumps(table_text_response, indent=2)}")
948
+ logger.info(
949
+ f"API response for table population: {json.dumps(table_text_response, indent=2)}"
950
+ )
854
951
 
855
952
  return {
856
953
  "presentationId": presentation_id,
@@ -891,14 +988,22 @@ class SlidesService(BaseGoogleService):
891
988
  }
892
989
  ]
893
990
 
894
- logger.info(f"Sending API request to add slide notes: {json.dumps(requests[0], indent=2)}")
991
+ logger.info(
992
+ f"Sending API request to add slide notes: {json.dumps(requests[0], indent=2)}"
993
+ )
895
994
 
896
995
  # Execute the request
897
996
  response = (
898
- self.service.presentations().batchUpdate(presentationId=presentation_id, body={"requests": requests}).execute()
997
+ self.service.presentations()
998
+ .batchUpdate(
999
+ presentationId=presentation_id, body={"requests": requests}
1000
+ )
1001
+ .execute()
899
1002
  )
900
1003
 
901
- logger.info(f"API response for slide notes: {json.dumps(response, indent=2)}")
1004
+ logger.info(
1005
+ f"API response for slide notes: {json.dumps(response, indent=2)}"
1006
+ )
902
1007
 
903
1008
  return {
904
1009
  "presentationId": presentation_id,
@@ -909,7 +1014,9 @@ class SlidesService(BaseGoogleService):
909
1014
  except Exception as e:
910
1015
  return self.handle_api_error("add_slide_notes", e)
911
1016
 
912
- def duplicate_slide(self, presentation_id: str, slide_id: str, insert_at_index: int | None = None) -> dict[str, Any]:
1017
+ def duplicate_slide(
1018
+ self, presentation_id: str, slide_id: str, insert_at_index: int | None = None
1019
+ ) -> dict[str, Any]:
913
1020
  """
914
1021
  Duplicate a slide in a presentation.
915
1022
 
@@ -929,7 +1036,9 @@ class SlidesService(BaseGoogleService):
929
1036
  if insert_at_index is not None:
930
1037
  duplicate_request["duplicateObject"]["insertionIndex"] = insert_at_index
931
1038
 
932
- logger.info(f"Sending API request to duplicate slide: {json.dumps(duplicate_request, indent=2)}")
1039
+ logger.info(
1040
+ f"Sending API request to duplicate slide: {json.dumps(duplicate_request, indent=2)}"
1041
+ )
933
1042
 
934
1043
  # Execute the duplicate request
935
1044
  response = (
@@ -941,12 +1050,16 @@ class SlidesService(BaseGoogleService):
941
1050
  .execute()
942
1051
  )
943
1052
 
944
- logger.info(f"API response for slide duplication: {json.dumps(response, indent=2)}")
1053
+ logger.info(
1054
+ f"API response for slide duplication: {json.dumps(response, indent=2)}"
1055
+ )
945
1056
 
946
1057
  # Extract the duplicated slide ID
947
1058
  new_slide_id = None
948
1059
  if "replies" in response and len(response["replies"]) > 0:
949
- new_slide_id = response["replies"][0].get("duplicateObject", {}).get("objectId")
1060
+ new_slide_id = (
1061
+ response["replies"][0].get("duplicateObject", {}).get("objectId")
1062
+ )
950
1063
 
951
1064
  return {
952
1065
  "presentationId": presentation_id,
@@ -957,3 +1070,79 @@ class SlidesService(BaseGoogleService):
957
1070
  }
958
1071
  except Exception as e:
959
1072
  return self.handle_api_error("duplicate_slide", e)
1073
+
1074
+ def embed_sheets_chart(
1075
+ self,
1076
+ presentation_id: str,
1077
+ slide_id: str,
1078
+ spreadsheet_id: str,
1079
+ chart_id: int,
1080
+ position: tuple[float, float],
1081
+ size: tuple[float, float],
1082
+ ) -> dict[str, Any]:
1083
+ """
1084
+ Embeds a chart from Google Sheets into a Google Slides presentation.
1085
+
1086
+ Args:
1087
+ presentation_id: The ID of the presentation.
1088
+ slide_id: The ID of the slide to add the chart to.
1089
+ spreadsheet_id: The ID of the Google Sheet containing the chart.
1090
+ chart_id: The ID of the chart within the sheet.
1091
+ position: Tuple of (x, y) coordinates for position in PT.
1092
+ size: Tuple of (width, height) for the chart size in PT.
1093
+
1094
+ Returns:
1095
+ The API response from the batchUpdate call.
1096
+ """
1097
+ try:
1098
+ element_id = f"chart_{chart_id}_{int(__import__('time').time() * 1000)}"
1099
+ logger.info(
1100
+ f"Embedding chart {chart_id} from sheet {spreadsheet_id} into slide {slide_id}"
1101
+ )
1102
+
1103
+ requests = [
1104
+ {
1105
+ "createSheetsChart": {
1106
+ "objectId": element_id,
1107
+ "spreadsheetId": spreadsheet_id,
1108
+ "chartId": chart_id,
1109
+ "linkingMode": "LINKED",
1110
+ "elementProperties": {
1111
+ "pageObjectId": slide_id,
1112
+ "size": {
1113
+ "width": {"magnitude": size[0], "unit": "PT"},
1114
+ "height": {"magnitude": size[1], "unit": "PT"},
1115
+ },
1116
+ "transform": {
1117
+ "scaleX": 1,
1118
+ "scaleY": 1,
1119
+ "translateX": position[0],
1120
+ "translateY": position[1],
1121
+ "unit": "PT",
1122
+ },
1123
+ },
1124
+ }
1125
+ }
1126
+ ]
1127
+
1128
+ response = self.batch_update(presentation_id, requests)
1129
+ if response.get("error"):
1130
+ raise ValueError(
1131
+ response.get("message", "Batch update for chart embedding failed")
1132
+ )
1133
+
1134
+ created_element_id = (
1135
+ response.get("replies", [{}])[0]
1136
+ .get("createSheetsChart", {})
1137
+ .get("objectId")
1138
+ )
1139
+
1140
+ return {
1141
+ "success": True,
1142
+ "presentation_id": presentation_id,
1143
+ "slide_id": slide_id,
1144
+ "element_id": created_element_id or element_id,
1145
+ }
1146
+
1147
+ except Exception as e:
1148
+ return self.handle_api_error("embed_sheets_chart", e)