workspace-mcp 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,316 @@
1
+ """
2
+ Google Slides MCP Tools
3
+
4
+ This module provides MCP tools for interacting with Google Slides API.
5
+ """
6
+
7
+ import logging
8
+ import asyncio
9
+ from typing import List, Optional, Dict, Any
10
+
11
+ from mcp import types
12
+ from googleapiclient.errors import HttpError
13
+
14
+ from auth.service_decorator import require_google_service
15
+ from core.server import server
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ @server.tool()
21
+ @require_google_service("slides", "slides")
22
+ async def create_presentation(
23
+ service,
24
+ user_google_email: str,
25
+ title: str = "Untitled Presentation"
26
+ ) -> str:
27
+ """
28
+ Create a new Google Slides presentation.
29
+
30
+ Args:
31
+ user_google_email (str): The user's Google email address. Required.
32
+ title (str): The title for the new presentation. Defaults to "Untitled Presentation".
33
+
34
+ Returns:
35
+ str: Details about the created presentation including ID and URL.
36
+ """
37
+ logger.info(f"[create_presentation] Invoked. Email: '{user_google_email}', Title: '{title}'")
38
+
39
+ try:
40
+ body = {
41
+ 'title': title
42
+ }
43
+
44
+ result = await asyncio.to_thread(
45
+ service.presentations().create(body=body).execute
46
+ )
47
+
48
+ presentation_id = result.get('presentationId')
49
+ presentation_url = f"https://docs.google.com/presentation/d/{presentation_id}/edit"
50
+
51
+ confirmation_message = f"""Presentation Created Successfully for {user_google_email}:
52
+ - Title: {title}
53
+ - Presentation ID: {presentation_id}
54
+ - URL: {presentation_url}
55
+ - Slides: {len(result.get('slides', []))} slide(s) created"""
56
+
57
+ logger.info(f"Presentation created successfully for {user_google_email}")
58
+ return confirmation_message
59
+
60
+ except HttpError as error:
61
+ message = f"API error: {error}. You might need to re-authenticate. LLM: Try 'start_google_auth' with the user's email ({user_google_email}) and service_name='Google Slides'."
62
+ logger.error(message, exc_info=True)
63
+ raise Exception(message)
64
+ except Exception as e:
65
+ message = f"Unexpected error: {e}."
66
+ logger.exception(message)
67
+ raise Exception(message)
68
+
69
+
70
+ @server.tool()
71
+ @require_google_service("slides", "slides_read")
72
+ async def get_presentation(
73
+ service,
74
+ user_google_email: str,
75
+ presentation_id: str
76
+ ) -> str:
77
+ """
78
+ Get details about a Google Slides presentation.
79
+
80
+ Args:
81
+ user_google_email (str): The user's Google email address. Required.
82
+ presentation_id (str): The ID of the presentation to retrieve.
83
+
84
+ Returns:
85
+ str: Details about the presentation including title, slides count, and metadata.
86
+ """
87
+ logger.info(f"[get_presentation] Invoked. Email: '{user_google_email}', ID: '{presentation_id}'")
88
+
89
+ try:
90
+ result = await asyncio.to_thread(
91
+ service.presentations().get(presentationId=presentation_id).execute
92
+ )
93
+
94
+ title = result.get('title', 'Untitled')
95
+ slides = result.get('slides', [])
96
+ page_size = result.get('pageSize', {})
97
+
98
+ slides_info = []
99
+ for i, slide in enumerate(slides, 1):
100
+ slide_id = slide.get('objectId', 'Unknown')
101
+ page_elements = slide.get('pageElements', [])
102
+ slides_info.append(f" Slide {i}: ID {slide_id}, {len(page_elements)} element(s)")
103
+
104
+ confirmation_message = f"""Presentation Details for {user_google_email}:
105
+ - Title: {title}
106
+ - Presentation ID: {presentation_id}
107
+ - URL: https://docs.google.com/presentation/d/{presentation_id}/edit
108
+ - Total Slides: {len(slides)}
109
+ - Page Size: {page_size.get('width', {}).get('magnitude', 'Unknown')} x {page_size.get('height', {}).get('magnitude', 'Unknown')} {page_size.get('width', {}).get('unit', '')}
110
+
111
+ Slides Breakdown:
112
+ {chr(10).join(slides_info) if slides_info else ' No slides found'}"""
113
+
114
+ logger.info(f"Presentation retrieved successfully for {user_google_email}")
115
+ return confirmation_message
116
+
117
+ except HttpError as error:
118
+ message = f"API error: {error}. You might need to re-authenticate. LLM: Try 'start_google_auth' with the user's email ({user_google_email}) and service_name='Google Slides'."
119
+ logger.error(message, exc_info=True)
120
+ raise Exception(message)
121
+ except Exception as e:
122
+ message = f"Unexpected error: {e}."
123
+ logger.exception(message)
124
+ raise Exception(message)
125
+
126
+
127
+ @server.tool()
128
+ @require_google_service("slides", "slides")
129
+ async def batch_update_presentation(
130
+ service,
131
+ user_google_email: str,
132
+ presentation_id: str,
133
+ requests: List[Dict[str, Any]]
134
+ ) -> str:
135
+ """
136
+ Apply batch updates to a Google Slides presentation.
137
+
138
+ Args:
139
+ user_google_email (str): The user's Google email address. Required.
140
+ presentation_id (str): The ID of the presentation to update.
141
+ requests (List[Dict[str, Any]]): List of update requests to apply.
142
+
143
+ Returns:
144
+ str: Details about the batch update operation results.
145
+ """
146
+ logger.info(f"[batch_update_presentation] Invoked. Email: '{user_google_email}', ID: '{presentation_id}', Requests: {len(requests)}")
147
+
148
+ try:
149
+ body = {
150
+ 'requests': requests
151
+ }
152
+
153
+ result = await asyncio.to_thread(
154
+ service.presentations().batchUpdate(
155
+ presentationId=presentation_id,
156
+ body=body
157
+ ).execute
158
+ )
159
+
160
+ replies = result.get('replies', [])
161
+
162
+ confirmation_message = f"""Batch Update Completed for {user_google_email}:
163
+ - Presentation ID: {presentation_id}
164
+ - URL: https://docs.google.com/presentation/d/{presentation_id}/edit
165
+ - Requests Applied: {len(requests)}
166
+ - Replies Received: {len(replies)}"""
167
+
168
+ if replies:
169
+ confirmation_message += "\n\nUpdate Results:"
170
+ for i, reply in enumerate(replies, 1):
171
+ if 'createSlide' in reply:
172
+ slide_id = reply['createSlide'].get('objectId', 'Unknown')
173
+ confirmation_message += f"\n Request {i}: Created slide with ID {slide_id}"
174
+ elif 'createShape' in reply:
175
+ shape_id = reply['createShape'].get('objectId', 'Unknown')
176
+ confirmation_message += f"\n Request {i}: Created shape with ID {shape_id}"
177
+ else:
178
+ confirmation_message += f"\n Request {i}: Operation completed"
179
+
180
+ logger.info(f"Batch update completed successfully for {user_google_email}")
181
+ return confirmation_message
182
+
183
+ except HttpError as error:
184
+ message = f"API error: {error}. You might need to re-authenticate. LLM: Try 'start_google_auth' with the user's email ({user_google_email}) and service_name='Google Slides'."
185
+ logger.error(message, exc_info=True)
186
+ raise Exception(message)
187
+ except Exception as e:
188
+ message = f"Unexpected error: {e}."
189
+ logger.exception(message)
190
+ raise Exception(message)
191
+
192
+
193
+ @server.tool()
194
+ @require_google_service("slides", "slides_read")
195
+ async def get_page(
196
+ service,
197
+ user_google_email: str,
198
+ presentation_id: str,
199
+ page_object_id: str
200
+ ) -> str:
201
+ """
202
+ Get details about a specific page (slide) in a presentation.
203
+
204
+ Args:
205
+ user_google_email (str): The user's Google email address. Required.
206
+ presentation_id (str): The ID of the presentation.
207
+ page_object_id (str): The object ID of the page/slide to retrieve.
208
+
209
+ Returns:
210
+ str: Details about the specific page including elements and layout.
211
+ """
212
+ logger.info(f"[get_page] Invoked. Email: '{user_google_email}', Presentation: '{presentation_id}', Page: '{page_object_id}'")
213
+
214
+ try:
215
+ result = await asyncio.to_thread(
216
+ service.presentations().pages().get(
217
+ presentationId=presentation_id,
218
+ pageObjectId=page_object_id
219
+ ).execute
220
+ )
221
+
222
+ page_type = result.get('pageType', 'Unknown')
223
+ page_elements = result.get('pageElements', [])
224
+
225
+ elements_info = []
226
+ for element in page_elements:
227
+ element_id = element.get('objectId', 'Unknown')
228
+ if 'shape' in element:
229
+ shape_type = element['shape'].get('shapeType', 'Unknown')
230
+ elements_info.append(f" Shape: ID {element_id}, Type: {shape_type}")
231
+ elif 'table' in element:
232
+ table = element['table']
233
+ rows = table.get('rows', 0)
234
+ cols = table.get('columns', 0)
235
+ elements_info.append(f" Table: ID {element_id}, Size: {rows}x{cols}")
236
+ elif 'line' in element:
237
+ line_type = element['line'].get('lineType', 'Unknown')
238
+ elements_info.append(f" Line: ID {element_id}, Type: {line_type}")
239
+ else:
240
+ elements_info.append(f" Element: ID {element_id}, Type: Unknown")
241
+
242
+ confirmation_message = f"""Page Details for {user_google_email}:
243
+ - Presentation ID: {presentation_id}
244
+ - Page ID: {page_object_id}
245
+ - Page Type: {page_type}
246
+ - Total Elements: {len(page_elements)}
247
+
248
+ Page Elements:
249
+ {chr(10).join(elements_info) if elements_info else ' No elements found'}"""
250
+
251
+ logger.info(f"Page retrieved successfully for {user_google_email}")
252
+ return confirmation_message
253
+
254
+ except HttpError as error:
255
+ message = f"API error: {error}. You might need to re-authenticate. LLM: Try 'start_google_auth' with the user's email ({user_google_email}) and service_name='Google Slides'."
256
+ logger.error(message, exc_info=True)
257
+ raise Exception(message)
258
+ except Exception as e:
259
+ message = f"Unexpected error: {e}."
260
+ logger.exception(message)
261
+ raise Exception(message)
262
+
263
+
264
+ @server.tool()
265
+ @require_google_service("slides", "slides_read")
266
+ async def get_page_thumbnail(
267
+ service,
268
+ user_google_email: str,
269
+ presentation_id: str,
270
+ page_object_id: str,
271
+ thumbnail_size: str = "MEDIUM"
272
+ ) -> str:
273
+ """
274
+ Generate a thumbnail URL for a specific page (slide) in a presentation.
275
+
276
+ Args:
277
+ user_google_email (str): The user's Google email address. Required.
278
+ presentation_id (str): The ID of the presentation.
279
+ page_object_id (str): The object ID of the page/slide.
280
+ thumbnail_size (str): Size of thumbnail ("LARGE", "MEDIUM", "SMALL"). Defaults to "MEDIUM".
281
+
282
+ Returns:
283
+ str: URL to the generated thumbnail image.
284
+ """
285
+ logger.info(f"[get_page_thumbnail] Invoked. Email: '{user_google_email}', Presentation: '{presentation_id}', Page: '{page_object_id}', Size: '{thumbnail_size}'")
286
+
287
+ try:
288
+ result = await asyncio.to_thread(
289
+ service.presentations().pages().getThumbnail(
290
+ presentationId=presentation_id,
291
+ pageObjectId=page_object_id,
292
+ thumbnailPropertiesImageSize=thumbnail_size
293
+ ).execute
294
+ )
295
+
296
+ thumbnail_url = result.get('contentUrl', '')
297
+
298
+ confirmation_message = f"""Thumbnail Generated for {user_google_email}:
299
+ - Presentation ID: {presentation_id}
300
+ - Page ID: {page_object_id}
301
+ - Thumbnail Size: {thumbnail_size}
302
+ - Thumbnail URL: {thumbnail_url}
303
+
304
+ You can view or download the thumbnail using the provided URL."""
305
+
306
+ logger.info(f"Thumbnail generated successfully for {user_google_email}")
307
+ return confirmation_message
308
+
309
+ except HttpError as error:
310
+ message = f"API error: {error}. You might need to re-authenticate. LLM: Try 'start_google_auth' with the user's email ({user_google_email}) and service_name='Google Slides'."
311
+ logger.error(message, exc_info=True)
312
+ raise Exception(message)
313
+ except Exception as e:
314
+ message = f"Unexpected error: {e}."
315
+ logger.exception(message)
316
+ raise Exception(message)
main.py ADDED
@@ -0,0 +1,160 @@
1
+ import argparse
2
+ import logging
3
+ import os
4
+ import sys
5
+
6
+ # Local imports
7
+ from core.server import server, set_transport_mode
8
+ from core.utils import check_credentials_directory_permissions
9
+
10
+ logging.basicConfig(
11
+ level=logging.INFO,
12
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
13
+ )
14
+ logger = logging.getLogger(__name__)
15
+
16
+ try:
17
+ root_logger = logging.getLogger()
18
+ log_file_dir = os.path.dirname(os.path.abspath(__file__))
19
+ log_file_path = os.path.join(log_file_dir, 'mcp_server_debug.log')
20
+
21
+ file_handler = logging.FileHandler(log_file_path, mode='a')
22
+ file_handler.setLevel(logging.DEBUG)
23
+
24
+ file_formatter = logging.Formatter(
25
+ '%(asctime)s - %(name)s - %(levelname)s - %(process)d - %(threadName)s '
26
+ '[%(module)s.%(funcName)s:%(lineno)d] - %(message)s'
27
+ )
28
+ file_handler.setFormatter(file_formatter)
29
+ root_logger.addHandler(file_handler)
30
+
31
+ logger.debug(f"Detailed file logging configured to: {log_file_path}")
32
+ except Exception as e:
33
+ sys.stderr.write(f"CRITICAL: Failed to set up file logging to '{log_file_path}': {e}\n")
34
+
35
+ def main():
36
+ """
37
+ Main entry point for the Google Workspace MCP server.
38
+ Uses FastMCP's native streamable-http transport.
39
+ """
40
+ # Parse command line arguments
41
+ parser = argparse.ArgumentParser(description='Google Workspace MCP Server')
42
+ parser.add_argument('--single-user', action='store_true',
43
+ help='Run in single-user mode - bypass session mapping and use any credentials from ./credentials directory')
44
+ parser.add_argument('--tools', nargs='*',
45
+ choices=['gmail', 'drive', 'calendar', 'docs', 'sheets', 'chat', 'forms', 'slides'],
46
+ help='Specify which tools to register. If not provided, all tools are registered.')
47
+ parser.add_argument('--transport', choices=['stdio', 'streamable-http'], default='stdio',
48
+ help='Transport mode: stdio (default) or streamable-http')
49
+ args = parser.parse_args()
50
+
51
+ # Set port and base URI once for reuse throughout the function
52
+ port = int(os.getenv("PORT", os.getenv("WORKSPACE_MCP_PORT", 8000)))
53
+ base_uri = os.getenv("WORKSPACE_MCP_BASE_URI", "http://localhost")
54
+
55
+ print("šŸ”§ Google Workspace MCP Server")
56
+ print("=" * 35)
57
+ print("šŸ“‹ Server Information:")
58
+ print(f" šŸ“¦ Version: 0.1.1")
59
+ print(f" 🌐 Transport: {args.transport}")
60
+ if args.transport == 'streamable-http':
61
+ print(f" šŸ”— URL: {base_uri}:{port}")
62
+ print(f" šŸ” OAuth Callback: {base_uri}:{port}/oauth2callback")
63
+ print(f" šŸ‘¤ Mode: {'Single-user' if args.single_user else 'Multi-user'}")
64
+ print(f" šŸ Python: {sys.version.split()[0]}")
65
+ print()
66
+
67
+ # Import tool modules to register them with the MCP server via decorators
68
+ tool_imports = {
69
+ 'gmail': lambda: __import__('gmail.gmail_tools'),
70
+ 'drive': lambda: __import__('gdrive.drive_tools'),
71
+ 'calendar': lambda: __import__('gcalendar.calendar_tools'),
72
+ 'docs': lambda: __import__('gdocs.docs_tools'),
73
+ 'sheets': lambda: __import__('gsheets.sheets_tools'),
74
+ 'chat': lambda: __import__('gchat.chat_tools'),
75
+ 'forms': lambda: __import__('gforms.forms_tools'),
76
+ 'slides': lambda: __import__('gslides.slides_tools')
77
+ }
78
+
79
+ tool_icons = {
80
+ 'gmail': 'šŸ“§',
81
+ 'drive': 'šŸ“',
82
+ 'calendar': 'šŸ“…',
83
+ 'docs': 'šŸ“„',
84
+ 'sheets': 'šŸ“Š',
85
+ 'chat': 'šŸ’¬',
86
+ 'forms': 'šŸ“',
87
+ 'slides': 'šŸ–¼ļø'
88
+ }
89
+
90
+ # Import specified tools or all tools if none specified
91
+ tools_to_import = args.tools if args.tools is not None else tool_imports.keys()
92
+ print(f"šŸ› ļø Loading {len(tools_to_import)} tool module{'s' if len(tools_to_import) != 1 else ''}:")
93
+ for tool in tools_to_import:
94
+ tool_imports[tool]()
95
+ print(f" {tool_icons[tool]} {tool.title()} - Google {tool.title()} API integration")
96
+ print()
97
+
98
+ print(f"šŸ“Š Configuration Summary:")
99
+ print(f" šŸ”§ Tools Enabled: {len(tools_to_import)}/{len(tool_imports)}")
100
+ print(f" šŸ”‘ Auth Method: OAuth 2.0 with PKCE")
101
+ print(f" šŸ“ Log Level: {logging.getLogger().getEffectiveLevel()}")
102
+ print()
103
+
104
+ # Set global single-user mode flag
105
+ if args.single_user:
106
+ os.environ['MCP_SINGLE_USER_MODE'] = '1'
107
+ print("šŸ” Single-user mode enabled")
108
+ print()
109
+
110
+ # Check credentials directory permissions before starting
111
+ try:
112
+ print("šŸ” Checking credentials directory permissions...")
113
+ check_credentials_directory_permissions()
114
+ print("āœ… Credentials directory permissions verified")
115
+ print()
116
+ except (PermissionError, OSError) as e:
117
+ print(f"āŒ Credentials directory permission check failed: {e}")
118
+ print(" Please ensure the service has write permissions to create/access the .credentials directory")
119
+ logger.error(f"Failed credentials directory permission check: {e}")
120
+ sys.exit(1)
121
+
122
+ try:
123
+ # Set transport mode for OAuth callback handling
124
+ set_transport_mode(args.transport)
125
+
126
+ if args.transport == 'streamable-http':
127
+ print(f"šŸš€ Starting server on {base_uri}:{port}")
128
+ else:
129
+ print("šŸš€ Starting server in stdio mode")
130
+ # Start minimal OAuth callback server for stdio mode
131
+ from auth.oauth_callback_server import ensure_oauth_callback_available
132
+ if ensure_oauth_callback_available('stdio', port, base_uri):
133
+ print(f" OAuth callback server started on {base_uri}:{port}/oauth2callback")
134
+ else:
135
+ print(" āš ļø Warning: Failed to start OAuth callback server")
136
+
137
+ print(" Ready for MCP connections!")
138
+ print()
139
+
140
+ if args.transport == 'streamable-http':
141
+ # The server is already configured with port and server_url in core/server.py
142
+ server.run(transport="streamable-http")
143
+ else:
144
+ server.run()
145
+ except KeyboardInterrupt:
146
+ print("\nšŸ‘‹ Server shutdown requested")
147
+ # Clean up OAuth callback server if running
148
+ from auth.oauth_callback_server import cleanup_oauth_callback_server
149
+ cleanup_oauth_callback_server()
150
+ sys.exit(0)
151
+ except Exception as e:
152
+ print(f"\nāŒ Server error: {e}")
153
+ logger.error(f"Unexpected error running server: {e}", exc_info=True)
154
+ # Clean up OAuth callback server if running
155
+ from auth.oauth_callback_server import cleanup_oauth_callback_server
156
+ cleanup_oauth_callback_server()
157
+ sys.exit(1)
158
+
159
+ if __name__ == "__main__":
160
+ main()
@@ -0,0 +1,29 @@
1
+ Metadata-Version: 2.4
2
+ Name: workspace-mcp
3
+ Version: 0.2.0
4
+ Summary: Comprehensive, highly performant Google Workspace Streamable HTTP & SSE MCP Server for Calendar, Gmail, Docs, Sheets, Slides & Drive
5
+ Author-email: Taylor Wilsdon <taylor@taylorwilsdon.com>
6
+ License-Expression: MIT
7
+ Keywords: mcp,google,workspace,llm,ai,claude,model,context,protocol,server
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Natural Language :: English
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
18
+ Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
19
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.11
22
+ License-File: LICENSE
23
+ Requires-Dist: fastapi>=0.115.12
24
+ Requires-Dist: fastmcp>=2.3.3
25
+ Requires-Dist: google-api-python-client>=2.168.0
26
+ Requires-Dist: google-auth-httplib2>=0.2.0
27
+ Requires-Dist: google-auth-oauthlib>=1.2.2
28
+ Requires-Dist: httpx>=0.28.1
29
+ Dynamic: license-file
@@ -0,0 +1,32 @@
1
+ main.py,sha256=0BYBsSW2f-hLOhAZpSlqLNA3ccvmV-yOSt4ZY8qAjJA,6650
2
+ auth/__init__.py,sha256=gPCU3GE-SLy91S3D3CbX-XfKBm6hteK_VSPKx7yjT5s,42
3
+ auth/google_auth.py,sha256=OdKFI7kW-R0peZutv9VXCnDvxH5jAtbeFYiXK9sxqjQ,25998
4
+ auth/oauth_callback_server.py,sha256=-vaaLO9f7oz6QsHUXjRFHjlEleFm0Y9ccBB33YX22wQ,9177
5
+ auth/oauth_responses.py,sha256=qbirSB4d7mBRKcJKqGLrJxRAPaLHqObf9t-VMAq6UKA,7020
6
+ auth/scopes.py,sha256=kMRdFN0wLyipFkp7IitTHs-M6zhZD-oieVd7fylueBc,3320
7
+ auth/service_decorator.py,sha256=jux1Fboa_ncau_5XcGekSpuD1USPvmIpDu9hNBA15Vk,15267
8
+ core/__init__.py,sha256=AHVKdPl6v4lUFm2R-KuGuAgEmCyfxseMeLGtntMcqCs,43
9
+ core/server.py,sha256=s89ptSJ9f1MFZ6pxoA0f0Ue3yHVZl6Ukndx4ZcQ0Y1M,9682
10
+ core/utils.py,sha256=Xsc2E0LSQl-tjcC95CtnmNFTJi8K8iwJvTUIjbRtu4o,8830
11
+ gcalendar/__init__.py,sha256=D5fSdAwbeomoaj7XAdxSnIy-NVKNkpExs67175bOtfc,46
12
+ gcalendar/calendar_tools.py,sha256=ptS_iyi6JBPhstbdPnRD5ruZtKzkAEbfaimiPabYLTo,20982
13
+ gchat/__init__.py,sha256=XBjH4SbtULfZHgFCxk3moel5XqG599HCgZWl_veIncg,88
14
+ gchat/chat_tools.py,sha256=jm2trdYhfqNfsMfoAGuhzkEvjA8tSOL1d4QwSPv9K1w,8832
15
+ gdocs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ gdocs/docs_tools.py,sha256=3530NcSFkWemMrJ6JRrTQuFUaZLv6gYSU8BIWdOPgak,9571
17
+ gdrive/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ gdrive/drive_tools.py,sha256=yBpV5DKwyhd83M2gSdwr3X-oWmAyBtpbl9L2JIdwNNU,15708
19
+ gforms/__init__.py,sha256=pL91XixrEp9YjpM-AYwONIEfeCP2OumkEG0Io5V4boE,37
20
+ gforms/forms_tools.py,sha256=8dX3JVFWqQUESXTi7WgynqZh7FIufcXZI1c3ph_F3bk,12248
21
+ gmail/__init__.py,sha256=l8PZ4_7Oet6ZE7tVu9oQ3-BaRAmI4YzAO86kf9uu6pU,60
22
+ gmail/gmail_tools.py,sha256=sit06sqietJ4fNbr1S1WfucxAsf3wWmoFte-fBHzfPk,29057
23
+ gsheets/__init__.py,sha256=jFfhD52w_EOVw6N5guf_dIc9eP2khW_eS9UAPJg_K3k,446
24
+ gsheets/sheets_tools.py,sha256=mZU7kf3kRRhjz406rDBlVtwNMAjye2F81W1cveXlBFY,14481
25
+ gslides/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
+ gslides/slides_tools.py,sha256=NAkApdjkX7GtZRksy7fLmjmbsa1khkeAJKNN6NZe0t8,11846
27
+ workspace_mcp-0.2.0.dist-info/licenses/LICENSE,sha256=bB8L7rIyRy5o-WHxGgvRuY8hUTzNu4h3DTkvyV8XFJo,1070
28
+ workspace_mcp-0.2.0.dist-info/METADATA,sha256=G9XQNqZbqoQegiamRh9wsIbpAbLkl7Jxx1D7xElAFA0,1287
29
+ workspace_mcp-0.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
30
+ workspace_mcp-0.2.0.dist-info/entry_points.txt,sha256=FMg6KOfvwWgrLe_qDKr1Cm8NFdZ_44JnrF9FMw1EE1Q,51
31
+ workspace_mcp-0.2.0.dist-info/top_level.txt,sha256=Y8mAkTitLNE2zZEJ-DbqR9R7Cs1V1MMf-UploVdOvlw,73
32
+ workspace_mcp-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ google-workspace-mcp = main:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Taylor Wilsdon
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,11 @@
1
+ auth
2
+ core
3
+ gcalendar
4
+ gchat
5
+ gdocs
6
+ gdrive
7
+ gforms
8
+ gmail
9
+ gsheets
10
+ gslides
11
+ main