google-workspace-mcp 1.0.0__tar.gz → 1.0.2__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.
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/PKG-INFO +2 -2
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/pyproject.toml +2 -2
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/__main__.py +7 -7
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/resources/sheets_resources.py +9 -29
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/services/calendar.py +4 -14
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/services/drive.py +13 -43
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/services/sheets_service.py +36 -115
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/tools/docs_tools.py +13 -39
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/tools/drive.py +2 -6
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/tools/sheets_tools.py +17 -51
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/.gitignore +0 -0
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/README.md +0 -0
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/__init__.py +0 -0
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/app.py +0 -0
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/auth/__init__.py +0 -0
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/auth/gauth.py +0 -0
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/config.py +0 -0
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/prompts/__init__.py +0 -0
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/prompts/calendar.py +0 -0
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/prompts/drive.py +0 -0
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/prompts/gmail.py +0 -0
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/prompts/slides.py +0 -0
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/resources/__init__.py +0 -0
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/resources/calendar.py +0 -0
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/resources/drive.py +0 -0
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/resources/gmail.py +0 -0
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/resources/slides.py +0 -0
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/services/__init__.py +0 -0
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/services/base.py +0 -0
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/services/docs_service.py +0 -0
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/services/gmail.py +0 -0
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/services/slides.py +0 -0
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/tools/__init__.py +0 -0
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/tools/calendar.py +0 -0
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/tools/gmail.py +0 -0
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/tools/slides.py +0 -0
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/utils/__init__.py +0 -0
- {google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/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.
|
3
|
+
Version: 1.0.2
|
4
4
|
Summary: MCP server for Google Workspace integration
|
5
5
|
Author-email: Arclio Team <info@arclio.com>
|
6
6
|
License: MIT
|
@@ -11,7 +11,7 @@ Requires-Dist: google-auth-httplib2>=0.1.0
|
|
11
11
|
Requires-Dist: google-auth-oauthlib>=1.0.0
|
12
12
|
Requires-Dist: google-auth>=2.22.0
|
13
13
|
Requires-Dist: markdown>=3.5.0
|
14
|
-
Requires-Dist: markdowndeck>=0.1.
|
14
|
+
Requires-Dist: markdowndeck>=0.1.1
|
15
15
|
Requires-Dist: mcp>=1.7.0
|
16
16
|
Requires-Dist: python-dotenv>=1.0.0
|
17
17
|
Requires-Dist: pytz>=2023.3
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "google-workspace-mcp"
|
7
|
-
version = "1.0.
|
7
|
+
version = "1.0.2"
|
8
8
|
description = "MCP server for Google Workspace integration"
|
9
9
|
authors = [
|
10
10
|
{name = "Arclio Team", email = "info@arclio.com"},
|
@@ -22,7 +22,7 @@ dependencies = [
|
|
22
22
|
"markdown>=3.5.0",
|
23
23
|
"beautifulsoup4>=4.12.0",
|
24
24
|
"mcp>=1.7.0",
|
25
|
-
"markdowndeck>=0.1.
|
25
|
+
"markdowndeck>=0.1.1",
|
26
26
|
]
|
27
27
|
|
28
28
|
[project.scripts]
|
{google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/__main__.py
RENAMED
@@ -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 (
|
25
|
-
|
26
|
-
|
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
|
-
|
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
|
-
|
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)
|
{google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/services/drive.py
RENAMED
@@ -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
|
-
|
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
|
-
|
232
|
-
), #
|
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}'.")
|
{google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/tools/drive.py
RENAMED
@@ -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
|
-
|
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
|
File without changes
|
File without changes
|
{google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/__init__.py
RENAMED
File without changes
|
File without changes
|
{google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/auth/__init__.py
RENAMED
File without changes
|
{google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/auth/gauth.py
RENAMED
File without changes
|
{google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/config.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/prompts/drive.py
RENAMED
File without changes
|
{google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/prompts/gmail.py
RENAMED
File without changes
|
{google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/prompts/slides.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/services/base.py
RENAMED
File without changes
|
File without changes
|
{google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/services/gmail.py
RENAMED
File without changes
|
File without changes
|
{google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/tools/__init__.py
RENAMED
File without changes
|
{google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/tools/calendar.py
RENAMED
File without changes
|
{google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/tools/gmail.py
RENAMED
File without changes
|
{google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/tools/slides.py
RENAMED
File without changes
|
{google_workspace_mcp-1.0.0 → google_workspace_mcp-1.0.2}/src/google_workspace_mcp/utils/__init__.py
RENAMED
File without changes
|
File without changes
|