workspace-mcp 1.1.5__py3-none-any.whl → 1.1.6__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.
- core/utils.py +153 -54
- gcalendar/calendar_tools.py +6 -6
- gdocs/docs_tools.py +4 -4
- gdrive/drive_tools.py +5 -5
- gforms/forms_tools.py +5 -5
- gmail/gmail_tools.py +199 -81
- gsheets/sheets_tools.py +7 -7
- gslides/slides_tools.py +25 -25
- gtasks/tasks_tools.py +13 -0
- {workspace_mcp-1.1.5.dist-info → workspace_mcp-1.1.6.dist-info}/METADATA +14 -7
- {workspace_mcp-1.1.5.dist-info → workspace_mcp-1.1.6.dist-info}/RECORD +15 -15
- {workspace_mcp-1.1.5.dist-info → workspace_mcp-1.1.6.dist-info}/WHEEL +0 -0
- {workspace_mcp-1.1.5.dist-info → workspace_mcp-1.1.6.dist-info}/entry_points.txt +0 -0
- {workspace_mcp-1.1.5.dist-info → workspace_mcp-1.1.6.dist-info}/licenses/LICENSE +0 -0
- {workspace_mcp-1.1.5.dist-info → workspace_mcp-1.1.6.dist-info}/top_level.txt +0 -0
gmail/gmail_tools.py
CHANGED
@@ -111,28 +111,31 @@ def _format_gmail_results_plain(messages: list, query: str) -> str:
|
|
111
111
|
message_url = _generate_gmail_web_url(msg["id"])
|
112
112
|
thread_url = _generate_gmail_web_url(msg["threadId"])
|
113
113
|
|
114
|
-
lines.extend(
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
])
|
114
|
+
lines.extend(
|
115
|
+
[
|
116
|
+
f" {i}. Message ID: {msg['id']}",
|
117
|
+
f" Web Link: {message_url}",
|
118
|
+
f" Thread ID: {msg['threadId']}",
|
119
|
+
f" Thread Link: {thread_url}",
|
120
|
+
"",
|
121
|
+
]
|
122
|
+
)
|
123
|
+
|
124
|
+
lines.extend(
|
125
|
+
[
|
126
|
+
"💡 USAGE:",
|
127
|
+
" • Pass the Message IDs **as a list** to get_gmail_messages_content_batch()",
|
128
|
+
" e.g. get_gmail_messages_content_batch(message_ids=[...])",
|
129
|
+
" • Pass the Thread IDs to get_gmail_thread_content() (single) or get_gmail_threads_content_batch() (batch)",
|
130
|
+
]
|
131
|
+
)
|
129
132
|
|
130
133
|
return "\n".join(lines)
|
131
134
|
|
132
135
|
|
133
136
|
@server.tool()
|
137
|
+
@handle_http_errors("search_gmail_messages", is_read_only=True)
|
134
138
|
@require_google_service("gmail", "gmail_read")
|
135
|
-
@handle_http_errors("search_gmail_messages")
|
136
139
|
async def search_gmail_messages(
|
137
140
|
service, query: str, user_google_email: str, page_size: int = 10
|
138
141
|
) -> str:
|
@@ -148,7 +151,9 @@ async def search_gmail_messages(
|
|
148
151
|
Returns:
|
149
152
|
str: LLM-friendly structured results with Message IDs, Thread IDs, and clickable Gmail web interface URLs for each found message.
|
150
153
|
"""
|
151
|
-
logger.info(
|
154
|
+
logger.info(
|
155
|
+
f"[search_gmail_messages] Email: '{user_google_email}', Query: '{query}'"
|
156
|
+
)
|
152
157
|
|
153
158
|
response = await asyncio.to_thread(
|
154
159
|
service.users()
|
@@ -164,8 +169,8 @@ async def search_gmail_messages(
|
|
164
169
|
|
165
170
|
|
166
171
|
@server.tool()
|
172
|
+
@handle_http_errors("get_gmail_message_content", is_read_only=True)
|
167
173
|
@require_google_service("gmail", "gmail_read")
|
168
|
-
@handle_http_errors("get_gmail_message_content")
|
169
174
|
async def get_gmail_message_content(
|
170
175
|
service, message_id: str, user_google_email: str
|
171
176
|
) -> str:
|
@@ -232,8 +237,8 @@ async def get_gmail_message_content(
|
|
232
237
|
|
233
238
|
|
234
239
|
@server.tool()
|
240
|
+
@handle_http_errors("get_gmail_messages_content_batch", is_read_only=True)
|
235
241
|
@require_google_service("gmail", "gmail_read")
|
236
|
-
@handle_http_errors("get_gmail_messages_content_batch")
|
237
242
|
async def get_gmail_messages_content_batch(
|
238
243
|
service,
|
239
244
|
message_ids: List[str],
|
@@ -263,7 +268,7 @@ async def get_gmail_messages_content_batch(
|
|
263
268
|
|
264
269
|
# Process in chunks of 100 (Gmail batch limit)
|
265
270
|
for chunk_start in range(0, len(message_ids), 100):
|
266
|
-
chunk_ids = message_ids[chunk_start:chunk_start + 100]
|
271
|
+
chunk_ids = message_ids[chunk_start : chunk_start + 100]
|
267
272
|
results: Dict[str, Dict] = {}
|
268
273
|
|
269
274
|
def _batch_callback(request_id, response, exception):
|
@@ -276,17 +281,21 @@ async def get_gmail_messages_content_batch(
|
|
276
281
|
|
277
282
|
for mid in chunk_ids:
|
278
283
|
if format == "metadata":
|
279
|
-
req =
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
+
req = (
|
285
|
+
service.users()
|
286
|
+
.messages()
|
287
|
+
.get(
|
288
|
+
userId="me",
|
289
|
+
id=mid,
|
290
|
+
format="metadata",
|
291
|
+
metadataHeaders=["Subject", "From"],
|
292
|
+
)
|
284
293
|
)
|
285
294
|
else:
|
286
|
-
req =
|
287
|
-
|
288
|
-
|
289
|
-
format="full"
|
295
|
+
req = (
|
296
|
+
service.users()
|
297
|
+
.messages()
|
298
|
+
.get(userId="me", id=mid, format="full")
|
290
299
|
)
|
291
300
|
batch.add(req, request_id=mid)
|
292
301
|
|
@@ -303,20 +312,22 @@ async def get_gmail_messages_content_batch(
|
|
303
312
|
try:
|
304
313
|
if format == "metadata":
|
305
314
|
msg = await asyncio.to_thread(
|
306
|
-
service.users()
|
315
|
+
service.users()
|
316
|
+
.messages()
|
317
|
+
.get(
|
307
318
|
userId="me",
|
308
319
|
id=mid,
|
309
320
|
format="metadata",
|
310
|
-
metadataHeaders=["Subject", "From"]
|
311
|
-
)
|
321
|
+
metadataHeaders=["Subject", "From"],
|
322
|
+
)
|
323
|
+
.execute
|
312
324
|
)
|
313
325
|
else:
|
314
326
|
msg = await asyncio.to_thread(
|
315
|
-
service.users()
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
).execute
|
327
|
+
service.users()
|
328
|
+
.messages()
|
329
|
+
.get(userId="me", id=mid, format="full")
|
330
|
+
.execute
|
320
331
|
)
|
321
332
|
return mid, msg, None
|
322
333
|
except Exception as e:
|
@@ -324,8 +335,7 @@ async def get_gmail_messages_content_batch(
|
|
324
335
|
|
325
336
|
# Fetch all messages in parallel
|
326
337
|
fetch_results = await asyncio.gather(
|
327
|
-
*[fetch_message(mid) for mid in chunk_ids],
|
328
|
-
return_exceptions=False
|
338
|
+
*[fetch_message(mid) for mid in chunk_ids], return_exceptions=False
|
329
339
|
)
|
330
340
|
|
331
341
|
# Convert to results format
|
@@ -337,15 +347,11 @@ async def get_gmail_messages_content_batch(
|
|
337
347
|
entry = results.get(mid, {"data": None, "error": "No result"})
|
338
348
|
|
339
349
|
if entry["error"]:
|
340
|
-
output_messages.append(
|
341
|
-
f"⚠️ Message {mid}: {entry['error']}\n"
|
342
|
-
)
|
350
|
+
output_messages.append(f"⚠️ Message {mid}: {entry['error']}\n")
|
343
351
|
else:
|
344
352
|
message = entry["data"]
|
345
353
|
if not message:
|
346
|
-
output_messages.append(
|
347
|
-
f"⚠️ Message {mid}: No data returned\n"
|
348
|
-
)
|
354
|
+
output_messages.append(f"⚠️ Message {mid}: No data returned\n")
|
349
355
|
continue
|
350
356
|
|
351
357
|
# Extract content based on format
|
@@ -385,8 +391,8 @@ async def get_gmail_messages_content_batch(
|
|
385
391
|
|
386
392
|
|
387
393
|
@server.tool()
|
388
|
-
@require_google_service("gmail", GMAIL_SEND_SCOPE)
|
389
394
|
@handle_http_errors("send_gmail_message")
|
395
|
+
@require_google_service("gmail", GMAIL_SEND_SCOPE)
|
390
396
|
async def send_gmail_message(
|
391
397
|
service,
|
392
398
|
user_google_email: str,
|
@@ -422,8 +428,8 @@ async def send_gmail_message(
|
|
422
428
|
|
423
429
|
|
424
430
|
@server.tool()
|
425
|
-
@require_google_service("gmail", GMAIL_COMPOSE_SCOPE)
|
426
431
|
@handle_http_errors("draft_gmail_message")
|
432
|
+
@require_google_service("gmail", GMAIL_COMPOSE_SCOPE)
|
427
433
|
async def draft_gmail_message(
|
428
434
|
service,
|
429
435
|
user_google_email: str,
|
@@ -468,35 +474,18 @@ async def draft_gmail_message(
|
|
468
474
|
return f"Draft created! Draft ID: {draft_id}"
|
469
475
|
|
470
476
|
|
471
|
-
|
472
|
-
@require_google_service("gmail", "gmail_read")
|
473
|
-
@handle_http_errors("get_gmail_thread_content")
|
474
|
-
async def get_gmail_thread_content(
|
475
|
-
service, thread_id: str, user_google_email: str
|
476
|
-
) -> str:
|
477
|
+
def _format_thread_content(thread_data: dict, thread_id: str) -> str:
|
477
478
|
"""
|
478
|
-
|
479
|
+
Helper function to format thread content from Gmail API response.
|
479
480
|
|
480
481
|
Args:
|
481
|
-
|
482
|
-
|
482
|
+
thread_data (dict): Thread data from Gmail API
|
483
|
+
thread_id (str): Thread ID for display
|
483
484
|
|
484
485
|
Returns:
|
485
|
-
str:
|
486
|
+
str: Formatted thread content
|
486
487
|
"""
|
487
|
-
|
488
|
-
f"[get_gmail_thread_content] Invoked. Thread ID: '{thread_id}', Email: '{user_google_email}'"
|
489
|
-
)
|
490
|
-
|
491
|
-
# Fetch the complete thread with all messages
|
492
|
-
thread_response = await asyncio.to_thread(
|
493
|
-
service.users()
|
494
|
-
.threads()
|
495
|
-
.get(userId="me", id=thread_id, format="full")
|
496
|
-
.execute
|
497
|
-
)
|
498
|
-
|
499
|
-
messages = thread_response.get("messages", [])
|
488
|
+
messages = thread_data.get("messages", [])
|
500
489
|
if not messages:
|
501
490
|
return f"No messages found in thread '{thread_id}'."
|
502
491
|
|
@@ -520,8 +509,7 @@ async def get_gmail_thread_content(
|
|
520
509
|
for i, message in enumerate(messages, 1):
|
521
510
|
# Extract headers
|
522
511
|
headers = {
|
523
|
-
h["name"]: h["value"]
|
524
|
-
for h in message.get("payload", {}).get("headers", [])
|
512
|
+
h["name"]: h["value"] for h in message.get("payload", {}).get("headers", [])
|
525
513
|
}
|
526
514
|
|
527
515
|
sender = headers.get("From", "(unknown sender)")
|
@@ -553,13 +541,134 @@ async def get_gmail_thread_content(
|
|
553
541
|
]
|
554
542
|
)
|
555
543
|
|
556
|
-
|
557
|
-
return content_text
|
544
|
+
return "\n".join(content_lines)
|
558
545
|
|
559
546
|
|
560
547
|
@server.tool()
|
561
548
|
@require_google_service("gmail", "gmail_read")
|
562
|
-
@handle_http_errors("
|
549
|
+
@handle_http_errors("get_gmail_thread_content", is_read_only=True)
|
550
|
+
async def get_gmail_thread_content(
|
551
|
+
service, thread_id: str, user_google_email: str
|
552
|
+
) -> str:
|
553
|
+
"""
|
554
|
+
Retrieves the complete content of a Gmail conversation thread, including all messages.
|
555
|
+
|
556
|
+
Args:
|
557
|
+
thread_id (str): The unique ID of the Gmail thread to retrieve.
|
558
|
+
user_google_email (str): The user's Google email address. Required.
|
559
|
+
|
560
|
+
Returns:
|
561
|
+
str: The complete thread content with all messages formatted for reading.
|
562
|
+
"""
|
563
|
+
logger.info(
|
564
|
+
f"[get_gmail_thread_content] Invoked. Thread ID: '{thread_id}', Email: '{user_google_email}'"
|
565
|
+
)
|
566
|
+
|
567
|
+
# Fetch the complete thread with all messages
|
568
|
+
thread_response = await asyncio.to_thread(
|
569
|
+
service.users().threads().get(userId="me", id=thread_id, format="full").execute
|
570
|
+
)
|
571
|
+
|
572
|
+
return _format_thread_content(thread_response, thread_id)
|
573
|
+
|
574
|
+
|
575
|
+
@server.tool()
|
576
|
+
@require_google_service("gmail", "gmail_read")
|
577
|
+
@handle_http_errors("get_gmail_threads_content_batch", is_read_only=True)
|
578
|
+
async def get_gmail_threads_content_batch(
|
579
|
+
service,
|
580
|
+
thread_ids: List[str],
|
581
|
+
user_google_email: str,
|
582
|
+
) -> str:
|
583
|
+
"""
|
584
|
+
Retrieves the content of multiple Gmail threads in a single batch request.
|
585
|
+
Supports up to 100 threads per request using Google's batch API.
|
586
|
+
|
587
|
+
Args:
|
588
|
+
thread_ids (List[str]): A list of Gmail thread IDs to retrieve. The function will automatically batch requests in chunks of 100.
|
589
|
+
user_google_email (str): The user's Google email address. Required.
|
590
|
+
|
591
|
+
Returns:
|
592
|
+
str: A formatted list of thread contents with separators.
|
593
|
+
"""
|
594
|
+
logger.info(
|
595
|
+
f"[get_gmail_threads_content_batch] Invoked. Thread count: {len(thread_ids)}, Email: '{user_google_email}'"
|
596
|
+
)
|
597
|
+
|
598
|
+
if not thread_ids:
|
599
|
+
raise ValueError("No thread IDs provided")
|
600
|
+
|
601
|
+
output_threads = []
|
602
|
+
|
603
|
+
def _batch_callback(request_id, response, exception):
|
604
|
+
"""Callback for batch requests"""
|
605
|
+
results[request_id] = {"data": response, "error": exception}
|
606
|
+
|
607
|
+
# Process in chunks of 100 (Gmail batch limit)
|
608
|
+
for chunk_start in range(0, len(thread_ids), 100):
|
609
|
+
chunk_ids = thread_ids[chunk_start : chunk_start + 100]
|
610
|
+
results: Dict[str, Dict] = {}
|
611
|
+
|
612
|
+
# Try to use batch API
|
613
|
+
try:
|
614
|
+
batch = service.new_batch_http_request(callback=_batch_callback)
|
615
|
+
|
616
|
+
for tid in chunk_ids:
|
617
|
+
req = service.users().threads().get(userId="me", id=tid, format="full")
|
618
|
+
batch.add(req, request_id=tid)
|
619
|
+
|
620
|
+
# Execute batch request
|
621
|
+
await asyncio.to_thread(batch.execute)
|
622
|
+
|
623
|
+
except Exception as batch_error:
|
624
|
+
# Fallback to asyncio.gather if batch API fails
|
625
|
+
logger.warning(
|
626
|
+
f"[get_gmail_threads_content_batch] Batch API failed, falling back to asyncio.gather: {batch_error}"
|
627
|
+
)
|
628
|
+
|
629
|
+
async def fetch_thread(tid: str):
|
630
|
+
try:
|
631
|
+
thread = await asyncio.to_thread(
|
632
|
+
service.users()
|
633
|
+
.threads()
|
634
|
+
.get(userId="me", id=tid, format="full")
|
635
|
+
.execute
|
636
|
+
)
|
637
|
+
return tid, thread, None
|
638
|
+
except Exception as e:
|
639
|
+
return tid, None, e
|
640
|
+
|
641
|
+
# Fetch all threads in parallel
|
642
|
+
fetch_results = await asyncio.gather(
|
643
|
+
*[fetch_thread(tid) for tid in chunk_ids], return_exceptions=False
|
644
|
+
)
|
645
|
+
|
646
|
+
# Convert to results format
|
647
|
+
for tid, thread, error in fetch_results:
|
648
|
+
results[tid] = {"data": thread, "error": error}
|
649
|
+
|
650
|
+
# Process results for this chunk
|
651
|
+
for tid in chunk_ids:
|
652
|
+
entry = results.get(tid, {"data": None, "error": "No result"})
|
653
|
+
|
654
|
+
if entry["error"]:
|
655
|
+
output_threads.append(f"⚠️ Thread {tid}: {entry['error']}\n")
|
656
|
+
else:
|
657
|
+
thread = entry["data"]
|
658
|
+
if not thread:
|
659
|
+
output_threads.append(f"⚠️ Thread {tid}: No data returned\n")
|
660
|
+
continue
|
661
|
+
|
662
|
+
output_threads.append(_format_thread_content(thread, tid))
|
663
|
+
|
664
|
+
# Combine all threads with separators
|
665
|
+
header = f"Retrieved {len(thread_ids)} threads:"
|
666
|
+
return header + "\n\n" + "\n---\n\n".join(output_threads)
|
667
|
+
|
668
|
+
|
669
|
+
@server.tool()
|
670
|
+
@handle_http_errors("list_gmail_labels", is_read_only=True)
|
671
|
+
@require_google_service("gmail", "gmail_read")
|
563
672
|
async def list_gmail_labels(service, user_google_email: str) -> str:
|
564
673
|
"""
|
565
674
|
Lists all labels in the user's Gmail account.
|
@@ -606,8 +715,8 @@ async def list_gmail_labels(service, user_google_email: str) -> str:
|
|
606
715
|
|
607
716
|
|
608
717
|
@server.tool()
|
609
|
-
@require_google_service("gmail", GMAIL_LABELS_SCOPE)
|
610
718
|
@handle_http_errors("manage_gmail_label")
|
719
|
+
@require_google_service("gmail", GMAIL_LABELS_SCOPE)
|
611
720
|
async def manage_gmail_label(
|
612
721
|
service,
|
613
722
|
user_google_email: str,
|
@@ -631,7 +740,9 @@ async def manage_gmail_label(
|
|
631
740
|
Returns:
|
632
741
|
str: Confirmation message of the label operation.
|
633
742
|
"""
|
634
|
-
logger.info(
|
743
|
+
logger.info(
|
744
|
+
f"[manage_gmail_label] Invoked. Email: '{user_google_email}', Action: '{action}'"
|
745
|
+
)
|
635
746
|
|
636
747
|
if action == "create" and not name:
|
637
748
|
raise Exception("Label name is required for create action.")
|
@@ -663,7 +774,10 @@ async def manage_gmail_label(
|
|
663
774
|
}
|
664
775
|
|
665
776
|
updated_label = await asyncio.to_thread(
|
666
|
-
service.users()
|
777
|
+
service.users()
|
778
|
+
.labels()
|
779
|
+
.update(userId="me", id=label_id, body=label_object)
|
780
|
+
.execute
|
667
781
|
)
|
668
782
|
return f"Label updated successfully!\nName: {updated_label['name']}\nID: {updated_label['id']}"
|
669
783
|
|
@@ -680,8 +794,8 @@ async def manage_gmail_label(
|
|
680
794
|
|
681
795
|
|
682
796
|
@server.tool()
|
683
|
-
@require_google_service("gmail", GMAIL_MODIFY_SCOPE)
|
684
797
|
@handle_http_errors("modify_gmail_message_labels")
|
798
|
+
@require_google_service("gmail", GMAIL_MODIFY_SCOPE)
|
685
799
|
async def modify_gmail_message_labels(
|
686
800
|
service,
|
687
801
|
user_google_email: str,
|
@@ -701,10 +815,14 @@ async def modify_gmail_message_labels(
|
|
701
815
|
Returns:
|
702
816
|
str: Confirmation message of the label changes applied to the message.
|
703
817
|
"""
|
704
|
-
logger.info(
|
818
|
+
logger.info(
|
819
|
+
f"[modify_gmail_message_labels] Invoked. Email: '{user_google_email}', Message ID: '{message_id}'"
|
820
|
+
)
|
705
821
|
|
706
822
|
if not add_label_ids and not remove_label_ids:
|
707
|
-
raise Exception(
|
823
|
+
raise Exception(
|
824
|
+
"At least one of add_label_ids or remove_label_ids must be provided."
|
825
|
+
)
|
708
826
|
|
709
827
|
body = {}
|
710
828
|
if add_label_ids:
|
gsheets/sheets_tools.py
CHANGED
@@ -21,8 +21,8 @@ logger = logging.getLogger(__name__)
|
|
21
21
|
|
22
22
|
|
23
23
|
@server.tool()
|
24
|
+
@handle_http_errors("list_spreadsheets", is_read_only=True)
|
24
25
|
@require_google_service("drive", "drive_read")
|
25
|
-
@handle_http_errors("list_spreadsheets")
|
26
26
|
async def list_spreadsheets(
|
27
27
|
service,
|
28
28
|
user_google_email: str,
|
@@ -70,8 +70,8 @@ async def list_spreadsheets(
|
|
70
70
|
|
71
71
|
|
72
72
|
@server.tool()
|
73
|
+
@handle_http_errors("get_spreadsheet_info", is_read_only=True)
|
73
74
|
@require_google_service("sheets", "sheets_read")
|
74
|
-
@handle_http_errors("get_spreadsheet_info")
|
75
75
|
async def get_spreadsheet_info(
|
76
76
|
service,
|
77
77
|
user_google_email: str,
|
@@ -120,8 +120,8 @@ async def get_spreadsheet_info(
|
|
120
120
|
|
121
121
|
|
122
122
|
@server.tool()
|
123
|
+
@handle_http_errors("read_sheet_values", is_read_only=True)
|
123
124
|
@require_google_service("sheets", "sheets_read")
|
124
|
-
@handle_http_errors("read_sheet_values")
|
125
125
|
async def read_sheet_values(
|
126
126
|
service,
|
127
127
|
user_google_email: str,
|
@@ -170,8 +170,8 @@ async def read_sheet_values(
|
|
170
170
|
|
171
171
|
|
172
172
|
@server.tool()
|
173
|
-
@require_google_service("sheets", "sheets_write")
|
174
173
|
@handle_http_errors("modify_sheet_values")
|
174
|
+
@require_google_service("sheets", "sheets_write")
|
175
175
|
async def modify_sheet_values(
|
176
176
|
service,
|
177
177
|
user_google_email: str,
|
@@ -241,8 +241,8 @@ async def modify_sheet_values(
|
|
241
241
|
|
242
242
|
|
243
243
|
@server.tool()
|
244
|
-
@require_google_service("sheets", "sheets_write")
|
245
244
|
@handle_http_errors("create_spreadsheet")
|
245
|
+
@require_google_service("sheets", "sheets_write")
|
246
246
|
async def create_spreadsheet(
|
247
247
|
service,
|
248
248
|
user_google_email: str,
|
@@ -290,8 +290,8 @@ async def create_spreadsheet(
|
|
290
290
|
|
291
291
|
|
292
292
|
@server.tool()
|
293
|
-
@require_google_service("sheets", "sheets_write")
|
294
293
|
@handle_http_errors("create_sheet")
|
294
|
+
@require_google_service("sheets", "sheets_write")
|
295
295
|
async def create_sheet(
|
296
296
|
service,
|
297
297
|
user_google_email: str,
|
@@ -344,7 +344,7 @@ _comment_tools = create_comment_tools("spreadsheet", "spreadsheet_id")
|
|
344
344
|
|
345
345
|
# Extract and register the functions
|
346
346
|
read_sheet_comments = _comment_tools['read_comments']
|
347
|
-
create_sheet_comment = _comment_tools['create_comment']
|
347
|
+
create_sheet_comment = _comment_tools['create_comment']
|
348
348
|
reply_to_sheet_comment = _comment_tools['reply_to_comment']
|
349
349
|
resolve_sheet_comment = _comment_tools['resolve_comment']
|
350
350
|
|
gslides/slides_tools.py
CHANGED
@@ -20,8 +20,8 @@ logger = logging.getLogger(__name__)
|
|
20
20
|
|
21
21
|
|
22
22
|
@server.tool()
|
23
|
-
@require_google_service("slides", "slides")
|
24
23
|
@handle_http_errors("create_presentation")
|
24
|
+
@require_google_service("slides", "slides")
|
25
25
|
async def create_presentation(
|
26
26
|
service,
|
27
27
|
user_google_email: str,
|
@@ -42,27 +42,27 @@ async def create_presentation(
|
|
42
42
|
body = {
|
43
43
|
'title': title
|
44
44
|
}
|
45
|
-
|
45
|
+
|
46
46
|
result = await asyncio.to_thread(
|
47
47
|
service.presentations().create(body=body).execute
|
48
48
|
)
|
49
|
-
|
49
|
+
|
50
50
|
presentation_id = result.get('presentationId')
|
51
51
|
presentation_url = f"https://docs.google.com/presentation/d/{presentation_id}/edit"
|
52
|
-
|
52
|
+
|
53
53
|
confirmation_message = f"""Presentation Created Successfully for {user_google_email}:
|
54
54
|
- Title: {title}
|
55
55
|
- Presentation ID: {presentation_id}
|
56
56
|
- URL: {presentation_url}
|
57
57
|
- Slides: {len(result.get('slides', []))} slide(s) created"""
|
58
|
-
|
58
|
+
|
59
59
|
logger.info(f"Presentation created successfully for {user_google_email}")
|
60
60
|
return confirmation_message
|
61
61
|
|
62
62
|
|
63
63
|
@server.tool()
|
64
|
+
@handle_http_errors("get_presentation", is_read_only=True)
|
64
65
|
@require_google_service("slides", "slides_read")
|
65
|
-
@handle_http_errors("get_presentation")
|
66
66
|
async def get_presentation(
|
67
67
|
service,
|
68
68
|
user_google_email: str,
|
@@ -83,17 +83,17 @@ async def get_presentation(
|
|
83
83
|
result = await asyncio.to_thread(
|
84
84
|
service.presentations().get(presentationId=presentation_id).execute
|
85
85
|
)
|
86
|
-
|
86
|
+
|
87
87
|
title = result.get('title', 'Untitled')
|
88
88
|
slides = result.get('slides', [])
|
89
89
|
page_size = result.get('pageSize', {})
|
90
|
-
|
90
|
+
|
91
91
|
slides_info = []
|
92
92
|
for i, slide in enumerate(slides, 1):
|
93
93
|
slide_id = slide.get('objectId', 'Unknown')
|
94
94
|
page_elements = slide.get('pageElements', [])
|
95
95
|
slides_info.append(f" Slide {i}: ID {slide_id}, {len(page_elements)} element(s)")
|
96
|
-
|
96
|
+
|
97
97
|
confirmation_message = f"""Presentation Details for {user_google_email}:
|
98
98
|
- Title: {title}
|
99
99
|
- Presentation ID: {presentation_id}
|
@@ -103,14 +103,14 @@ async def get_presentation(
|
|
103
103
|
|
104
104
|
Slides Breakdown:
|
105
105
|
{chr(10).join(slides_info) if slides_info else ' No slides found'}"""
|
106
|
-
|
106
|
+
|
107
107
|
logger.info(f"Presentation retrieved successfully for {user_google_email}")
|
108
108
|
return confirmation_message
|
109
109
|
|
110
110
|
|
111
111
|
@server.tool()
|
112
|
-
@require_google_service("slides", "slides")
|
113
112
|
@handle_http_errors("batch_update_presentation")
|
113
|
+
@require_google_service("slides", "slides")
|
114
114
|
async def batch_update_presentation(
|
115
115
|
service,
|
116
116
|
user_google_email: str,
|
@@ -133,22 +133,22 @@ async def batch_update_presentation(
|
|
133
133
|
body = {
|
134
134
|
'requests': requests
|
135
135
|
}
|
136
|
-
|
136
|
+
|
137
137
|
result = await asyncio.to_thread(
|
138
138
|
service.presentations().batchUpdate(
|
139
139
|
presentationId=presentation_id,
|
140
140
|
body=body
|
141
141
|
).execute
|
142
142
|
)
|
143
|
-
|
143
|
+
|
144
144
|
replies = result.get('replies', [])
|
145
|
-
|
145
|
+
|
146
146
|
confirmation_message = f"""Batch Update Completed for {user_google_email}:
|
147
147
|
- Presentation ID: {presentation_id}
|
148
148
|
- URL: https://docs.google.com/presentation/d/{presentation_id}/edit
|
149
149
|
- Requests Applied: {len(requests)}
|
150
150
|
- Replies Received: {len(replies)}"""
|
151
|
-
|
151
|
+
|
152
152
|
if replies:
|
153
153
|
confirmation_message += "\n\nUpdate Results:"
|
154
154
|
for i, reply in enumerate(replies, 1):
|
@@ -160,14 +160,14 @@ async def batch_update_presentation(
|
|
160
160
|
confirmation_message += f"\n Request {i}: Created shape with ID {shape_id}"
|
161
161
|
else:
|
162
162
|
confirmation_message += f"\n Request {i}: Operation completed"
|
163
|
-
|
163
|
+
|
164
164
|
logger.info(f"Batch update completed successfully for {user_google_email}")
|
165
165
|
return confirmation_message
|
166
166
|
|
167
167
|
|
168
168
|
@server.tool()
|
169
|
+
@handle_http_errors("get_page", is_read_only=True)
|
169
170
|
@require_google_service("slides", "slides_read")
|
170
|
-
@handle_http_errors("get_page")
|
171
171
|
async def get_page(
|
172
172
|
service,
|
173
173
|
user_google_email: str,
|
@@ -193,10 +193,10 @@ async def get_page(
|
|
193
193
|
pageObjectId=page_object_id
|
194
194
|
).execute
|
195
195
|
)
|
196
|
-
|
196
|
+
|
197
197
|
page_type = result.get('pageType', 'Unknown')
|
198
198
|
page_elements = result.get('pageElements', [])
|
199
|
-
|
199
|
+
|
200
200
|
elements_info = []
|
201
201
|
for element in page_elements:
|
202
202
|
element_id = element.get('objectId', 'Unknown')
|
@@ -213,7 +213,7 @@ async def get_page(
|
|
213
213
|
elements_info.append(f" Line: ID {element_id}, Type: {line_type}")
|
214
214
|
else:
|
215
215
|
elements_info.append(f" Element: ID {element_id}, Type: Unknown")
|
216
|
-
|
216
|
+
|
217
217
|
confirmation_message = f"""Page Details for {user_google_email}:
|
218
218
|
- Presentation ID: {presentation_id}
|
219
219
|
- Page ID: {page_object_id}
|
@@ -222,14 +222,14 @@ async def get_page(
|
|
222
222
|
|
223
223
|
Page Elements:
|
224
224
|
{chr(10).join(elements_info) if elements_info else ' No elements found'}"""
|
225
|
-
|
225
|
+
|
226
226
|
logger.info(f"Page retrieved successfully for {user_google_email}")
|
227
227
|
return confirmation_message
|
228
228
|
|
229
229
|
|
230
230
|
@server.tool()
|
231
|
+
@handle_http_errors("get_page_thumbnail", is_read_only=True)
|
231
232
|
@require_google_service("slides", "slides_read")
|
232
|
-
@handle_http_errors("get_page_thumbnail")
|
233
233
|
async def get_page_thumbnail(
|
234
234
|
service,
|
235
235
|
user_google_email: str,
|
@@ -258,9 +258,9 @@ async def get_page_thumbnail(
|
|
258
258
|
thumbnailPropertiesImageSize=thumbnail_size
|
259
259
|
).execute
|
260
260
|
)
|
261
|
-
|
261
|
+
|
262
262
|
thumbnail_url = result.get('contentUrl', '')
|
263
|
-
|
263
|
+
|
264
264
|
confirmation_message = f"""Thumbnail Generated for {user_google_email}:
|
265
265
|
- Presentation ID: {presentation_id}
|
266
266
|
- Page ID: {page_object_id}
|
@@ -268,7 +268,7 @@ async def get_page_thumbnail(
|
|
268
268
|
- Thumbnail URL: {thumbnail_url}
|
269
269
|
|
270
270
|
You can view or download the thumbnail using the provided URL."""
|
271
|
-
|
271
|
+
|
272
272
|
logger.info(f"Thumbnail generated successfully for {user_google_email}")
|
273
273
|
return confirmation_message
|
274
274
|
|