google-workspace-mcp 1.1.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/__main__.py +5 -6
- google_workspace_mcp/models.py +486 -0
- google_workspace_mcp/services/calendar.py +14 -4
- google_workspace_mcp/services/drive.py +237 -14
- google_workspace_mcp/services/sheets_service.py +273 -35
- google_workspace_mcp/services/slides.py +42 -1829
- google_workspace_mcp/tools/calendar.py +116 -100
- google_workspace_mcp/tools/docs_tools.py +99 -57
- google_workspace_mcp/tools/drive.py +112 -92
- google_workspace_mcp/tools/gmail.py +131 -66
- google_workspace_mcp/tools/sheets_tools.py +137 -64
- google_workspace_mcp/tools/slides.py +295 -743
- {google_workspace_mcp-1.1.5.dist-info → google_workspace_mcp-1.2.0.dist-info}/METADATA +3 -2
- {google_workspace_mcp-1.1.5.dist-info → google_workspace_mcp-1.2.0.dist-info}/RECORD +16 -17
- google_workspace_mcp/tools/add_image.py +0 -1781
- google_workspace_mcp/utils/unit_conversion.py +0 -201
- {google_workspace_mcp-1.1.5.dist-info → google_workspace_mcp-1.2.0.dist-info}/WHEEL +0 -0
- {google_workspace_mcp-1.1.5.dist-info → google_workspace_mcp-1.2.0.dist-info}/entry_points.txt +0 -0
@@ -6,15 +6,25 @@ import logging
|
|
6
6
|
from typing import Any
|
7
7
|
|
8
8
|
from google_workspace_mcp.app import mcp
|
9
|
+
from google_workspace_mcp.models import (
|
10
|
+
SheetsAddSheetOutput,
|
11
|
+
SheetsAppendOutput,
|
12
|
+
SheetsClearOutput,
|
13
|
+
SheetsCreationOutput,
|
14
|
+
SheetsDeleteSheetOutput,
|
15
|
+
SheetsReadOutput,
|
16
|
+
SheetsWriteOutput,
|
17
|
+
)
|
9
18
|
from google_workspace_mcp.services.sheets_service import SheetsService
|
10
19
|
|
11
20
|
logger = logging.getLogger(__name__)
|
12
21
|
|
13
22
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
23
|
+
@mcp.tool(
|
24
|
+
name="sheets_create_spreadsheet",
|
25
|
+
description="Creates a new Google Spreadsheet with a specified title.",
|
26
|
+
)
|
27
|
+
async def sheets_create_spreadsheet(title: str) -> SheetsCreationOutput:
|
18
28
|
"""
|
19
29
|
Creates a new, empty Google Spreadsheet.
|
20
30
|
|
@@ -22,8 +32,7 @@ async def sheets_create_spreadsheet(title: str) -> dict[str, Any]:
|
|
22
32
|
title: The title for the new Google Spreadsheet.
|
23
33
|
|
24
34
|
Returns:
|
25
|
-
|
26
|
-
of the created spreadsheet, or an error message.
|
35
|
+
SheetsCreationOutput containing the spreadsheet_id, title, and spreadsheet_url.
|
27
36
|
"""
|
28
37
|
logger.info(f"Executing sheets_create_spreadsheet tool with title: '{title}'")
|
29
38
|
if not title or not title.strip():
|
@@ -36,15 +45,22 @@ async def sheets_create_spreadsheet(title: str) -> dict[str, Any]:
|
|
36
45
|
raise ValueError(result.get("message", "Error creating spreadsheet"))
|
37
46
|
|
38
47
|
if not result or not result.get("spreadsheet_id"):
|
39
|
-
raise ValueError(
|
40
|
-
|
41
|
-
|
48
|
+
raise ValueError(
|
49
|
+
f"Failed to create spreadsheet '{title}' or did not receive a spreadsheet ID."
|
50
|
+
)
|
51
|
+
|
52
|
+
return SheetsCreationOutput(
|
53
|
+
spreadsheet_id=result["spreadsheet_id"],
|
54
|
+
title=result["title"],
|
55
|
+
spreadsheet_url=result["spreadsheet_url"],
|
56
|
+
)
|
42
57
|
|
43
58
|
|
44
59
|
@mcp.tool(
|
45
60
|
name="sheets_read_range",
|
61
|
+
description="Reads data from a specified range in a Google Spreadsheet (e.g., 'Sheet1!A1:B5').",
|
46
62
|
)
|
47
|
-
async def sheets_read_range(spreadsheet_id: str, range_a1: str) ->
|
63
|
+
async def sheets_read_range(spreadsheet_id: str, range_a1: str) -> SheetsReadOutput:
|
48
64
|
"""
|
49
65
|
Reads data from a given A1 notation range in a Google Spreadsheet.
|
50
66
|
|
@@ -54,10 +70,11 @@ async def sheets_read_range(spreadsheet_id: str, range_a1: str) -> dict[str, Any
|
|
54
70
|
to the first visible sheet or if sheet name is part of it).
|
55
71
|
|
56
72
|
Returns:
|
57
|
-
|
58
|
-
or an error message.
|
73
|
+
SheetsReadOutput containing the range and cell values.
|
59
74
|
"""
|
60
|
-
logger.info(
|
75
|
+
logger.info(
|
76
|
+
f"Executing sheets_read_range tool for spreadsheet_id: '{spreadsheet_id}', range: '{range_a1}'"
|
77
|
+
)
|
61
78
|
if not spreadsheet_id or not spreadsheet_id.strip():
|
62
79
|
raise ValueError("Spreadsheet ID cannot be empty.")
|
63
80
|
if not range_a1 or not range_a1.strip():
|
@@ -69,21 +86,30 @@ async def sheets_read_range(spreadsheet_id: str, range_a1: str) -> dict[str, Any
|
|
69
86
|
if isinstance(result, dict) and result.get("error"):
|
70
87
|
raise ValueError(result.get("message", "Error reading range from spreadsheet"))
|
71
88
|
|
72
|
-
if
|
73
|
-
|
74
|
-
|
75
|
-
|
89
|
+
if (
|
90
|
+
not result or "values" not in result
|
91
|
+
): # Check for 'values' as it's key for successful read
|
92
|
+
raise ValueError(
|
93
|
+
f"Failed to read range '{range_a1}' from spreadsheet '{spreadsheet_id}'."
|
94
|
+
)
|
95
|
+
|
96
|
+
return SheetsReadOutput(
|
97
|
+
range=result.get("range", range_a1),
|
98
|
+
values=result.get("values", []),
|
99
|
+
major_dimension=result.get("major_dimension", "ROWS"),
|
100
|
+
)
|
76
101
|
|
77
102
|
|
78
|
-
|
79
|
-
|
80
|
-
|
103
|
+
@mcp.tool(
|
104
|
+
name="sheets_write_range",
|
105
|
+
description="Writes data to a specified range in a Google Spreadsheet (e.g., 'Sheet1!A1:B5').",
|
106
|
+
)
|
81
107
|
async def sheets_write_range(
|
82
108
|
spreadsheet_id: str,
|
83
109
|
range_a1: str,
|
84
110
|
values: list[list[Any]],
|
85
111
|
value_input_option: str = "USER_ENTERED",
|
86
|
-
) ->
|
112
|
+
) -> SheetsWriteOutput:
|
87
113
|
"""
|
88
114
|
Writes data (list of lists) to a given A1 notation range in a Google Spreadsheet.
|
89
115
|
|
@@ -96,10 +122,11 @@ async def sheets_write_range(
|
|
96
122
|
"USER_ENTERED": Values parsed as if typed by user (e.g., formulas).
|
97
123
|
"RAW": Values taken literally. (Default: "USER_ENTERED")
|
98
124
|
Returns:
|
99
|
-
|
100
|
-
or an error message.
|
125
|
+
SheetsWriteOutput detailing the update.
|
101
126
|
"""
|
102
|
-
logger.info(
|
127
|
+
logger.info(
|
128
|
+
f"Executing sheets_write_range tool for spreadsheet_id: '{spreadsheet_id}', range: '{range_a1}'"
|
129
|
+
)
|
103
130
|
if not spreadsheet_id or not spreadsheet_id.strip():
|
104
131
|
raise ValueError("Spreadsheet ID cannot be empty.")
|
105
132
|
if not range_a1 or not range_a1.strip():
|
@@ -121,21 +148,29 @@ async def sheets_write_range(
|
|
121
148
|
raise ValueError(result.get("message", "Error writing to range in spreadsheet"))
|
122
149
|
|
123
150
|
if not result or not result.get("updated_range"):
|
124
|
-
raise ValueError(
|
125
|
-
|
126
|
-
|
151
|
+
raise ValueError(
|
152
|
+
f"Failed to write to range '{range_a1}' in spreadsheet '{spreadsheet_id}'."
|
153
|
+
)
|
154
|
+
|
155
|
+
return SheetsWriteOutput(
|
156
|
+
updated_range=result["updated_range"],
|
157
|
+
updated_rows=result.get("updated_rows", 0),
|
158
|
+
updated_columns=result.get("updated_columns", 0),
|
159
|
+
updated_cells=result.get("updated_cells", 0),
|
160
|
+
)
|
127
161
|
|
128
162
|
|
129
|
-
|
130
|
-
|
131
|
-
|
163
|
+
@mcp.tool(
|
164
|
+
name="sheets_append_rows",
|
165
|
+
description="Appends rows of data to a sheet or table in a Google Spreadsheet (e.g., to 'Sheet1').",
|
166
|
+
)
|
132
167
|
async def sheets_append_rows(
|
133
168
|
spreadsheet_id: str,
|
134
169
|
range_a1: str,
|
135
170
|
values: list[list[Any]],
|
136
171
|
value_input_option: str = "USER_ENTERED",
|
137
172
|
insert_data_option: str = "INSERT_ROWS",
|
138
|
-
) ->
|
173
|
+
) -> SheetsAppendOutput:
|
139
174
|
"""
|
140
175
|
Appends rows of data to a sheet or table in a Google Spreadsheet.
|
141
176
|
|
@@ -148,10 +183,11 @@ async def sheets_append_rows(
|
|
148
183
|
insert_data_option: How new data should be inserted ("INSERT_ROWS" or "OVERWRITE"). Default: "INSERT_ROWS".
|
149
184
|
|
150
185
|
Returns:
|
151
|
-
|
152
|
-
or an error message.
|
186
|
+
SheetsAppendOutput detailing the append operation.
|
153
187
|
"""
|
154
|
-
logger.info(
|
188
|
+
logger.info(
|
189
|
+
f"Executing sheets_append_rows tool for spreadsheet_id: '{spreadsheet_id}', range: '{range_a1}'"
|
190
|
+
)
|
155
191
|
if not spreadsheet_id or not spreadsheet_id.strip():
|
156
192
|
raise ValueError("Spreadsheet ID cannot be empty.")
|
157
193
|
if not range_a1 or not range_a1.strip():
|
@@ -163,7 +199,9 @@ async def sheets_append_rows(
|
|
163
199
|
if value_input_option not in ["USER_ENTERED", "RAW"]:
|
164
200
|
raise ValueError("value_input_option must be either 'USER_ENTERED' or 'RAW'.")
|
165
201
|
if insert_data_option not in ["INSERT_ROWS", "OVERWRITE"]:
|
166
|
-
raise ValueError(
|
202
|
+
raise ValueError(
|
203
|
+
"insert_data_option must be either 'INSERT_ROWS' or 'OVERWRITE'."
|
204
|
+
)
|
167
205
|
|
168
206
|
sheets_service = SheetsService()
|
169
207
|
result = sheets_service.append_rows(
|
@@ -178,15 +216,22 @@ async def sheets_append_rows(
|
|
178
216
|
raise ValueError(result.get("message", "Error appending rows to spreadsheet"))
|
179
217
|
|
180
218
|
if not result: # Check for empty or None result as well
|
181
|
-
raise ValueError(
|
219
|
+
raise ValueError(
|
220
|
+
f"Failed to append rows to range '{range_a1}' in spreadsheet '{spreadsheet_id}'."
|
221
|
+
)
|
182
222
|
|
183
|
-
return
|
223
|
+
return SheetsAppendOutput(
|
224
|
+
spreadsheet_id=spreadsheet_id,
|
225
|
+
table_range=result.get("table_range", range_a1),
|
226
|
+
updates=result.get("updates", {}),
|
227
|
+
)
|
184
228
|
|
185
229
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
230
|
+
@mcp.tool(
|
231
|
+
name="sheets_clear_range",
|
232
|
+
description="Clears values from a specified range in a Google Spreadsheet (e.g., 'Sheet1!A1:B5').",
|
233
|
+
)
|
234
|
+
async def sheets_clear_range(spreadsheet_id: str, range_a1: str) -> SheetsClearOutput:
|
190
235
|
"""
|
191
236
|
Clears all values from a given A1 notation range in a Google Spreadsheet.
|
192
237
|
Note: This usually clears only the values, not formatting.
|
@@ -196,30 +241,39 @@ async def sheets_clear_range(spreadsheet_id: str, range_a1: str) -> dict[str, An
|
|
196
241
|
range_a1: The A1 notation of the range to clear (e.g., "Sheet1!A1:B5").
|
197
242
|
|
198
243
|
Returns:
|
199
|
-
|
244
|
+
SheetsClearOutput confirming the cleared range.
|
200
245
|
"""
|
201
|
-
logger.info(
|
246
|
+
logger.info(
|
247
|
+
f"Executing sheets_clear_range tool for spreadsheet_id: '{spreadsheet_id}', range: '{range_a1}'"
|
248
|
+
)
|
202
249
|
if not spreadsheet_id or not spreadsheet_id.strip():
|
203
250
|
raise ValueError("Spreadsheet ID cannot be empty.")
|
204
251
|
if not range_a1 or not range_a1.strip():
|
205
252
|
raise ValueError("Range (A1 notation) cannot be empty.")
|
206
253
|
|
207
254
|
sheets_service = SheetsService()
|
208
|
-
result = sheets_service.clear_range(
|
255
|
+
result = sheets_service.clear_range(
|
256
|
+
spreadsheet_id=spreadsheet_id, range_a1=range_a1
|
257
|
+
)
|
209
258
|
|
210
259
|
if isinstance(result, dict) and result.get("error"):
|
211
260
|
raise ValueError(result.get("message", "Error clearing range in spreadsheet"))
|
212
261
|
|
213
262
|
if not result or not result.get("cleared_range"):
|
214
|
-
raise ValueError(
|
263
|
+
raise ValueError(
|
264
|
+
f"Failed to clear range '{range_a1}' in spreadsheet '{spreadsheet_id}'."
|
265
|
+
)
|
215
266
|
|
216
|
-
return
|
267
|
+
return SheetsClearOutput(
|
268
|
+
cleared_range=result["cleared_range"], spreadsheet_id=spreadsheet_id
|
269
|
+
)
|
217
270
|
|
218
271
|
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
272
|
+
@mcp.tool(
|
273
|
+
name="sheets_add_sheet",
|
274
|
+
description="Adds a new sheet (tab) to an existing Google Spreadsheet.",
|
275
|
+
)
|
276
|
+
async def sheets_add_sheet(spreadsheet_id: str, title: str) -> SheetsAddSheetOutput:
|
223
277
|
"""
|
224
278
|
Adds a new sheet with the given title to the specified spreadsheet.
|
225
279
|
|
@@ -228,10 +282,11 @@ async def sheets_add_sheet(spreadsheet_id: str, title: str) -> dict[str, Any]:
|
|
228
282
|
title: The title for the new sheet.
|
229
283
|
|
230
284
|
Returns:
|
231
|
-
|
232
|
-
or an error message.
|
285
|
+
SheetsAddSheetOutput containing properties of the newly created sheet.
|
233
286
|
"""
|
234
|
-
logger.info(
|
287
|
+
logger.info(
|
288
|
+
f"Executing sheets_add_sheet tool for spreadsheet_id: '{spreadsheet_id}', title: '{title}'"
|
289
|
+
)
|
235
290
|
if not spreadsheet_id or not spreadsheet_id.strip():
|
236
291
|
raise ValueError("Spreadsheet ID cannot be empty.")
|
237
292
|
if not title or not title.strip():
|
@@ -244,15 +299,22 @@ async def sheets_add_sheet(spreadsheet_id: str, title: str) -> dict[str, Any]:
|
|
244
299
|
raise ValueError(result.get("message", "Error adding sheet to spreadsheet"))
|
245
300
|
|
246
301
|
if not result or not result.get("sheet_properties"):
|
247
|
-
raise ValueError(
|
302
|
+
raise ValueError(
|
303
|
+
f"Failed to add sheet '{title}' to spreadsheet '{spreadsheet_id}'."
|
304
|
+
)
|
248
305
|
|
249
|
-
return
|
306
|
+
return SheetsAddSheetOutput(
|
307
|
+
sheet_properties=result["sheet_properties"], spreadsheet_id=spreadsheet_id
|
308
|
+
)
|
250
309
|
|
251
310
|
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
311
|
+
@mcp.tool(
|
312
|
+
name="sheets_delete_sheet",
|
313
|
+
description="Deletes a specific sheet (tab) from a Google Spreadsheet using its numeric sheet ID.",
|
314
|
+
)
|
315
|
+
async def sheets_delete_sheet(
|
316
|
+
spreadsheet_id: str, sheet_id: int
|
317
|
+
) -> SheetsDeleteSheetOutput:
|
256
318
|
"""
|
257
319
|
Deletes a sheet from the specified spreadsheet using its numeric ID.
|
258
320
|
|
@@ -261,21 +323,32 @@ async def sheets_delete_sheet(spreadsheet_id: str, sheet_id: int) -> dict[str, A
|
|
261
323
|
sheet_id: The numeric ID of the sheet to delete.
|
262
324
|
|
263
325
|
Returns:
|
264
|
-
|
326
|
+
SheetsDeleteSheetOutput confirming the deletion.
|
265
327
|
"""
|
266
|
-
logger.info(
|
328
|
+
logger.info(
|
329
|
+
f"Executing sheets_delete_sheet tool for spreadsheet_id: '{spreadsheet_id}', sheet_id: {sheet_id}"
|
330
|
+
)
|
267
331
|
if not spreadsheet_id or not spreadsheet_id.strip():
|
268
332
|
raise ValueError("Spreadsheet ID cannot be empty.")
|
269
333
|
if not isinstance(sheet_id, int):
|
270
334
|
raise ValueError("Sheet ID must be an integer.")
|
271
335
|
|
272
336
|
sheets_service = SheetsService()
|
273
|
-
result = sheets_service.delete_sheet(
|
337
|
+
result = sheets_service.delete_sheet(
|
338
|
+
spreadsheet_id=spreadsheet_id, sheet_id=sheet_id
|
339
|
+
)
|
274
340
|
|
275
341
|
if isinstance(result, dict) and result.get("error"):
|
276
342
|
raise ValueError(result.get("message", "Error deleting sheet from spreadsheet"))
|
277
343
|
|
278
344
|
if not result or not result.get("success"):
|
279
|
-
raise ValueError(
|
345
|
+
raise ValueError(
|
346
|
+
f"Failed to delete sheet ID '{sheet_id}' from spreadsheet '{spreadsheet_id}'."
|
347
|
+
)
|
280
348
|
|
281
|
-
return
|
349
|
+
return SheetsDeleteSheetOutput(
|
350
|
+
success=result["success"],
|
351
|
+
message=result.get("message", f"Sheet ID '{sheet_id}' deleted successfully"),
|
352
|
+
spreadsheet_id=spreadsheet_id,
|
353
|
+
deleted_sheet_id=sheet_id,
|
354
|
+
)
|