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.
- google_workspace_mcp/models.py +486 -0
- google_workspace_mcp/services/calendar.py +14 -4
- google_workspace_mcp/services/drive.py +268 -18
- google_workspace_mcp/services/sheets_service.py +273 -35
- google_workspace_mcp/services/slides.py +242 -53
- google_workspace_mcp/tools/calendar.py +99 -88
- google_workspace_mcp/tools/docs_tools.py +67 -33
- google_workspace_mcp/tools/drive.py +288 -25
- google_workspace_mcp/tools/gmail.py +95 -39
- google_workspace_mcp/tools/sheets_tools.py +112 -46
- google_workspace_mcp/tools/slides.py +317 -46
- {google_workspace_mcp-1.0.5.dist-info → google_workspace_mcp-1.2.0.dist-info}/METADATA +4 -3
- {google_workspace_mcp-1.0.5.dist-info → google_workspace_mcp-1.2.0.dist-info}/RECORD +15 -14
- {google_workspace_mcp-1.0.5.dist-info → google_workspace_mcp-1.2.0.dist-info}/WHEEL +0 -0
- {google_workspace_mcp-1.0.5.dist-info → google_workspace_mcp-1.2.0.dist-info}/entry_points.txt +0 -0
@@ -3,9 +3,14 @@ Google Calendar tool handlers for Google Workspace MCP.
|
|
3
3
|
"""
|
4
4
|
|
5
5
|
import logging
|
6
|
-
from typing import Any
|
7
6
|
|
8
7
|
from google_workspace_mcp.app import mcp # Import from central app module
|
8
|
+
from google_workspace_mcp.models import (
|
9
|
+
CalendarEventCreationOutput,
|
10
|
+
CalendarEventDeletionOutput,
|
11
|
+
CalendarEventDetailsOutput,
|
12
|
+
CalendarEventsOutput,
|
13
|
+
)
|
9
14
|
from google_workspace_mcp.services.calendar import CalendarService
|
10
15
|
|
11
16
|
logger = logging.getLogger(__name__)
|
@@ -50,80 +55,85 @@ async def calendar_get_events(
|
|
50
55
|
calendar_id: str = "primary",
|
51
56
|
max_results: int = 250,
|
52
57
|
show_deleted: bool = False,
|
53
|
-
) ->
|
58
|
+
) -> CalendarEventsOutput:
|
54
59
|
"""
|
55
60
|
Retrieve calendar events within a specified time range.
|
56
61
|
|
57
62
|
Args:
|
58
|
-
time_min: Start time
|
59
|
-
time_max: End time
|
60
|
-
calendar_id:
|
61
|
-
max_results: Maximum number of events to return
|
62
|
-
show_deleted: Whether to include deleted events
|
63
|
+
time_min: Start of time range (ISO datetime string)
|
64
|
+
time_max: End of time range (ISO datetime string)
|
65
|
+
calendar_id: Calendar identifier (default: 'primary')
|
66
|
+
max_results: Maximum number of events to return
|
67
|
+
show_deleted: Whether to include deleted events
|
63
68
|
|
64
69
|
Returns:
|
65
|
-
|
70
|
+
CalendarEventsOutput: Structured output containing the events data
|
66
71
|
"""
|
67
|
-
logger.info(f"Executing calendar_get_events tool
|
68
|
-
|
69
|
-
if not calendar_id:
|
70
|
-
raise ValueError("calendar_id parameter is required")
|
71
|
-
if not time_min:
|
72
|
-
raise ValueError("time_min parameter is required")
|
73
|
-
if not time_max:
|
74
|
-
raise ValueError("time_max parameter is required")
|
72
|
+
logger.info(f"Executing calendar_get_events tool for calendar {calendar_id}")
|
75
73
|
|
76
74
|
calendar_service = CalendarService()
|
77
|
-
|
78
|
-
calendar_id=calendar_id,
|
75
|
+
result = calendar_service.get_events(
|
79
76
|
time_min=time_min,
|
80
77
|
time_max=time_max,
|
78
|
+
calendar_id=calendar_id,
|
81
79
|
max_results=max_results,
|
82
80
|
show_deleted=show_deleted,
|
83
81
|
)
|
84
82
|
|
85
|
-
|
86
|
-
|
83
|
+
# Check for errors in the service result
|
84
|
+
if isinstance(result, dict) and result.get("error"):
|
85
|
+
raise ValueError(result.get("message", "Error retrieving calendar events"))
|
87
86
|
|
87
|
+
# Extract events list - the service returns a dict with 'items' key containing events
|
88
|
+
events = result.get("items", []) if isinstance(result, dict) else result
|
88
89
|
if not events:
|
89
|
-
|
90
|
+
events = []
|
90
91
|
|
91
|
-
# Return
|
92
|
-
return
|
92
|
+
# Return the Pydantic model instance
|
93
|
+
return CalendarEventsOutput(count=len(events), events=events)
|
93
94
|
|
94
95
|
|
95
96
|
@mcp.tool(
|
96
97
|
name="calendar_get_event_details",
|
97
98
|
description="Retrieves detailed information for a specific calendar event by its ID.",
|
98
99
|
)
|
99
|
-
async def calendar_get_event_details(
|
100
|
+
async def calendar_get_event_details(
|
101
|
+
event_id: str, calendar_id: str = "primary"
|
102
|
+
) -> CalendarEventDetailsOutput:
|
100
103
|
"""
|
101
|
-
Retrieves
|
104
|
+
Retrieves detailed information for a specific calendar event by its ID.
|
102
105
|
|
103
106
|
Args:
|
104
|
-
event_id: The
|
105
|
-
calendar_id:
|
107
|
+
event_id: The unique identifier of the event
|
108
|
+
calendar_id: Calendar identifier (default: 'primary')
|
106
109
|
|
107
110
|
Returns:
|
108
|
-
|
109
|
-
or an error message.
|
111
|
+
CalendarEventDetailsOutput: Structured output containing the event details
|
110
112
|
"""
|
111
|
-
logger.info(f"Executing calendar_get_event_details tool for
|
112
|
-
if not event_id or not event_id.strip():
|
113
|
-
raise ValueError("Event ID cannot be empty.")
|
114
|
-
if not calendar_id or not calendar_id.strip():
|
115
|
-
raise ValueError("Calendar ID cannot be empty.") # Ensure calendar_id is also validated
|
113
|
+
logger.info(f"Executing calendar_get_event_details tool for event {event_id}")
|
116
114
|
|
117
115
|
calendar_service = CalendarService()
|
118
|
-
|
116
|
+
result = calendar_service.get_event_details(
|
117
|
+
event_id=event_id, calendar_id=calendar_id
|
118
|
+
)
|
119
119
|
|
120
|
-
|
121
|
-
|
120
|
+
# Check for errors in the service result
|
121
|
+
if isinstance(result, dict) and result.get("error"):
|
122
|
+
raise ValueError(result.get("message", "Error retrieving event details"))
|
122
123
|
|
123
|
-
if not
|
124
|
+
if not result: # Should be caught by error dict check
|
124
125
|
raise ValueError(f"Failed to retrieve details for event '{event_id}'")
|
125
126
|
|
126
|
-
|
127
|
+
# Return the Pydantic model instance
|
128
|
+
return CalendarEventDetailsOutput(
|
129
|
+
id=result["id"],
|
130
|
+
summary=result.get("summary", ""),
|
131
|
+
start=result.get("start", {}),
|
132
|
+
end=result.get("end", {}),
|
133
|
+
description=result.get("description"),
|
134
|
+
attendees=result.get("attendees"),
|
135
|
+
location=result.get("location"),
|
136
|
+
)
|
127
137
|
|
128
138
|
|
129
139
|
@mcp.tool(
|
@@ -135,53 +145,59 @@ async def create_calendar_event(
|
|
135
145
|
start_time: str,
|
136
146
|
end_time: str,
|
137
147
|
calendar_id: str = "primary",
|
138
|
-
location: str
|
139
|
-
description: str
|
140
|
-
attendees: list[str]
|
148
|
+
location: str = None,
|
149
|
+
description: str = None,
|
150
|
+
attendees: list[str] = None,
|
141
151
|
send_notifications: bool = True,
|
142
|
-
timezone: str
|
143
|
-
) ->
|
152
|
+
timezone: str = None,
|
153
|
+
) -> CalendarEventCreationOutput:
|
144
154
|
"""
|
145
|
-
|
155
|
+
Creates a new event in a specified Google Calendar.
|
146
156
|
|
147
157
|
Args:
|
148
|
-
summary:
|
149
|
-
start_time:
|
150
|
-
end_time:
|
151
|
-
calendar_id: Calendar
|
152
|
-
location:
|
153
|
-
description:
|
154
|
-
attendees: List of attendee email addresses (optional)
|
155
|
-
send_notifications: Whether to send notifications
|
156
|
-
timezone: Timezone for the event (
|
158
|
+
summary: Event title/summary
|
159
|
+
start_time: Event start time (ISO datetime string)
|
160
|
+
end_time: Event end time (ISO datetime string)
|
161
|
+
calendar_id: Calendar identifier (default: 'primary')
|
162
|
+
location: Event location (optional)
|
163
|
+
description: Event description (optional)
|
164
|
+
attendees: List of attendee email addresses (optional)
|
165
|
+
send_notifications: Whether to send notifications (default: True)
|
166
|
+
timezone: Timezone for the event (optional)
|
157
167
|
|
158
168
|
Returns:
|
159
|
-
|
169
|
+
CalendarEventCreationOutput: Structured output containing the created event data
|
160
170
|
"""
|
161
|
-
logger.info(f"Executing create_calendar_event
|
162
|
-
if not summary or not start_time or not end_time:
|
163
|
-
raise ValueError("Summary, start_time, and end_time are required")
|
171
|
+
logger.info(f"Executing create_calendar_event tool for summary: {summary}")
|
164
172
|
|
165
173
|
calendar_service = CalendarService()
|
166
174
|
result = calendar_service.create_event(
|
167
175
|
summary=summary,
|
168
176
|
start_time=start_time,
|
169
177
|
end_time=end_time,
|
178
|
+
calendar_id=calendar_id,
|
170
179
|
location=location,
|
171
180
|
description=description,
|
172
181
|
attendees=attendees,
|
173
182
|
send_notifications=send_notifications,
|
174
183
|
timezone=timezone,
|
175
|
-
calendar_id=calendar_id,
|
176
184
|
)
|
177
185
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
186
|
+
# Check for errors in the service result
|
187
|
+
if isinstance(result, dict) and result.get("error"):
|
188
|
+
raise ValueError(result.get("message", "Error creating calendar event"))
|
189
|
+
|
190
|
+
if not result:
|
191
|
+
raise ValueError("Error creating calendar event")
|
183
192
|
|
184
|
-
|
193
|
+
# Return the Pydantic model instance
|
194
|
+
return CalendarEventCreationOutput(
|
195
|
+
id=result["id"],
|
196
|
+
html_link=result.get("htmlLink", ""),
|
197
|
+
summary=result.get("summary", summary),
|
198
|
+
start=result.get("start", {}),
|
199
|
+
end=result.get("end", {}),
|
200
|
+
)
|
185
201
|
|
186
202
|
|
187
203
|
@mcp.tool(
|
@@ -192,38 +208,33 @@ async def delete_calendar_event(
|
|
192
208
|
event_id: str,
|
193
209
|
calendar_id: str = "primary",
|
194
210
|
send_notifications: bool = True,
|
195
|
-
) ->
|
211
|
+
) -> CalendarEventDeletionOutput:
|
196
212
|
"""
|
197
|
-
|
213
|
+
Deletes an event from Google Calendar by its event ID.
|
198
214
|
|
199
215
|
Args:
|
200
|
-
event_id: The
|
201
|
-
calendar_id: Calendar
|
202
|
-
send_notifications: Whether to send
|
216
|
+
event_id: The unique identifier of the event to delete
|
217
|
+
calendar_id: Calendar identifier (default: 'primary')
|
218
|
+
send_notifications: Whether to send notifications (default: True)
|
203
219
|
|
204
220
|
Returns:
|
205
|
-
|
221
|
+
CalendarEventDeletionOutput: Structured output containing the deletion result
|
206
222
|
"""
|
207
|
-
logger.info(f"Executing delete_calendar_event
|
208
|
-
if not event_id:
|
209
|
-
raise ValueError("Event ID is required")
|
223
|
+
logger.info(f"Executing delete_calendar_event tool for event {event_id}")
|
210
224
|
|
211
225
|
calendar_service = CalendarService()
|
212
|
-
|
226
|
+
result = calendar_service.delete_event(
|
213
227
|
event_id=event_id,
|
214
|
-
send_notifications=send_notifications,
|
215
228
|
calendar_id=calendar_id,
|
229
|
+
send_notifications=send_notifications,
|
216
230
|
)
|
217
231
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
"message": f"Event with ID '{event_id}' deleted successfully from calendar '{calendar_id}'.",
|
228
|
-
"success": True,
|
229
|
-
}
|
232
|
+
# Check for errors in the service result
|
233
|
+
if isinstance(result, dict) and result.get("error"):
|
234
|
+
raise ValueError(result.get("message", "Error deleting calendar event"))
|
235
|
+
|
236
|
+
# Return the Pydantic model instance
|
237
|
+
return CalendarEventDeletionOutput(
|
238
|
+
message=f"Event with ID '{event_id}' deleted successfully from calendar '{calendar_id}'.",
|
239
|
+
success=True,
|
240
|
+
)
|
@@ -3,9 +3,16 @@ Google Docs tool handlers for Google Workspace MCP.
|
|
3
3
|
"""
|
4
4
|
|
5
5
|
import logging
|
6
|
-
from typing import Any
|
7
6
|
|
8
7
|
from google_workspace_mcp.app import mcp
|
8
|
+
from google_workspace_mcp.models import (
|
9
|
+
DocumentBatchUpdateOutput,
|
10
|
+
DocumentContentOutput,
|
11
|
+
DocumentCreationOutput,
|
12
|
+
DocumentImageInsertOutput,
|
13
|
+
DocumentMetadataOutput,
|
14
|
+
DocumentUpdateOutput,
|
15
|
+
)
|
9
16
|
from google_workspace_mcp.services.docs_service import DocsService
|
10
17
|
|
11
18
|
logger = logging.getLogger(__name__)
|
@@ -15,7 +22,7 @@ logger = logging.getLogger(__name__)
|
|
15
22
|
name="docs_create_document",
|
16
23
|
description="Creates a new Google Document with a specified title.",
|
17
24
|
)
|
18
|
-
async def docs_create_document(title: str) ->
|
25
|
+
async def docs_create_document(title: str) -> DocumentCreationOutput:
|
19
26
|
"""
|
20
27
|
Creates a new, empty Google Document.
|
21
28
|
|
@@ -23,8 +30,7 @@ async def docs_create_document(title: str) -> dict[str, Any]:
|
|
23
30
|
title: The title for the new Google Document.
|
24
31
|
|
25
32
|
Returns:
|
26
|
-
|
27
|
-
or an error message.
|
33
|
+
DocumentCreationOutput containing document_id, title, and document_link.
|
28
34
|
"""
|
29
35
|
logger.info(f"Executing docs_create_document tool with title: '{title}'")
|
30
36
|
if not title or not title.strip():
|
@@ -41,14 +47,18 @@ async def docs_create_document(title: str) -> dict[str, Any]:
|
|
41
47
|
f"Failed to create document '{title}' or did not receive a document ID."
|
42
48
|
)
|
43
49
|
|
44
|
-
return
|
50
|
+
return DocumentCreationOutput(
|
51
|
+
document_id=result["document_id"],
|
52
|
+
title=result["title"],
|
53
|
+
document_link=result["document_link"],
|
54
|
+
)
|
45
55
|
|
46
56
|
|
47
57
|
@mcp.tool(
|
48
58
|
name="docs_get_document_metadata",
|
49
59
|
description="Retrieves metadata (like title and ID) for a Google Document.",
|
50
60
|
)
|
51
|
-
async def docs_get_document_metadata(document_id: str) ->
|
61
|
+
async def docs_get_document_metadata(document_id: str) -> DocumentMetadataOutput:
|
52
62
|
"""
|
53
63
|
Retrieves metadata for a specific Google Document.
|
54
64
|
|
@@ -56,8 +66,7 @@ async def docs_get_document_metadata(document_id: str) -> dict[str, Any]:
|
|
56
66
|
document_id: The ID of the Google Document.
|
57
67
|
|
58
68
|
Returns:
|
59
|
-
|
60
|
-
or an error message.
|
69
|
+
DocumentMetadataOutput containing document_id, title, and document_link.
|
61
70
|
"""
|
62
71
|
logger.info(
|
63
72
|
f"Executing docs_get_document_metadata tool for document_id: '{document_id}'"
|
@@ -74,14 +83,18 @@ async def docs_get_document_metadata(document_id: str) -> dict[str, Any]:
|
|
74
83
|
if not result or not result.get("document_id"):
|
75
84
|
raise ValueError(f"Failed to retrieve metadata for document '{document_id}'.")
|
76
85
|
|
77
|
-
return
|
86
|
+
return DocumentMetadataOutput(
|
87
|
+
document_id=result["document_id"],
|
88
|
+
title=result["title"],
|
89
|
+
document_link=result["document_link"],
|
90
|
+
)
|
78
91
|
|
79
92
|
|
80
93
|
@mcp.tool(
|
81
94
|
name="docs_get_content_as_markdown",
|
82
95
|
description="Retrieves the content of a Google Document, attempting to convert it to Markdown. Note: Direct Markdown export from Google Docs via Drive API is not officially guaranteed for all document complexities and may result in errors or suboptimal formatting. For critical conversions, consider exporting as HTML and using a dedicated Markdown conversion library.",
|
83
96
|
)
|
84
|
-
async def docs_get_content_as_markdown(document_id: str) ->
|
97
|
+
async def docs_get_content_as_markdown(document_id: str) -> DocumentContentOutput:
|
85
98
|
"""
|
86
99
|
Retrieves the main textual content of a Google Document, converted to Markdown.
|
87
100
|
|
@@ -89,8 +102,7 @@ async def docs_get_content_as_markdown(document_id: str) -> dict[str, Any]:
|
|
89
102
|
document_id: The ID of the Google Document.
|
90
103
|
|
91
104
|
Returns:
|
92
|
-
|
93
|
-
or an error message.
|
105
|
+
DocumentContentOutput containing document_id and markdown_content.
|
94
106
|
"""
|
95
107
|
logger.info(
|
96
108
|
f"Executing docs_get_content_as_markdown tool for document_id: '{document_id}'"
|
@@ -111,7 +123,9 @@ async def docs_get_content_as_markdown(document_id: str) -> dict[str, Any]:
|
|
111
123
|
f"Failed to retrieve Markdown content for document '{document_id}'."
|
112
124
|
)
|
113
125
|
|
114
|
-
return
|
126
|
+
return DocumentContentOutput(
|
127
|
+
document_id=result["document_id"], markdown_content=result["markdown_content"]
|
128
|
+
)
|
115
129
|
|
116
130
|
|
117
131
|
@mcp.tool(
|
@@ -120,7 +134,7 @@ async def docs_get_content_as_markdown(document_id: str) -> dict[str, Any]:
|
|
120
134
|
)
|
121
135
|
async def docs_append_text(
|
122
136
|
document_id: str, text: str, ensure_newline: bool = True
|
123
|
-
) ->
|
137
|
+
) -> DocumentUpdateOutput:
|
124
138
|
"""
|
125
139
|
Appends the given text to the end of the specified Google Document.
|
126
140
|
|
@@ -131,7 +145,7 @@ async def docs_append_text(
|
|
131
145
|
if the document is not empty, to ensure the new text starts on a new line. (Default: True)
|
132
146
|
|
133
147
|
Returns:
|
134
|
-
|
148
|
+
DocumentUpdateOutput indicating success or failure.
|
135
149
|
"""
|
136
150
|
logger.info(f"Executing docs_append_text tool for document_id: '{document_id}'")
|
137
151
|
if not document_id or not document_id.strip():
|
@@ -149,7 +163,11 @@ async def docs_append_text(
|
|
149
163
|
if not result or not result.get("success"):
|
150
164
|
raise ValueError(f"Failed to append text to document '{document_id}'.")
|
151
165
|
|
152
|
-
return
|
166
|
+
return DocumentUpdateOutput(
|
167
|
+
success=result["success"],
|
168
|
+
message=result.get("message", "Text appended successfully"),
|
169
|
+
updated_range=result.get("updated_range"),
|
170
|
+
)
|
153
171
|
|
154
172
|
|
155
173
|
@mcp.tool(
|
@@ -158,7 +176,7 @@ async def docs_append_text(
|
|
158
176
|
)
|
159
177
|
async def docs_prepend_text(
|
160
178
|
document_id: str, text: str, ensure_newline: bool = True
|
161
|
-
) ->
|
179
|
+
) -> DocumentUpdateOutput:
|
162
180
|
"""
|
163
181
|
Prepends the given text to the beginning of the specified Google Document.
|
164
182
|
|
@@ -169,7 +187,7 @@ async def docs_prepend_text(
|
|
169
187
|
if the document was not empty, to ensure existing content starts on a new line. (Default: True)
|
170
188
|
|
171
189
|
Returns:
|
172
|
-
|
190
|
+
DocumentUpdateOutput indicating success or failure.
|
173
191
|
"""
|
174
192
|
logger.info(f"Executing docs_prepend_text tool for document_id: '{document_id}'")
|
175
193
|
if not document_id or not document_id.strip():
|
@@ -187,7 +205,11 @@ async def docs_prepend_text(
|
|
187
205
|
if not result or not result.get("success"):
|
188
206
|
raise ValueError(f"Failed to prepend text to document '{document_id}'.")
|
189
207
|
|
190
|
-
return
|
208
|
+
return DocumentUpdateOutput(
|
209
|
+
success=result["success"],
|
210
|
+
message=result.get("message", "Text prepended successfully"),
|
211
|
+
updated_range=result.get("updated_range"),
|
212
|
+
)
|
191
213
|
|
192
214
|
|
193
215
|
@mcp.tool(
|
@@ -196,7 +218,7 @@ async def docs_prepend_text(
|
|
196
218
|
)
|
197
219
|
async def docs_insert_text(
|
198
220
|
document_id: str, text: str, index: int | None = None, segment_id: str | None = None
|
199
|
-
) ->
|
221
|
+
) -> DocumentUpdateOutput:
|
200
222
|
"""
|
201
223
|
Inserts the given text at a specific location within the specified Google Document.
|
202
224
|
|
@@ -204,14 +226,14 @@ async def docs_insert_text(
|
|
204
226
|
document_id: The ID of the Google Document.
|
205
227
|
text: The text to insert.
|
206
228
|
index: Optional. The 0-based index where the text should be inserted within the segment.
|
207
|
-
|
208
|
-
|
209
|
-
|
229
|
+
For the main body, an index of 1 typically refers to the beginning of the content.
|
230
|
+
Consult Google Docs API documentation for details on indexing if precise placement is needed.
|
231
|
+
If omitted, defaults to a sensible starting position (e.g., beginning of the body).
|
210
232
|
segment_id: Optional. The ID of a specific document segment (e.g., header, footer).
|
211
233
|
If omitted, the text is inserted into the main document body.
|
212
234
|
|
213
235
|
Returns:
|
214
|
-
|
236
|
+
DocumentUpdateOutput indicating success or failure.
|
215
237
|
"""
|
216
238
|
logger.info(
|
217
239
|
f"Executing docs_insert_text tool for document_id: '{document_id}' at index: {index}"
|
@@ -231,14 +253,20 @@ async def docs_insert_text(
|
|
231
253
|
if not result or not result.get("success"):
|
232
254
|
raise ValueError(f"Failed to insert text into document '{document_id}'.")
|
233
255
|
|
234
|
-
return
|
256
|
+
return DocumentUpdateOutput(
|
257
|
+
success=result["success"],
|
258
|
+
message=result.get("message", "Text inserted successfully"),
|
259
|
+
updated_range=result.get("updated_range"),
|
260
|
+
)
|
235
261
|
|
236
262
|
|
237
263
|
@mcp.tool(
|
238
264
|
name="docs_batch_update",
|
239
265
|
description="Applies a list of raw Google Docs API update requests to a document. For advanced users familiar with Docs API request structures.",
|
240
266
|
)
|
241
|
-
async def docs_batch_update(
|
267
|
+
async def docs_batch_update(
|
268
|
+
document_id: str, requests: list[dict]
|
269
|
+
) -> DocumentBatchUpdateOutput:
|
242
270
|
"""
|
243
271
|
Applies a list of Google Docs API update requests to the specified document.
|
244
272
|
This is an advanced tool; requests must conform to the Google Docs API format.
|
@@ -247,11 +275,10 @@ async def docs_batch_update(document_id: str, requests: list[dict]) -> dict[str,
|
|
247
275
|
Args:
|
248
276
|
document_id: The ID of the Google Document.
|
249
277
|
requests: A list of request objects (as dictionaries) to apply.
|
250
|
-
|
278
|
+
Example request: {"insertText": {"location": {"index": 1}, "text": "Hello"}}
|
251
279
|
|
252
280
|
Returns:
|
253
|
-
|
254
|
-
or an error message.
|
281
|
+
DocumentBatchUpdateOutput containing the API response from the batchUpdate call.
|
255
282
|
"""
|
256
283
|
logger.info(
|
257
284
|
f"Executing docs_batch_update tool for document_id: '{document_id}' with {len(requests)} requests."
|
@@ -274,7 +301,10 @@ async def docs_batch_update(document_id: str, requests: list[dict]) -> dict[str,
|
|
274
301
|
if not result: # Should be caught by error dict check
|
275
302
|
raise ValueError(f"Failed to execute batch update on document '{document_id}'.")
|
276
303
|
|
277
|
-
return
|
304
|
+
return DocumentBatchUpdateOutput(
|
305
|
+
document_id=result.get("documentId", document_id),
|
306
|
+
replies=result.get("replies", []),
|
307
|
+
)
|
278
308
|
|
279
309
|
|
280
310
|
@mcp.tool(
|
@@ -287,7 +317,7 @@ async def docs_insert_image(
|
|
287
317
|
index: int,
|
288
318
|
width: float | None = None,
|
289
319
|
height: float | None = None,
|
290
|
-
) ->
|
320
|
+
) -> DocumentImageInsertOutput:
|
291
321
|
"""
|
292
322
|
Inserts an image into a Google Document from a URL at a specific index.
|
293
323
|
|
@@ -299,7 +329,7 @@ async def docs_insert_image(
|
|
299
329
|
height: Optional height of the image in points (PT).
|
300
330
|
|
301
331
|
Returns:
|
302
|
-
|
332
|
+
DocumentImageInsertOutput containing the inserted image details.
|
303
333
|
"""
|
304
334
|
logger.info(
|
305
335
|
f"Executing docs_insert_image tool for document_id: '{document_id}' at index: {index}"
|
@@ -335,4 +365,8 @@ async def docs_insert_image(
|
|
335
365
|
if not result or not result.get("success"):
|
336
366
|
raise ValueError(f"Failed to insert image into document '{document_id}'.")
|
337
367
|
|
338
|
-
return
|
368
|
+
return DocumentImageInsertOutput(
|
369
|
+
success=result["success"],
|
370
|
+
image_id=result.get("image_id"),
|
371
|
+
message=result.get("message", "Image inserted successfully"),
|
372
|
+
)
|