workspace-mcp 1.0.1__py3-none-any.whl → 1.0.3__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.
gsheets/sheets_tools.py CHANGED
@@ -13,6 +13,7 @@ from googleapiclient.errors import HttpError
13
13
 
14
14
  from auth.service_decorator import require_google_service
15
15
  from core.server import server
16
+ from core.utils import handle_http_errors
16
17
 
17
18
  # Configure module logger
18
19
  logger = logging.getLogger(__name__)
@@ -20,6 +21,7 @@ logger = logging.getLogger(__name__)
20
21
 
21
22
  @server.tool()
22
23
  @require_google_service("drive", "drive_read")
24
+ @handle_http_errors("list_spreadsheets")
23
25
  async def list_spreadsheets(
24
26
  service,
25
27
  user_google_email: str,
@@ -37,47 +39,38 @@ async def list_spreadsheets(
37
39
  """
38
40
  logger.info(f"[list_spreadsheets] Invoked. Email: '{user_google_email}'")
39
41
 
40
- try:
41
- files_response = await asyncio.to_thread(
42
- service.files()
43
- .list(
44
- q="mimeType='application/vnd.google-apps.spreadsheet'",
45
- pageSize=max_results,
46
- fields="files(id,name,modifiedTime,webViewLink)",
47
- orderBy="modifiedTime desc",
48
- )
49
- .execute
42
+ files_response = await asyncio.to_thread(
43
+ service.files()
44
+ .list(
45
+ q="mimeType='application/vnd.google-apps.spreadsheet'",
46
+ pageSize=max_results,
47
+ fields="files(id,name,modifiedTime,webViewLink)",
48
+ orderBy="modifiedTime desc",
50
49
  )
50
+ .execute
51
+ )
51
52
 
52
- files = files_response.get("files", [])
53
- if not files:
54
- return f"No spreadsheets found for {user_google_email}."
55
-
56
- spreadsheets_list = [
57
- f"- \"{file['name']}\" (ID: {file['id']}) | Modified: {file.get('modifiedTime', 'Unknown')} | Link: {file.get('webViewLink', 'No link')}"
58
- for file in files
59
- ]
53
+ files = files_response.get("files", [])
54
+ if not files:
55
+ return f"No spreadsheets found for {user_google_email}."
60
56
 
61
- text_output = (
62
- f"Successfully listed {len(files)} spreadsheets for {user_google_email}:\n"
63
- + "\n".join(spreadsheets_list)
64
- )
57
+ spreadsheets_list = [
58
+ f"- \"{file['name']}\" (ID: {file['id']}) | Modified: {file.get('modifiedTime', 'Unknown')} | Link: {file.get('webViewLink', 'No link')}"
59
+ for file in files
60
+ ]
65
61
 
66
- logger.info(f"Successfully listed {len(files)} spreadsheets for {user_google_email}.")
67
- return text_output
62
+ text_output = (
63
+ f"Successfully listed {len(files)} spreadsheets for {user_google_email}:\n"
64
+ + "\n".join(spreadsheets_list)
65
+ )
68
66
 
69
- except HttpError as error:
70
- message = f"API error listing spreadsheets: {error}. You might need to re-authenticate. LLM: Try 'start_google_auth' with user's email and service_name='Google Sheets'."
71
- logger.error(message, exc_info=True)
72
- raise Exception(message)
73
- except Exception as e:
74
- message = f"Unexpected error listing spreadsheets: {e}."
75
- logger.exception(message)
76
- raise Exception(message)
67
+ logger.info(f"Successfully listed {len(files)} spreadsheets for {user_google_email}.")
68
+ return text_output
77
69
 
78
70
 
79
71
  @server.tool()
80
72
  @require_google_service("sheets", "sheets_read")
73
+ @handle_http_errors("get_spreadsheet_info")
81
74
  async def get_spreadsheet_info(
82
75
  service,
83
76
  user_google_email: str,
@@ -95,48 +88,39 @@ async def get_spreadsheet_info(
95
88
  """
96
89
  logger.info(f"[get_spreadsheet_info] Invoked. Email: '{user_google_email}', Spreadsheet ID: {spreadsheet_id}")
97
90
 
98
- try:
99
- spreadsheet = await asyncio.to_thread(
100
- service.spreadsheets().get(spreadsheetId=spreadsheet_id).execute
101
- )
102
-
103
- title = spreadsheet.get("properties", {}).get("title", "Unknown")
104
- sheets = spreadsheet.get("sheets", [])
91
+ spreadsheet = await asyncio.to_thread(
92
+ service.spreadsheets().get(spreadsheetId=spreadsheet_id).execute
93
+ )
105
94
 
106
- sheets_info = []
107
- for sheet in sheets:
108
- sheet_props = sheet.get("properties", {})
109
- sheet_name = sheet_props.get("title", "Unknown")
110
- sheet_id = sheet_props.get("sheetId", "Unknown")
111
- grid_props = sheet_props.get("gridProperties", {})
112
- rows = grid_props.get("rowCount", "Unknown")
113
- cols = grid_props.get("columnCount", "Unknown")
95
+ title = spreadsheet.get("properties", {}).get("title", "Unknown")
96
+ sheets = spreadsheet.get("sheets", [])
114
97
 
115
- sheets_info.append(
116
- f" - \"{sheet_name}\" (ID: {sheet_id}) | Size: {rows}x{cols}"
117
- )
98
+ sheets_info = []
99
+ for sheet in sheets:
100
+ sheet_props = sheet.get("properties", {})
101
+ sheet_name = sheet_props.get("title", "Unknown")
102
+ sheet_id = sheet_props.get("sheetId", "Unknown")
103
+ grid_props = sheet_props.get("gridProperties", {})
104
+ rows = grid_props.get("rowCount", "Unknown")
105
+ cols = grid_props.get("columnCount", "Unknown")
118
106
 
119
- text_output = (
120
- f"Spreadsheet: \"{title}\" (ID: {spreadsheet_id})\n"
121
- f"Sheets ({len(sheets)}):\n"
122
- + "\n".join(sheets_info) if sheets_info else " No sheets found"
107
+ sheets_info.append(
108
+ f" - \"{sheet_name}\" (ID: {sheet_id}) | Size: {rows}x{cols}"
123
109
  )
124
110
 
125
- logger.info(f"Successfully retrieved info for spreadsheet {spreadsheet_id} for {user_google_email}.")
126
- return text_output
111
+ text_output = (
112
+ f"Spreadsheet: \"{title}\" (ID: {spreadsheet_id})\n"
113
+ f"Sheets ({len(sheets)}):\n"
114
+ + "\n".join(sheets_info) if sheets_info else " No sheets found"
115
+ )
127
116
 
128
- except HttpError as error:
129
- message = f"API error getting spreadsheet info: {error}. You might need to re-authenticate. LLM: Try 'start_google_auth' with user's email and service_name='Google Sheets'."
130
- logger.error(message, exc_info=True)
131
- raise Exception(message)
132
- except Exception as e:
133
- message = f"Unexpected error getting spreadsheet info: {e}."
134
- logger.exception(message)
135
- raise Exception(message)
117
+ logger.info(f"Successfully retrieved info for spreadsheet {spreadsheet_id} for {user_google_email}.")
118
+ return text_output
136
119
 
137
120
 
138
121
  @server.tool()
139
122
  @require_google_service("sheets", "sheets_read")
123
+ @handle_http_errors("read_sheet_values")
140
124
  async def read_sheet_values(
141
125
  service,
142
126
  user_google_email: str,
@@ -156,46 +140,37 @@ async def read_sheet_values(
156
140
  """
157
141
  logger.info(f"[read_sheet_values] Invoked. Email: '{user_google_email}', Spreadsheet: {spreadsheet_id}, Range: {range_name}")
158
142
 
159
- try:
160
- result = await asyncio.to_thread(
161
- service.spreadsheets()
162
- .values()
163
- .get(spreadsheetId=spreadsheet_id, range=range_name)
164
- .execute
165
- )
166
-
167
- values = result.get("values", [])
168
- if not values:
169
- return f"No data found in range '{range_name}' for {user_google_email}."
143
+ result = await asyncio.to_thread(
144
+ service.spreadsheets()
145
+ .values()
146
+ .get(spreadsheetId=spreadsheet_id, range=range_name)
147
+ .execute
148
+ )
170
149
 
171
- # Format the output as a readable table
172
- formatted_rows = []
173
- for i, row in enumerate(values, 1):
174
- # Pad row with empty strings to show structure
175
- padded_row = row + [""] * max(0, len(values[0]) - len(row)) if values else row
176
- formatted_rows.append(f"Row {i:2d}: {padded_row}")
150
+ values = result.get("values", [])
151
+ if not values:
152
+ return f"No data found in range '{range_name}' for {user_google_email}."
177
153
 
178
- text_output = (
179
- f"Successfully read {len(values)} rows from range '{range_name}' in spreadsheet {spreadsheet_id} for {user_google_email}:\n"
180
- + "\n".join(formatted_rows[:50]) # Limit to first 50 rows for readability
181
- + (f"\n... and {len(values) - 50} more rows" if len(values) > 50 else "")
182
- )
154
+ # Format the output as a readable table
155
+ formatted_rows = []
156
+ for i, row in enumerate(values, 1):
157
+ # Pad row with empty strings to show structure
158
+ padded_row = row + [""] * max(0, len(values[0]) - len(row)) if values else row
159
+ formatted_rows.append(f"Row {i:2d}: {padded_row}")
183
160
 
184
- logger.info(f"Successfully read {len(values)} rows for {user_google_email}.")
185
- return text_output
161
+ text_output = (
162
+ f"Successfully read {len(values)} rows from range '{range_name}' in spreadsheet {spreadsheet_id} for {user_google_email}:\n"
163
+ + "\n".join(formatted_rows[:50]) # Limit to first 50 rows for readability
164
+ + (f"\n... and {len(values) - 50} more rows" if len(values) > 50 else "")
165
+ )
186
166
 
187
- except HttpError as error:
188
- message = f"API error reading sheet values: {error}. You might need to re-authenticate. LLM: Try 'start_google_auth' with user's email and service_name='Google Sheets'."
189
- logger.error(message, exc_info=True)
190
- raise Exception(message)
191
- except Exception as e:
192
- message = f"Unexpected error reading sheet values: {e}."
193
- logger.exception(message)
194
- raise Exception(message)
167
+ logger.info(f"Successfully read {len(values)} rows for {user_google_email}.")
168
+ return text_output
195
169
 
196
170
 
197
171
  @server.tool()
198
172
  @require_google_service("sheets", "sheets_write")
173
+ @handle_http_errors("modify_sheet_values")
199
174
  async def modify_sheet_values(
200
175
  service,
201
176
  user_google_email: str,
@@ -225,57 +200,48 @@ async def modify_sheet_values(
225
200
  if not clear_values and not values:
226
201
  raise Exception("Either 'values' must be provided or 'clear_values' must be True.")
227
202
 
228
- try:
229
- if clear_values:
230
- result = await asyncio.to_thread(
231
- service.spreadsheets()
232
- .values()
233
- .clear(spreadsheetId=spreadsheet_id, range=range_name)
234
- .execute
235
- )
236
-
237
- cleared_range = result.get("clearedRange", range_name)
238
- text_output = f"Successfully cleared range '{cleared_range}' in spreadsheet {spreadsheet_id} for {user_google_email}."
239
- logger.info(f"Successfully cleared range '{cleared_range}' for {user_google_email}.")
240
- else:
241
- body = {"values": values}
242
-
243
- result = await asyncio.to_thread(
244
- service.spreadsheets()
245
- .values()
246
- .update(
247
- spreadsheetId=spreadsheet_id,
248
- range=range_name,
249
- valueInputOption=value_input_option,
250
- body=body,
251
- )
252
- .execute
253
- )
203
+ if clear_values:
204
+ result = await asyncio.to_thread(
205
+ service.spreadsheets()
206
+ .values()
207
+ .clear(spreadsheetId=spreadsheet_id, range=range_name)
208
+ .execute
209
+ )
254
210
 
255
- updated_cells = result.get("updatedCells", 0)
256
- updated_rows = result.get("updatedRows", 0)
257
- updated_columns = result.get("updatedColumns", 0)
211
+ cleared_range = result.get("clearedRange", range_name)
212
+ text_output = f"Successfully cleared range '{cleared_range}' in spreadsheet {spreadsheet_id} for {user_google_email}."
213
+ logger.info(f"Successfully cleared range '{cleared_range}' for {user_google_email}.")
214
+ else:
215
+ body = {"values": values}
258
216
 
259
- text_output = (
260
- f"Successfully updated range '{range_name}' in spreadsheet {spreadsheet_id} for {user_google_email}. "
261
- f"Updated: {updated_cells} cells, {updated_rows} rows, {updated_columns} columns."
217
+ result = await asyncio.to_thread(
218
+ service.spreadsheets()
219
+ .values()
220
+ .update(
221
+ spreadsheetId=spreadsheet_id,
222
+ range=range_name,
223
+ valueInputOption=value_input_option,
224
+ body=body,
262
225
  )
263
- logger.info(f"Successfully updated {updated_cells} cells for {user_google_email}.")
226
+ .execute
227
+ )
228
+
229
+ updated_cells = result.get("updatedCells", 0)
230
+ updated_rows = result.get("updatedRows", 0)
231
+ updated_columns = result.get("updatedColumns", 0)
264
232
 
265
- return text_output
233
+ text_output = (
234
+ f"Successfully updated range '{range_name}' in spreadsheet {spreadsheet_id} for {user_google_email}. "
235
+ f"Updated: {updated_cells} cells, {updated_rows} rows, {updated_columns} columns."
236
+ )
237
+ logger.info(f"Successfully updated {updated_cells} cells for {user_google_email}.")
266
238
 
267
- except HttpError as error:
268
- message = f"API error modifying sheet values: {error}. You might need to re-authenticate. LLM: Try 'start_google_auth' with user's email and service_name='Google Sheets'."
269
- logger.error(message, exc_info=True)
270
- raise Exception(message)
271
- except Exception as e:
272
- message = f"Unexpected error modifying sheet values: {e}."
273
- logger.exception(message)
274
- raise Exception(message)
239
+ return text_output
275
240
 
276
241
 
277
242
  @server.tool()
278
243
  @require_google_service("sheets", "sheets_write")
244
+ @handle_http_errors("create_spreadsheet")
279
245
  async def create_spreadsheet(
280
246
  service,
281
247
  user_google_email: str,
@@ -295,45 +261,36 @@ async def create_spreadsheet(
295
261
  """
296
262
  logger.info(f"[create_spreadsheet] Invoked. Email: '{user_google_email}', Title: {title}")
297
263
 
298
- try:
299
- spreadsheet_body = {
300
- "properties": {
301
- "title": title
302
- }
264
+ spreadsheet_body = {
265
+ "properties": {
266
+ "title": title
303
267
  }
268
+ }
304
269
 
305
- if sheet_names:
306
- spreadsheet_body["sheets"] = [
307
- {"properties": {"title": sheet_name}} for sheet_name in sheet_names
308
- ]
309
-
310
- spreadsheet = await asyncio.to_thread(
311
- service.spreadsheets().create(body=spreadsheet_body).execute
312
- )
270
+ if sheet_names:
271
+ spreadsheet_body["sheets"] = [
272
+ {"properties": {"title": sheet_name}} for sheet_name in sheet_names
273
+ ]
313
274
 
314
- spreadsheet_id = spreadsheet.get("spreadsheetId")
315
- spreadsheet_url = spreadsheet.get("spreadsheetUrl")
275
+ spreadsheet = await asyncio.to_thread(
276
+ service.spreadsheets().create(body=spreadsheet_body).execute
277
+ )
316
278
 
317
- text_output = (
318
- f"Successfully created spreadsheet '{title}' for {user_google_email}. "
319
- f"ID: {spreadsheet_id} | URL: {spreadsheet_url}"
320
- )
279
+ spreadsheet_id = spreadsheet.get("spreadsheetId")
280
+ spreadsheet_url = spreadsheet.get("spreadsheetUrl")
321
281
 
322
- logger.info(f"Successfully created spreadsheet for {user_google_email}. ID: {spreadsheet_id}")
323
- return text_output
282
+ text_output = (
283
+ f"Successfully created spreadsheet '{title}' for {user_google_email}. "
284
+ f"ID: {spreadsheet_id} | URL: {spreadsheet_url}"
285
+ )
324
286
 
325
- except HttpError as error:
326
- message = f"API error creating spreadsheet: {error}. You might need to re-authenticate. LLM: Try 'start_google_auth' with user's email and service_name='Google Sheets'."
327
- logger.error(message, exc_info=True)
328
- raise Exception(message)
329
- except Exception as e:
330
- message = f"Unexpected error creating spreadsheet: {e}."
331
- logger.exception(message)
332
- raise Exception(message)
287
+ logger.info(f"Successfully created spreadsheet for {user_google_email}. ID: {spreadsheet_id}")
288
+ return text_output
333
289
 
334
290
 
335
291
  @server.tool()
336
292
  @require_google_service("sheets", "sheets_write")
293
+ @handle_http_errors("create_sheet")
337
294
  async def create_sheet(
338
295
  service,
339
296
  user_google_email: str,
@@ -353,41 +310,31 @@ async def create_sheet(
353
310
  """
354
311
  logger.info(f"[create_sheet] Invoked. Email: '{user_google_email}', Spreadsheet: {spreadsheet_id}, Sheet: {sheet_name}")
355
312
 
356
- try:
357
- request_body = {
358
- "requests": [
359
- {
360
- "addSheet": {
361
- "properties": {
362
- "title": sheet_name
363
- }
313
+ request_body = {
314
+ "requests": [
315
+ {
316
+ "addSheet": {
317
+ "properties": {
318
+ "title": sheet_name
364
319
  }
365
320
  }
366
- ]
367
- }
321
+ }
322
+ ]
323
+ }
368
324
 
369
- response = await asyncio.to_thread(
370
- service.spreadsheets()
371
- .batchUpdate(spreadsheetId=spreadsheet_id, body=request_body)
372
- .execute
373
- )
325
+ response = await asyncio.to_thread(
326
+ service.spreadsheets()
327
+ .batchUpdate(spreadsheetId=spreadsheet_id, body=request_body)
328
+ .execute
329
+ )
374
330
 
375
- sheet_id = response["replies"][0]["addSheet"]["properties"]["sheetId"]
331
+ sheet_id = response["replies"][0]["addSheet"]["properties"]["sheetId"]
376
332
 
377
- text_output = (
378
- f"Successfully created sheet '{sheet_name}' (ID: {sheet_id}) in spreadsheet {spreadsheet_id} for {user_google_email}."
379
- )
333
+ text_output = (
334
+ f"Successfully created sheet '{sheet_name}' (ID: {sheet_id}) in spreadsheet {spreadsheet_id} for {user_google_email}."
335
+ )
380
336
 
381
- logger.info(f"Successfully created sheet for {user_google_email}. Sheet ID: {sheet_id}")
382
- return text_output
383
-
384
- except HttpError as error:
385
- message = f"API error creating sheet: {error}. You might need to re-authenticate. LLM: Try 'start_google_auth' with user's email and service_name='Google Sheets'."
386
- logger.error(message, exc_info=True)
387
- raise Exception(message)
388
- except Exception as e:
389
- message = f"Unexpected error creating sheet: {e}."
390
- logger.exception(message)
391
- raise Exception(message)
337
+ logger.info(f"Successfully created sheet for {user_google_email}. Sheet ID: {sheet_id}")
338
+ return text_output
392
339
 
393
340