google-workspace-mcp 1.0.0__tar.gz → 1.0.1__tar.gz

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.
Files changed (38) hide show
  1. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/PKG-INFO +1 -1
  2. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/pyproject.toml +1 -1
  3. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/__main__.py +7 -7
  4. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/resources/sheets_resources.py +9 -29
  5. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/services/calendar.py +4 -14
  6. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/services/drive.py +13 -43
  7. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/services/sheets_service.py +36 -115
  8. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/tools/docs_tools.py +13 -39
  9. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/tools/drive.py +2 -6
  10. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/tools/sheets_tools.py +17 -51
  11. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/.gitignore +0 -0
  12. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/README.md +0 -0
  13. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/__init__.py +0 -0
  14. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/app.py +0 -0
  15. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/auth/__init__.py +0 -0
  16. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/auth/gauth.py +0 -0
  17. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/config.py +0 -0
  18. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/prompts/__init__.py +0 -0
  19. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/prompts/calendar.py +0 -0
  20. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/prompts/drive.py +0 -0
  21. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/prompts/gmail.py +0 -0
  22. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/prompts/slides.py +0 -0
  23. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/resources/__init__.py +0 -0
  24. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/resources/calendar.py +0 -0
  25. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/resources/drive.py +0 -0
  26. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/resources/gmail.py +0 -0
  27. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/resources/slides.py +0 -0
  28. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/services/__init__.py +0 -0
  29. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/services/base.py +0 -0
  30. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/services/docs_service.py +0 -0
  31. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/services/gmail.py +0 -0
  32. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/services/slides.py +0 -0
  33. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/tools/__init__.py +0 -0
  34. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/tools/calendar.py +0 -0
  35. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/tools/gmail.py +0 -0
  36. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/tools/slides.py +0 -0
  37. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/utils/__init__.py +0 -0
  38. {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.1}/src/google_workspace_mcp/utils/markdown_slides.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: google-workspace-mcp
3
- Version: 1.0.0
3
+ Version: 1.0.1
4
4
  Summary: MCP server for Google Workspace integration
5
5
  Author-email: Arclio Team <info@arclio.com>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "google-workspace-mcp"
7
- version = "1.0.0"
7
+ version = "1.0.1"
8
8
  description = "MCP server for Google Workspace integration"
9
9
  authors = [
10
10
  {name = "Arclio Team", email = "info@arclio.com"},
@@ -19,16 +19,16 @@ from google_workspace_mcp.resources import drive as drive_resources # noqa: F40
19
19
  from google_workspace_mcp.resources import gmail as gmail_resources # noqa: F401
20
20
  from google_workspace_mcp.resources import sheets_resources # noqa: F401
21
21
  from google_workspace_mcp.resources import slides as slides_resources # noqa: F401
22
+ from google_workspace_mcp.tools import calendar as calendar_tools # noqa: F401
22
23
 
23
24
  # Register tools
24
- from google_workspace_mcp.tools import ( # noqa: F401
25
- calendar_tools,
26
- docs_tools,
27
- drive_tools,
28
- gmail_tools,
29
- sheets_tools,
30
- slides_tools,
25
+ from google_workspace_mcp.tools import (
26
+ docs_tools, # noqa: F401
27
+ sheets_tools, # noqa: F401
31
28
  )
29
+ from google_workspace_mcp.tools import drive as drive_tools # noqa: F401
30
+ from google_workspace_mcp.tools import gmail as gmail_tools # noqa: F401
31
+ from google_workspace_mcp.tools import slides as slides_tools # noqa: F401
32
32
 
33
33
 
34
34
  def main():
@@ -13,9 +13,7 @@ async def get_spreadsheet_metadata_resource(spreadsheet_id: str) -> dict[str, An
13
13
  Retrieves metadata for a specific Google Spreadsheet.
14
14
  Maps to URI: sheets://spreadsheets/{spreadsheet_id}/metadata
15
15
  """
16
- logger.info(
17
- f"Executing get_spreadsheet_metadata_resource for spreadsheet_id: {spreadsheet_id}"
18
- )
16
+ logger.info(f"Executing get_spreadsheet_metadata_resource for spreadsheet_id: {spreadsheet_id}")
19
17
  if not spreadsheet_id:
20
18
  raise ValueError("Spreadsheet ID is required in the URI path.")
21
19
 
@@ -23,24 +21,16 @@ async def get_spreadsheet_metadata_resource(spreadsheet_id: str) -> dict[str, An
23
21
  metadata = sheets_service.get_spreadsheet_metadata(spreadsheet_id=spreadsheet_id)
24
22
 
25
23
  if isinstance(metadata, dict) and metadata.get("error"):
26
- raise ValueError(
27
- metadata.get("message", "Error retrieving spreadsheet metadata")
28
- )
24
+ raise ValueError(metadata.get("message", "Error retrieving spreadsheet metadata"))
29
25
 
30
26
  if not metadata:
31
- raise ValueError(
32
- f"Could not retrieve metadata for spreadsheet ID: {spreadsheet_id}"
33
- )
27
+ raise ValueError(f"Could not retrieve metadata for spreadsheet ID: {spreadsheet_id}")
34
28
 
35
29
  return metadata
36
30
 
37
31
 
38
- @mcp.resource(
39
- "sheets://spreadsheets/{spreadsheet_id}/sheets/{sheet_identifier}/metadata"
40
- )
41
- async def get_specific_sheet_metadata_resource(
42
- spreadsheet_id: str, sheet_identifier: str
43
- ) -> dict[str, Any]:
32
+ @mcp.resource("sheets://spreadsheets/{spreadsheet_id}/sheets/{sheet_identifier}/metadata")
33
+ async def get_specific_sheet_metadata_resource(spreadsheet_id: str, sheet_identifier: str) -> dict[str, Any]:
44
34
  """
45
35
  Retrieves metadata for a specific sheet within a Google Spreadsheet,
46
36
  identified by its title (name) or numeric sheetId.
@@ -50,9 +40,7 @@ async def get_specific_sheet_metadata_resource(
50
40
  f"Executing get_specific_sheet_metadata_resource for spreadsheet: {spreadsheet_id}, sheet_identifier: {sheet_identifier}"
51
41
  )
52
42
  if not spreadsheet_id or not sheet_identifier:
53
- raise ValueError(
54
- "Spreadsheet ID and sheet identifier (name or ID) are required."
55
- )
43
+ raise ValueError("Spreadsheet ID and sheet identifier (name or ID) are required.")
56
44
 
57
45
  sheets_service = SheetsService()
58
46
  # Fetch metadata for all sheets first
@@ -62,16 +50,10 @@ async def get_specific_sheet_metadata_resource(
62
50
  )
63
51
 
64
52
  if isinstance(full_metadata, dict) and full_metadata.get("error"):
65
- raise ValueError(
66
- full_metadata.get(
67
- "message", "Error retrieving spreadsheet to find sheet metadata"
68
- )
69
- )
53
+ raise ValueError(full_metadata.get("message", "Error retrieving spreadsheet to find sheet metadata"))
70
54
 
71
55
  if not full_metadata or not full_metadata.get("sheets"):
72
- raise ValueError(
73
- f"No sheets found in spreadsheet {spreadsheet_id} or metadata incomplete."
74
- )
56
+ raise ValueError(f"No sheets found in spreadsheet {spreadsheet_id} or metadata incomplete.")
75
57
 
76
58
  found_sheet = None
77
59
  for sheet in full_metadata.get("sheets", []):
@@ -85,8 +67,6 @@ async def get_specific_sheet_metadata_resource(
85
67
  break
86
68
 
87
69
  if not found_sheet:
88
- raise ValueError(
89
- f"Sheet '{sheet_identifier}' not found in spreadsheet '{spreadsheet_id}'."
90
- )
70
+ raise ValueError(f"Sheet '{sheet_identifier}' not found in spreadsheet '{spreadsheet_id}'.")
91
71
 
92
72
  return found_sheet
@@ -226,9 +226,7 @@ class CalendarService(BaseGoogleService):
226
226
  self.handle_api_error("delete_event", e)
227
227
  return False
228
228
 
229
- def get_event_details(
230
- self, event_id: str, calendar_id: str = "primary"
231
- ) -> dict[str, Any] | None:
229
+ def get_event_details(self, event_id: str, calendar_id: str = "primary") -> dict[str, Any] | None:
232
230
  """
233
231
  Retrieves details for a specific event.
234
232
 
@@ -240,17 +238,9 @@ class CalendarService(BaseGoogleService):
240
238
  A dictionary containing the event details or an error dictionary.
241
239
  """
242
240
  try:
243
- logger.info(
244
- f"Fetching details for event ID: {event_id} from calendar: {calendar_id}"
245
- )
246
- event = (
247
- self.service.events()
248
- .get(calendarId=calendar_id, eventId=event_id)
249
- .execute()
250
- )
251
- logger.info(
252
- f"Successfully fetched details for event: {event.get('summary')}"
253
- )
241
+ logger.info(f"Fetching details for event ID: {event_id} from calendar: {calendar_id}")
242
+ event = self.service.events().get(calendarId=calendar_id, eventId=event_id).execute()
243
+ logger.info(f"Successfully fetched details for event: {event.get('summary')}")
254
244
  return event # Return the full event resource as per API
255
245
  except Exception as e:
256
246
  return self.handle_api_error("get_event_details", e)
@@ -27,9 +27,7 @@ class DriveService(BaseGoogleService):
27
27
  """Initialize the Drive service."""
28
28
  super().__init__("drive", "v3")
29
29
 
30
- def search_files(
31
- self, query: str, page_size: int = 10, shared_drive_id: str | None = None
32
- ) -> list[dict[str, Any]]:
30
+ def search_files(self, query: str, page_size: int = 10, shared_drive_id: str | None = None) -> list[dict[str, Any]]:
33
31
  """
34
32
  Search for files in Google Drive.
35
33
 
@@ -42,9 +40,7 @@ class DriveService(BaseGoogleService):
42
40
  List of file metadata dictionaries (id, name, mimeType, etc.) or an error dictionary
43
41
  """
44
42
  try:
45
- logger.info(
46
- f"Searching files with query: '{query}', page_size: {page_size}, shared_drive_id: {shared_drive_id}"
47
- )
43
+ logger.info(f"Searching files with query: '{query}', page_size: {page_size}, shared_drive_id: {shared_drive_id}")
48
44
 
49
45
  # Validate and constrain page_size
50
46
  page_size = max(1, min(page_size, 1000))
@@ -63,13 +59,9 @@ class DriveService(BaseGoogleService):
63
59
 
64
60
  if shared_drive_id:
65
61
  list_params["driveId"] = shared_drive_id
66
- list_params["corpora"] = (
67
- "drive" # Search within the specified shared drive
68
- )
62
+ list_params["corpora"] = "drive" # Search within the specified shared drive
69
63
  else:
70
- list_params["corpora"] = (
71
- "user" # Default to user's files if no specific shared drive ID
72
- )
64
+ list_params["corpora"] = "user" # Default to user's files if no specific shared drive ID
73
65
 
74
66
  results = self.service.files().list(**list_params).execute()
75
67
  files = results.get("files", [])
@@ -92,18 +84,12 @@ class DriveService(BaseGoogleService):
92
84
  """
93
85
  try:
94
86
  # Get file metadata
95
- file_metadata = (
96
- self.service.files()
97
- .get(fileId=file_id, fields="mimeType, name")
98
- .execute()
99
- )
87
+ file_metadata = self.service.files().get(fileId=file_id, fields="mimeType, name").execute()
100
88
 
101
89
  original_mime_type = file_metadata.get("mimeType")
102
90
  file_name = file_metadata.get("name", "Unknown")
103
91
 
104
- logger.info(
105
- f"Reading file '{file_name}' ({file_id}) with mimeType: {original_mime_type}"
106
- )
92
+ logger.info(f"Reading file '{file_name}' ({file_id}) with mimeType: {original_mime_type}")
107
93
 
108
94
  # Handle Google Workspace files by exporting
109
95
  if original_mime_type.startswith("application/vnd.google-apps."):
@@ -143,9 +129,7 @@ class DriveService(BaseGoogleService):
143
129
  .execute()
144
130
  )
145
131
 
146
- logger.info(
147
- f"Successfully retrieved metadata for file: {file_metadata.get('name', 'Unknown')}"
148
- )
132
+ logger.info(f"Successfully retrieved metadata for file: {file_metadata.get('name', 'Unknown')}")
149
133
  return file_metadata
150
134
 
151
135
  except Exception as e:
@@ -201,17 +185,13 @@ class DriveService(BaseGoogleService):
201
185
 
202
186
  created_folder = self.service.files().create(**create_params).execute()
203
187
 
204
- logger.info(
205
- f"Successfully created folder '{folder_name}' with ID: {created_folder.get('id')}"
206
- )
188
+ logger.info(f"Successfully created folder '{folder_name}' with ID: {created_folder.get('id')}")
207
189
  return created_folder
208
190
 
209
191
  except Exception as e:
210
192
  return self.handle_api_error("create_folder", e)
211
193
 
212
- def _export_google_file(
213
- self, file_id: str, file_name: str, mime_type: str
214
- ) -> dict[str, Any]:
194
+ def _export_google_file(self, file_id: str, file_name: str, mime_type: str) -> dict[str, Any]:
215
195
  """Export a Google Workspace file in an appropriate format."""
216
196
  # Determine export format
217
197
  export_mime_type = None
@@ -236,9 +216,7 @@ class DriveService(BaseGoogleService):
236
216
 
237
217
  # Export the file
238
218
  try:
239
- request = self.service.files().export_media(
240
- fileId=file_id, mimeType=export_mime_type, supportsAllDrives=True
241
- )
219
+ request = self.service.files().export_media(fileId=file_id, mimeType=export_mime_type, supportsAllDrives=True)
242
220
 
243
221
  content_bytes = self._download_content(request)
244
222
  if isinstance(content_bytes, dict) and content_bytes.get("error"):
@@ -270,9 +248,7 @@ class DriveService(BaseGoogleService):
270
248
  except Exception as e:
271
249
  return self.handle_api_error("_export_google_file", e)
272
250
 
273
- def _download_regular_file(
274
- self, file_id: str, file_name: str, mime_type: str
275
- ) -> dict[str, Any]:
251
+ def _download_regular_file(self, file_id: str, file_name: str, mime_type: str) -> dict[str, Any]:
276
252
  """Download a regular (non-Google Workspace) file."""
277
253
  request = self.service.files().get_media(fileId=file_id, supportsAllDrives=True)
278
254
 
@@ -286,9 +262,7 @@ class DriveService(BaseGoogleService):
286
262
  content = content_bytes.decode("utf-8")
287
263
  return {"mimeType": mime_type, "content": content, "encoding": "utf-8"}
288
264
  except UnicodeDecodeError:
289
- logger.warning(
290
- f"UTF-8 decoding failed for file {file_id} ('{file_name}', {mime_type}). Using base64."
291
- )
265
+ logger.warning(f"UTF-8 decoding failed for file {file_id} ('{file_name}', {mime_type}). Using base64.")
292
266
  content = base64.b64encode(content_bytes).decode("utf-8")
293
267
  return {
294
268
  "mimeType": mime_type,
@@ -426,11 +400,7 @@ class DriveService(BaseGoogleService):
426
400
  # API allows pageSize up to 100 for drives.list
427
401
  actual_page_size = min(max(1, page_size), 100)
428
402
 
429
- results = (
430
- self.service.drives()
431
- .list(pageSize=actual_page_size, fields="drives(id, name, kind)")
432
- .execute()
433
- )
403
+ results = self.service.drives().list(pageSize=actual_page_size, fields="drives(id, name, kind)").execute()
434
404
  drives = results.get("drives", [])
435
405
 
436
406
  # Filter for kind='drive#drive' just to be sure, though API should only return these
@@ -36,9 +36,7 @@ class SheetsService(BaseGoogleService):
36
36
  try:
37
37
  logger.info(f"Creating new Google Spreadsheet with title: '{title}'")
38
38
  spreadsheet_body = {"properties": {"title": title}}
39
- spreadsheet = (
40
- self.service.spreadsheets().create(body=spreadsheet_body).execute()
41
- )
39
+ spreadsheet = self.service.spreadsheets().create(body=spreadsheet_body).execute()
42
40
  spreadsheet_id = spreadsheet.get("spreadsheetId")
43
41
  logger.info(
44
42
  f"Successfully created spreadsheet: {spreadsheet.get('properties', {}).get('title')} (ID: {spreadsheet_id})"
@@ -73,15 +71,8 @@ class SheetsService(BaseGoogleService):
73
71
  or an error dictionary.
74
72
  """
75
73
  try:
76
- logger.info(
77
- f"Reading range '{range_a1}' from spreadsheet ID: {spreadsheet_id}"
78
- )
79
- result = (
80
- self.service.spreadsheets()
81
- .values()
82
- .get(spreadsheetId=spreadsheet_id, range=range_a1)
83
- .execute()
84
- )
74
+ logger.info(f"Reading range '{range_a1}' from spreadsheet ID: {spreadsheet_id}")
75
+ result = self.service.spreadsheets().values().get(spreadsheetId=spreadsheet_id, range=range_a1).execute()
85
76
 
86
77
  # result will contain 'range', 'majorDimension', 'values'
87
78
  # 'values' is a list of lists.
@@ -91,23 +82,15 @@ class SheetsService(BaseGoogleService):
91
82
  return {
92
83
  "spreadsheet_id": spreadsheet_id,
93
84
  "range_requested": range_a1, # The input range
94
- "range_returned": result.get(
95
- "range"
96
- ), # The actual range returned by API
85
+ "range_returned": result.get("range"), # The actual range returned by API
97
86
  "major_dimension": result.get("majorDimension"),
98
- "values": result.get(
99
- "values", []
100
- ), # Default to empty list if no values
87
+ "values": result.get("values", []), # Default to empty list if no values
101
88
  }
102
89
  except HttpError as error:
103
- logger.error(
104
- f"Error reading range '{range_a1}' from spreadsheet {spreadsheet_id}: {error}"
105
- )
90
+ logger.error(f"Error reading range '{range_a1}' from spreadsheet {spreadsheet_id}: {error}")
106
91
  return self.handle_api_error("read_range", error)
107
92
  except Exception as e:
108
- logger.exception(
109
- f"Unexpected error reading range '{range_a1}' from spreadsheet {spreadsheet_id}"
110
- )
93
+ logger.exception(f"Unexpected error reading range '{range_a1}' from spreadsheet {spreadsheet_id}")
111
94
  return {
112
95
  "error": True,
113
96
  "error_type": "unexpected_service_error",
@@ -157,23 +140,17 @@ class SheetsService(BaseGoogleService):
157
140
  f"Successfully wrote to range '{result.get('updatedRange')}' in spreadsheet ID: {spreadsheet_id}. Updated {result.get('updatedCells')} cells."
158
141
  )
159
142
  return {
160
- "spreadsheet_id": result.get(
161
- "spreadsheetId"
162
- ), # Or use the input spreadsheet_id
143
+ "spreadsheet_id": result.get("spreadsheetId"), # Or use the input spreadsheet_id
163
144
  "updated_range": result.get("updatedRange"),
164
145
  "updated_rows": result.get("updatedRows"),
165
146
  "updated_columns": result.get("updatedColumns"),
166
147
  "updated_cells": result.get("updatedCells"),
167
148
  }
168
149
  except HttpError as error:
169
- logger.error(
170
- f"Error writing to range '{range_a1}' in spreadsheet {spreadsheet_id}: {error}"
171
- )
150
+ logger.error(f"Error writing to range '{range_a1}' in spreadsheet {spreadsheet_id}: {error}")
172
151
  return self.handle_api_error("write_range", error)
173
152
  except Exception as e:
174
- logger.exception(
175
- f"Unexpected error writing to range '{range_a1}' in spreadsheet {spreadsheet_id}"
176
- )
153
+ logger.exception(f"Unexpected error writing to range '{range_a1}' in spreadsheet {spreadsheet_id}")
177
154
  return {
178
155
  "error": True,
179
156
  "error_type": "unexpected_service_error",
@@ -223,29 +200,17 @@ class SheetsService(BaseGoogleService):
223
200
  .execute()
224
201
  )
225
202
  # result typically includes: spreadsheetId, tableRange (if appended to a table), updates (if named ranges/etc. were affected)
226
- logger.info(
227
- f"Successfully appended rows to spreadsheet ID: {spreadsheet_id}. Updates: {result.get('updates')}"
228
- )
203
+ logger.info(f"Successfully appended rows to spreadsheet ID: {spreadsheet_id}. Updates: {result.get('updates')}")
229
204
  return {
230
- "spreadsheet_id": result.get(
231
- "spreadsheetId"
232
- ), # Or use the input spreadsheet_id
233
- "table_range_updated": result.get(
234
- "tableRange"
235
- ), # The range of the new data
236
- "updates": result.get(
237
- "updates"
238
- ), # Info about other updates, e.g. to named ranges
205
+ "spreadsheet_id": result.get("spreadsheetId"), # Or use the input spreadsheet_id
206
+ "table_range_updated": result.get("tableRange"), # The range of the new data
207
+ "updates": result.get("updates"), # Info about other updates, e.g. to named ranges
239
208
  }
240
209
  except HttpError as error:
241
- logger.error(
242
- f"Error appending rows to range '{range_a1}' in spreadsheet {spreadsheet_id}: {error}"
243
- )
210
+ logger.error(f"Error appending rows to range '{range_a1}' in spreadsheet {spreadsheet_id}: {error}")
244
211
  return self.handle_api_error("append_rows", error)
245
212
  except Exception as e:
246
- logger.exception(
247
- f"Unexpected error appending rows to range '{range_a1}' in spreadsheet {spreadsheet_id}"
248
- )
213
+ logger.exception(f"Unexpected error appending rows to range '{range_a1}' in spreadsheet {spreadsheet_id}")
249
214
  return {
250
215
  "error": True,
251
216
  "error_type": "unexpected_service_error",
@@ -266,35 +231,22 @@ class SheetsService(BaseGoogleService):
266
231
  A dictionary containing the cleared range and spreadsheet ID, or an error dictionary.
267
232
  """
268
233
  try:
269
- logger.info(
270
- f"Clearing range '{range_a1}' in spreadsheet ID: {spreadsheet_id}"
271
- )
234
+ logger.info(f"Clearing range '{range_a1}' in spreadsheet ID: {spreadsheet_id}")
272
235
  # The body for clear is an empty JSON object {}.
273
236
  result = (
274
- self.service.spreadsheets()
275
- .values()
276
- .clear(spreadsheetId=spreadsheet_id, range=range_a1, body={})
277
- .execute()
237
+ self.service.spreadsheets().values().clear(spreadsheetId=spreadsheet_id, range=range_a1, body={}).execute()
278
238
  )
279
239
  # result typically includes: spreadsheetId, clearedRange
280
- logger.info(
281
- f"Successfully cleared range '{result.get('clearedRange')}' in spreadsheet ID: {spreadsheet_id}."
282
- )
240
+ logger.info(f"Successfully cleared range '{result.get('clearedRange')}' in spreadsheet ID: {spreadsheet_id}.")
283
241
  return {
284
- "spreadsheet_id": result.get(
285
- "spreadsheetId"
286
- ), # Or use the input spreadsheet_id
242
+ "spreadsheet_id": result.get("spreadsheetId"), # Or use the input spreadsheet_id
287
243
  "cleared_range": result.get("clearedRange"),
288
244
  }
289
245
  except HttpError as error:
290
- logger.error(
291
- f"Error clearing range '{range_a1}' from spreadsheet {spreadsheet_id}: {error}"
292
- )
246
+ logger.error(f"Error clearing range '{range_a1}' from spreadsheet {spreadsheet_id}: {error}")
293
247
  return self.handle_api_error("clear_range", error)
294
248
  except Exception as e:
295
- logger.exception(
296
- f"Unexpected error clearing range '{range_a1}' from spreadsheet {spreadsheet_id}"
297
- )
249
+ logger.exception(f"Unexpected error clearing range '{range_a1}' from spreadsheet {spreadsheet_id}")
298
250
  return {
299
251
  "error": True,
300
252
  "error_type": "unexpected_service_error",
@@ -315,16 +267,10 @@ class SheetsService(BaseGoogleService):
315
267
  or an error dictionary.
316
268
  """
317
269
  try:
318
- logger.info(
319
- f"Adding new sheet with title '{title}' to spreadsheet ID: {spreadsheet_id}"
320
- )
270
+ logger.info(f"Adding new sheet with title '{title}' to spreadsheet ID: {spreadsheet_id}")
321
271
  requests = [{"addSheet": {"properties": {"title": title}}}]
322
272
  body = {"requests": requests}
323
- response = (
324
- self.service.spreadsheets()
325
- .batchUpdate(spreadsheetId=spreadsheet_id, body=body)
326
- .execute()
327
- )
273
+ response = self.service.spreadsheets().batchUpdate(spreadsheetId=spreadsheet_id, body=body).execute()
328
274
 
329
275
  # The response contains a list of replies, one for each request.
330
276
  # The addSheet reply contains the properties of the new sheet.
@@ -354,14 +300,10 @@ class SheetsService(BaseGoogleService):
354
300
  "sheet_properties": new_sheet_properties,
355
301
  }
356
302
  except HttpError as error:
357
- logger.error(
358
- f"Error adding sheet '{title}' to spreadsheet {spreadsheet_id}: {error}"
359
- )
303
+ logger.error(f"Error adding sheet '{title}' to spreadsheet {spreadsheet_id}: {error}")
360
304
  return self.handle_api_error("add_sheet", error)
361
305
  except Exception as e:
362
- logger.exception(
363
- f"Unexpected error adding sheet '{title}' to spreadsheet {spreadsheet_id}"
364
- )
306
+ logger.exception(f"Unexpected error adding sheet '{title}' to spreadsheet {spreadsheet_id}")
365
307
  return {
366
308
  "error": True,
367
309
  "error_type": "unexpected_service_error",
@@ -381,16 +323,10 @@ class SheetsService(BaseGoogleService):
381
323
  A dictionary indicating success (spreadsheetId and deleted sheetId) or an error dictionary.
382
324
  """
383
325
  try:
384
- logger.info(
385
- f"Deleting sheet ID: {sheet_id} from spreadsheet ID: {spreadsheet_id}"
386
- )
326
+ logger.info(f"Deleting sheet ID: {sheet_id} from spreadsheet ID: {spreadsheet_id}")
387
327
  requests = [{"deleteSheet": {"sheetId": sheet_id}}]
388
328
  body = {"requests": requests}
389
- response = (
390
- self.service.spreadsheets()
391
- .batchUpdate(spreadsheetId=spreadsheet_id, body=body)
392
- .execute()
393
- )
329
+ response = self.service.spreadsheets().batchUpdate(spreadsheetId=spreadsheet_id, body=body).execute()
394
330
 
395
331
  # A successful deleteSheet request usually doesn't return detailed content in the reply.
396
332
  # The overall response.spreadsheetId confirms the operation was on the correct spreadsheet.
@@ -403,14 +339,10 @@ class SheetsService(BaseGoogleService):
403
339
  "success": True,
404
340
  }
405
341
  except HttpError as error:
406
- logger.error(
407
- f"Error deleting sheet ID {sheet_id} from spreadsheet {spreadsheet_id}: {error}"
408
- )
342
+ logger.error(f"Error deleting sheet ID {sheet_id} from spreadsheet {spreadsheet_id}: {error}")
409
343
  return self.handle_api_error("delete_sheet", error)
410
344
  except Exception as e:
411
- logger.exception(
412
- f"Unexpected error deleting sheet ID {sheet_id} from spreadsheet {spreadsheet_id}"
413
- )
345
+ logger.exception(f"Unexpected error deleting sheet ID {sheet_id} from spreadsheet {spreadsheet_id}")
414
346
  return {
415
347
  "error": True,
416
348
  "error_type": "unexpected_service_error",
@@ -418,9 +350,7 @@ class SheetsService(BaseGoogleService):
418
350
  "operation": "delete_sheet",
419
351
  }
420
352
 
421
- def get_spreadsheet_metadata(
422
- self, spreadsheet_id: str, fields: str | None = None
423
- ) -> dict[str, Any] | None:
353
+ def get_spreadsheet_metadata(self, spreadsheet_id: str, fields: str | None = None) -> dict[str, Any] | None:
424
354
  """
425
355
  Retrieves metadata for a specific Google Spreadsheet.
426
356
 
@@ -434,30 +364,21 @@ class SheetsService(BaseGoogleService):
434
364
  """
435
365
  try:
436
366
  logger.info(
437
- f"Fetching metadata for spreadsheet ID: {spreadsheet_id}"
438
- + (f" with fields: {fields}" if fields else "")
367
+ f"Fetching metadata for spreadsheet ID: {spreadsheet_id}" + (f" with fields: {fields}" if fields else "")
439
368
  )
440
369
  if fields is None:
441
370
  fields = "spreadsheetId,properties,sheets(properties(sheetId,title,index,sheetType,gridProperties))"
442
371
 
443
- spreadsheet_metadata = (
444
- self.service.spreadsheets()
445
- .get(spreadsheetId=spreadsheet_id, fields=fields)
446
- .execute()
447
- )
372
+ spreadsheet_metadata = self.service.spreadsheets().get(spreadsheetId=spreadsheet_id, fields=fields).execute()
448
373
  logger.info(
449
- f"Successfully fetched metadata for spreadsheet: {spreadsheet_metadata.get('properties',{}).get('title')}"
374
+ f"Successfully fetched metadata for spreadsheet: {spreadsheet_metadata.get('properties', {}).get('title')}"
450
375
  )
451
376
  return spreadsheet_metadata
452
377
  except HttpError as error:
453
- logger.error(
454
- f"Error fetching metadata for spreadsheet ID {spreadsheet_id}: {error}"
455
- )
378
+ logger.error(f"Error fetching metadata for spreadsheet ID {spreadsheet_id}: {error}")
456
379
  return self.handle_api_error("get_spreadsheet_metadata", error)
457
380
  except Exception as e:
458
- logger.exception(
459
- f"Unexpected error fetching metadata for spreadsheet ID {spreadsheet_id}"
460
- )
381
+ logger.exception(f"Unexpected error fetching metadata for spreadsheet ID {spreadsheet_id}")
461
382
  return {
462
383
  "error": True,
463
384
  "error_type": "unexpected_service_error",
@@ -37,9 +37,7 @@ async def docs_create_document(title: str) -> dict[str, Any]:
37
37
  raise ValueError(result.get("message", "Error creating document"))
38
38
 
39
39
  if not result or not result.get("document_id"):
40
- raise ValueError(
41
- f"Failed to create document '{title}' or did not receive a document ID."
42
- )
40
+ raise ValueError(f"Failed to create document '{title}' or did not receive a document ID.")
43
41
 
44
42
  return result
45
43
 
@@ -59,9 +57,7 @@ async def docs_get_document_metadata(document_id: str) -> dict[str, Any]:
59
57
  A dictionary containing the document's 'document_id', 'title', and 'document_link',
60
58
  or an error message.
61
59
  """
62
- logger.info(
63
- f"Executing docs_get_document_metadata tool for document_id: '{document_id}'"
64
- )
60
+ logger.info(f"Executing docs_get_document_metadata tool for document_id: '{document_id}'")
65
61
  if not document_id or not document_id.strip():
66
62
  raise ValueError("Document ID cannot be empty.")
67
63
 
@@ -92,9 +88,7 @@ async def docs_get_content_as_markdown(document_id: str) -> dict[str, Any]:
92
88
  A dictionary containing the 'document_id' and 'markdown_content',
93
89
  or an error message.
94
90
  """
95
- logger.info(
96
- f"Executing docs_get_content_as_markdown tool for document_id: '{document_id}'"
97
- )
91
+ logger.info(f"Executing docs_get_content_as_markdown tool for document_id: '{document_id}'")
98
92
  if not document_id or not document_id.strip():
99
93
  raise ValueError("Document ID cannot be empty.")
100
94
 
@@ -102,14 +96,10 @@ async def docs_get_content_as_markdown(document_id: str) -> dict[str, Any]:
102
96
  result = docs_service.get_document_content_as_markdown(document_id=document_id)
103
97
 
104
98
  if isinstance(result, dict) and result.get("error"):
105
- raise ValueError(
106
- result.get("message", "Error retrieving document content as Markdown")
107
- )
99
+ raise ValueError(result.get("message", "Error retrieving document content as Markdown"))
108
100
 
109
101
  if not result or "markdown_content" not in result:
110
- raise ValueError(
111
- f"Failed to retrieve Markdown content for document '{document_id}'."
112
- )
102
+ raise ValueError(f"Failed to retrieve Markdown content for document '{document_id}'.")
113
103
 
114
104
  return result
115
105
 
@@ -118,9 +108,7 @@ async def docs_get_content_as_markdown(document_id: str) -> dict[str, Any]:
118
108
  name="docs_append_text",
119
109
  description="Appends text to the end of a specified Google Document.",
120
110
  )
121
- async def docs_append_text(
122
- document_id: str, text: str, ensure_newline: bool = True
123
- ) -> dict[str, Any]:
111
+ async def docs_append_text(document_id: str, text: str, ensure_newline: bool = True) -> dict[str, Any]:
124
112
  """
125
113
  Appends the given text to the end of the specified Google Document.
126
114
 
@@ -139,9 +127,7 @@ async def docs_append_text(
139
127
  # Text can be empty, that's fine.
140
128
 
141
129
  docs_service = DocsService()
142
- result = docs_service.append_text(
143
- document_id=document_id, text=text, ensure_newline=ensure_newline
144
- )
130
+ result = docs_service.append_text(document_id=document_id, text=text, ensure_newline=ensure_newline)
145
131
 
146
132
  if isinstance(result, dict) and result.get("error"):
147
133
  raise ValueError(result.get("message", "Error appending text to document"))
@@ -156,9 +142,7 @@ async def docs_append_text(
156
142
  name="docs_prepend_text",
157
143
  description="Prepends text to the beginning of a specified Google Document.",
158
144
  )
159
- async def docs_prepend_text(
160
- document_id: str, text: str, ensure_newline: bool = True
161
- ) -> dict[str, Any]:
145
+ async def docs_prepend_text(document_id: str, text: str, ensure_newline: bool = True) -> dict[str, Any]:
162
146
  """
163
147
  Prepends the given text to the beginning of the specified Google Document.
164
148
 
@@ -177,9 +161,7 @@ async def docs_prepend_text(
177
161
  # Text can be empty, that's fine.
178
162
 
179
163
  docs_service = DocsService()
180
- result = docs_service.prepend_text(
181
- document_id=document_id, text=text, ensure_newline=ensure_newline
182
- )
164
+ result = docs_service.prepend_text(document_id=document_id, text=text, ensure_newline=ensure_newline)
183
165
 
184
166
  if isinstance(result, dict) and result.get("error"):
185
167
  raise ValueError(result.get("message", "Error prepending text to document"))
@@ -213,17 +195,13 @@ async def docs_insert_text(
213
195
  Returns:
214
196
  A dictionary indicating success or an error message.
215
197
  """
216
- logger.info(
217
- f"Executing docs_insert_text tool for document_id: '{document_id}' at index: {index}"
218
- )
198
+ logger.info(f"Executing docs_insert_text tool for document_id: '{document_id}' at index: {index}")
219
199
  if not document_id or not document_id.strip():
220
200
  raise ValueError("Document ID cannot be empty.")
221
201
  # Text can be empty, that's a valid insertion (though perhaps not useful).
222
202
 
223
203
  docs_service = DocsService()
224
- result = docs_service.insert_text(
225
- document_id=document_id, text=text, index=index, segment_id=segment_id
226
- )
204
+ result = docs_service.insert_text(document_id=document_id, text=text, index=index, segment_id=segment_id)
227
205
 
228
206
  if isinstance(result, dict) and result.get("error"):
229
207
  raise ValueError(result.get("message", "Error inserting text into document"))
@@ -253,9 +231,7 @@ async def docs_batch_update(document_id: str, requests: list[dict]) -> dict[str,
253
231
  A dictionary containing the API response from the batchUpdate call (includes replies for each request),
254
232
  or an error message.
255
233
  """
256
- logger.info(
257
- f"Executing docs_batch_update tool for document_id: '{document_id}' with {len(requests)} requests."
258
- )
234
+ logger.info(f"Executing docs_batch_update tool for document_id: '{document_id}' with {len(requests)} requests.")
259
235
  if not document_id or not document_id.strip():
260
236
  raise ValueError("Document ID cannot be empty.")
261
237
  if not isinstance(requests, list):
@@ -267,9 +243,7 @@ async def docs_batch_update(document_id: str, requests: list[dict]) -> dict[str,
267
243
  result = docs_service.batch_update(document_id=document_id, requests=requests)
268
244
 
269
245
  if isinstance(result, dict) and result.get("error"):
270
- raise ValueError(
271
- result.get("message", "Error executing batch update on document")
272
- )
246
+ raise ValueError(result.get("message", "Error executing batch update on document"))
273
247
 
274
248
  if not result: # Should be caught by error dict check
275
249
  raise ValueError(f"Failed to execute batch update on document '{document_id}'.")
@@ -42,9 +42,7 @@ async def drive_search_files(
42
42
  raise ValueError("Query cannot be empty")
43
43
 
44
44
  drive_service = DriveService()
45
- files = drive_service.search_files(
46
- query=query, page_size=page_size, shared_drive_id=shared_drive_id
47
- )
45
+ files = drive_service.search_files(query=query, page_size=page_size, shared_drive_id=shared_drive_id)
48
46
 
49
47
  if isinstance(files, dict) and files.get("error"):
50
48
  raise ValueError(f"Search failed: {files.get('message', 'Unknown error')}")
@@ -156,9 +154,7 @@ async def drive_create_folder(
156
154
  )
157
155
 
158
156
  if isinstance(result, dict) and result.get("error"):
159
- raise ValueError(
160
- f"Folder creation failed: {result.get('message', 'Unknown error')}"
161
- )
157
+ raise ValueError(f"Folder creation failed: {result.get('message', 'Unknown error')}")
162
158
 
163
159
  return result
164
160
 
@@ -37,9 +37,7 @@ async def sheets_create_spreadsheet(title: str) -> dict[str, Any]:
37
37
  raise ValueError(result.get("message", "Error creating spreadsheet"))
38
38
 
39
39
  if not result or not result.get("spreadsheet_id"):
40
- raise ValueError(
41
- f"Failed to create spreadsheet '{title}' or did not receive a spreadsheet ID."
42
- )
40
+ raise ValueError(f"Failed to create spreadsheet '{title}' or did not receive a spreadsheet ID.")
43
41
 
44
42
  return result
45
43
 
@@ -61,9 +59,7 @@ async def sheets_read_range(spreadsheet_id: str, range_a1: str) -> dict[str, Any
61
59
  A dictionary containing the range and a list of lists representing the cell values,
62
60
  or an error message.
63
61
  """
64
- logger.info(
65
- f"Executing sheets_read_range tool for spreadsheet_id: '{spreadsheet_id}', range: '{range_a1}'"
66
- )
62
+ logger.info(f"Executing sheets_read_range tool for spreadsheet_id: '{spreadsheet_id}', range: '{range_a1}'")
67
63
  if not spreadsheet_id or not spreadsheet_id.strip():
68
64
  raise ValueError("Spreadsheet ID cannot be empty.")
69
65
  if not range_a1 or not range_a1.strip():
@@ -75,12 +71,8 @@ async def sheets_read_range(spreadsheet_id: str, range_a1: str) -> dict[str, Any
75
71
  if isinstance(result, dict) and result.get("error"):
76
72
  raise ValueError(result.get("message", "Error reading range from spreadsheet"))
77
73
 
78
- if (
79
- not result or "values" not in result
80
- ): # Check for 'values' as it's key for successful read
81
- raise ValueError(
82
- f"Failed to read range '{range_a1}' from spreadsheet '{spreadsheet_id}'."
83
- )
74
+ if not result or "values" not in result: # Check for 'values' as it's key for successful read
75
+ raise ValueError(f"Failed to read range '{range_a1}' from spreadsheet '{spreadsheet_id}'.")
84
76
 
85
77
  return result
86
78
 
@@ -110,9 +102,7 @@ async def sheets_write_range(
110
102
  A dictionary detailing the update (updated range, number of cells, etc.),
111
103
  or an error message.
112
104
  """
113
- logger.info(
114
- f"Executing sheets_write_range tool for spreadsheet_id: '{spreadsheet_id}', range: '{range_a1}'"
115
- )
105
+ logger.info(f"Executing sheets_write_range tool for spreadsheet_id: '{spreadsheet_id}', range: '{range_a1}'")
116
106
  if not spreadsheet_id or not spreadsheet_id.strip():
117
107
  raise ValueError("Spreadsheet ID cannot be empty.")
118
108
  if not range_a1 or not range_a1.strip():
@@ -134,9 +124,7 @@ async def sheets_write_range(
134
124
  raise ValueError(result.get("message", "Error writing to range in spreadsheet"))
135
125
 
136
126
  if not result or not result.get("updated_range"):
137
- raise ValueError(
138
- f"Failed to write to range '{range_a1}' in spreadsheet '{spreadsheet_id}'."
139
- )
127
+ raise ValueError(f"Failed to write to range '{range_a1}' in spreadsheet '{spreadsheet_id}'.")
140
128
 
141
129
  return result
142
130
 
@@ -167,9 +155,7 @@ async def sheets_append_rows(
167
155
  A dictionary detailing the append operation (e.g., range of appended data),
168
156
  or an error message.
169
157
  """
170
- logger.info(
171
- f"Executing sheets_append_rows tool for spreadsheet_id: '{spreadsheet_id}', range: '{range_a1}'"
172
- )
158
+ logger.info(f"Executing sheets_append_rows tool for spreadsheet_id: '{spreadsheet_id}', range: '{range_a1}'")
173
159
  if not spreadsheet_id or not spreadsheet_id.strip():
174
160
  raise ValueError("Spreadsheet ID cannot be empty.")
175
161
  if not range_a1 or not range_a1.strip():
@@ -181,9 +167,7 @@ async def sheets_append_rows(
181
167
  if value_input_option not in ["USER_ENTERED", "RAW"]:
182
168
  raise ValueError("value_input_option must be either 'USER_ENTERED' or 'RAW'.")
183
169
  if insert_data_option not in ["INSERT_ROWS", "OVERWRITE"]:
184
- raise ValueError(
185
- "insert_data_option must be either 'INSERT_ROWS' or 'OVERWRITE'."
186
- )
170
+ raise ValueError("insert_data_option must be either 'INSERT_ROWS' or 'OVERWRITE'.")
187
171
 
188
172
  sheets_service = SheetsService()
189
173
  result = sheets_service.append_rows(
@@ -198,9 +182,7 @@ async def sheets_append_rows(
198
182
  raise ValueError(result.get("message", "Error appending rows to spreadsheet"))
199
183
 
200
184
  if not result: # Check for empty or None result as well
201
- raise ValueError(
202
- f"Failed to append rows to range '{range_a1}' in spreadsheet '{spreadsheet_id}'."
203
- )
185
+ raise ValueError(f"Failed to append rows to range '{range_a1}' in spreadsheet '{spreadsheet_id}'.")
204
186
 
205
187
  return result
206
188
 
@@ -221,26 +203,20 @@ async def sheets_clear_range(spreadsheet_id: str, range_a1: str) -> dict[str, An
221
203
  Returns:
222
204
  A dictionary confirming the cleared range, or an error message.
223
205
  """
224
- logger.info(
225
- f"Executing sheets_clear_range tool for spreadsheet_id: '{spreadsheet_id}', range: '{range_a1}'"
226
- )
206
+ logger.info(f"Executing sheets_clear_range tool for spreadsheet_id: '{spreadsheet_id}', range: '{range_a1}'")
227
207
  if not spreadsheet_id or not spreadsheet_id.strip():
228
208
  raise ValueError("Spreadsheet ID cannot be empty.")
229
209
  if not range_a1 or not range_a1.strip():
230
210
  raise ValueError("Range (A1 notation) cannot be empty.")
231
211
 
232
212
  sheets_service = SheetsService()
233
- result = sheets_service.clear_range(
234
- spreadsheet_id=spreadsheet_id, range_a1=range_a1
235
- )
213
+ result = sheets_service.clear_range(spreadsheet_id=spreadsheet_id, range_a1=range_a1)
236
214
 
237
215
  if isinstance(result, dict) and result.get("error"):
238
216
  raise ValueError(result.get("message", "Error clearing range in spreadsheet"))
239
217
 
240
218
  if not result or not result.get("cleared_range"):
241
- raise ValueError(
242
- f"Failed to clear range '{range_a1}' in spreadsheet '{spreadsheet_id}'."
243
- )
219
+ raise ValueError(f"Failed to clear range '{range_a1}' in spreadsheet '{spreadsheet_id}'.")
244
220
 
245
221
  return result
246
222
 
@@ -261,9 +237,7 @@ async def sheets_add_sheet(spreadsheet_id: str, title: str) -> dict[str, Any]:
261
237
  A dictionary containing properties of the newly created sheet (like sheetId, title, index),
262
238
  or an error message.
263
239
  """
264
- logger.info(
265
- f"Executing sheets_add_sheet tool for spreadsheet_id: '{spreadsheet_id}', title: '{title}'"
266
- )
240
+ logger.info(f"Executing sheets_add_sheet tool for spreadsheet_id: '{spreadsheet_id}', title: '{title}'")
267
241
  if not spreadsheet_id or not spreadsheet_id.strip():
268
242
  raise ValueError("Spreadsheet ID cannot be empty.")
269
243
  if not title or not title.strip():
@@ -276,9 +250,7 @@ async def sheets_add_sheet(spreadsheet_id: str, title: str) -> dict[str, Any]:
276
250
  raise ValueError(result.get("message", "Error adding sheet to spreadsheet"))
277
251
 
278
252
  if not result or not result.get("sheet_properties"):
279
- raise ValueError(
280
- f"Failed to add sheet '{title}' to spreadsheet '{spreadsheet_id}'."
281
- )
253
+ raise ValueError(f"Failed to add sheet '{title}' to spreadsheet '{spreadsheet_id}'.")
282
254
 
283
255
  return result
284
256
 
@@ -298,25 +270,19 @@ async def sheets_delete_sheet(spreadsheet_id: str, sheet_id: int) -> dict[str, A
298
270
  Returns:
299
271
  A dictionary confirming the deletion or an error message.
300
272
  """
301
- logger.info(
302
- f"Executing sheets_delete_sheet tool for spreadsheet_id: '{spreadsheet_id}', sheet_id: {sheet_id}"
303
- )
273
+ logger.info(f"Executing sheets_delete_sheet tool for spreadsheet_id: '{spreadsheet_id}', sheet_id: {sheet_id}")
304
274
  if not spreadsheet_id or not spreadsheet_id.strip():
305
275
  raise ValueError("Spreadsheet ID cannot be empty.")
306
276
  if not isinstance(sheet_id, int):
307
277
  raise ValueError("Sheet ID must be an integer.")
308
278
 
309
279
  sheets_service = SheetsService()
310
- result = sheets_service.delete_sheet(
311
- spreadsheet_id=spreadsheet_id, sheet_id=sheet_id
312
- )
280
+ result = sheets_service.delete_sheet(spreadsheet_id=spreadsheet_id, sheet_id=sheet_id)
313
281
 
314
282
  if isinstance(result, dict) and result.get("error"):
315
283
  raise ValueError(result.get("message", "Error deleting sheet from spreadsheet"))
316
284
 
317
285
  if not result or not result.get("success"):
318
- raise ValueError(
319
- f"Failed to delete sheet ID '{sheet_id}' from spreadsheet '{spreadsheet_id}'."
320
- )
286
+ raise ValueError(f"Failed to delete sheet ID '{sheet_id}' from spreadsheet '{spreadsheet_id}'.")
321
287
 
322
288
  return result