universal-mcp 0.1.7rc2__py3-none-any.whl → 0.1.8__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.
- universal_mcp/__init__.py +0 -2
- universal_mcp/analytics.py +75 -0
- universal_mcp/applications/ahrefs/README.md +76 -0
- universal_mcp/applications/ahrefs/app.py +2291 -0
- universal_mcp/applications/application.py +95 -5
- universal_mcp/applications/calendly/README.md +78 -0
- universal_mcp/applications/calendly/__init__.py +0 -0
- universal_mcp/applications/calendly/app.py +1195 -0
- universal_mcp/applications/coda/README.md +133 -0
- universal_mcp/applications/coda/__init__.py +0 -0
- universal_mcp/applications/coda/app.py +3671 -0
- universal_mcp/applications/e2b/app.py +14 -35
- universal_mcp/applications/figma/README.md +74 -0
- universal_mcp/applications/figma/__init__.py +0 -0
- universal_mcp/applications/figma/app.py +1261 -0
- universal_mcp/applications/firecrawl/app.py +29 -32
- universal_mcp/applications/github/app.py +127 -85
- universal_mcp/applications/google_calendar/app.py +62 -138
- universal_mcp/applications/google_docs/app.py +47 -52
- universal_mcp/applications/google_drive/app.py +119 -113
- universal_mcp/applications/google_mail/app.py +124 -50
- universal_mcp/applications/google_sheet/app.py +89 -91
- universal_mcp/applications/markitdown/app.py +9 -8
- universal_mcp/applications/notion/app.py +254 -134
- universal_mcp/applications/perplexity/app.py +13 -45
- universal_mcp/applications/reddit/app.py +94 -85
- universal_mcp/applications/resend/app.py +12 -23
- universal_mcp/applications/{serp → serpapi}/app.py +14 -33
- universal_mcp/applications/tavily/app.py +11 -28
- universal_mcp/applications/wrike/README.md +71 -0
- universal_mcp/applications/wrike/__init__.py +0 -0
- universal_mcp/applications/wrike/app.py +1372 -0
- universal_mcp/applications/youtube/README.md +82 -0
- universal_mcp/applications/youtube/__init__.py +0 -0
- universal_mcp/applications/youtube/app.py +1428 -0
- universal_mcp/applications/zenquotes/app.py +12 -2
- universal_mcp/exceptions.py +9 -2
- universal_mcp/integrations/__init__.py +24 -1
- universal_mcp/integrations/agentr.py +27 -4
- universal_mcp/integrations/integration.py +143 -30
- universal_mcp/logger.py +3 -56
- universal_mcp/servers/__init__.py +6 -14
- universal_mcp/servers/server.py +201 -146
- universal_mcp/stores/__init__.py +7 -2
- universal_mcp/stores/store.py +103 -40
- universal_mcp/tools/__init__.py +3 -0
- universal_mcp/tools/adapters.py +43 -0
- universal_mcp/tools/func_metadata.py +213 -0
- universal_mcp/tools/tools.py +342 -0
- universal_mcp/utils/docgen.py +325 -119
- universal_mcp/utils/docstring_parser.py +179 -0
- universal_mcp/utils/dump_app_tools.py +33 -23
- universal_mcp/utils/installation.py +199 -8
- universal_mcp/utils/openapi.py +229 -46
- {universal_mcp-0.1.7rc2.dist-info → universal_mcp-0.1.8.dist-info}/METADATA +9 -5
- universal_mcp-0.1.8.dist-info/RECORD +81 -0
- universal_mcp-0.1.7rc2.dist-info/RECORD +0 -58
- /universal_mcp/{utils/bridge.py → applications/ahrefs/__init__.py} +0 -0
- /universal_mcp/applications/{serp → serpapi}/README.md +0 -0
- {universal_mcp-0.1.7rc2.dist-info → universal_mcp-0.1.8.dist-info}/WHEEL +0 -0
- {universal_mcp-0.1.7rc2.dist-info → universal_mcp-0.1.8.dist-info}/entry_points.txt +0 -0
@@ -11,17 +11,6 @@ class GoogleCalendarApp(APIApplication):
|
|
11
11
|
super().__init__(name="google-calendar", integration=integration)
|
12
12
|
self.base_api_url = "https://www.googleapis.com/calendar/v3/calendars/primary"
|
13
13
|
|
14
|
-
def _get_headers(self):
|
15
|
-
if not self.integration:
|
16
|
-
raise ValueError("Integration not configured for GoogleCalendarApp")
|
17
|
-
credentials = self.integration.get_credentials()
|
18
|
-
if "headers" in credentials:
|
19
|
-
return credentials["headers"]
|
20
|
-
return {
|
21
|
-
"Authorization": f"Bearer {credentials['access_token']}",
|
22
|
-
"Accept": "application/json",
|
23
|
-
}
|
24
|
-
|
25
14
|
def _format_datetime(self, dt_string: str) -> str:
|
26
15
|
"""Format a datetime string from ISO format to a human-readable format.
|
27
16
|
|
@@ -59,7 +48,8 @@ class GoogleCalendarApp(APIApplication):
|
|
59
48
|
def get_today_events(
|
60
49
|
self, days: int = 1, max_results: int = None, time_zone: str = None
|
61
50
|
) -> str:
|
62
|
-
"""
|
51
|
+
"""
|
52
|
+
Retrieves and formats events from Google Calendar for today or a specified number of future days, with optional result limiting and timezone specification.
|
63
53
|
|
64
54
|
Args:
|
65
55
|
days: Number of days to retrieve events for (default: 1, which is just today)
|
@@ -67,42 +57,36 @@ class GoogleCalendarApp(APIApplication):
|
|
67
57
|
time_zone: Time zone used in the response (optional, default is calendar's time zone)
|
68
58
|
|
69
59
|
Returns:
|
70
|
-
A formatted list of events or
|
60
|
+
A formatted string containing a list of calendar events with their times and IDs, or a message indicating no events were found
|
61
|
+
|
62
|
+
Raises:
|
63
|
+
HTTPError: Raised when the API request fails or returns an error status code
|
64
|
+
|
65
|
+
Tags:
|
66
|
+
fetch, list, calendar, events, date-time, important, api, formatting
|
71
67
|
"""
|
72
|
-
# Get today's date in ISO format
|
73
68
|
today = datetime.utcnow().date()
|
74
69
|
end_date = today + timedelta(days=days)
|
75
|
-
|
76
|
-
# Format dates for API
|
77
70
|
time_min = f"{today.isoformat()}T00:00:00Z"
|
78
71
|
time_max = f"{end_date.isoformat()}T00:00:00Z"
|
79
|
-
|
80
72
|
url = f"{self.base_api_url}/events"
|
81
|
-
|
82
|
-
# Build query parameters
|
83
73
|
params = {
|
84
74
|
"timeMin": time_min,
|
85
75
|
"timeMax": time_max,
|
86
76
|
"singleEvents": "true",
|
87
77
|
"orderBy": "startTime",
|
88
78
|
}
|
89
|
-
|
90
79
|
if max_results is not None:
|
91
80
|
params["maxResults"] = max_results
|
92
|
-
|
93
81
|
if time_zone:
|
94
82
|
params["timeZone"] = time_zone
|
95
|
-
|
96
83
|
date_range = "today" if days == 1 else f"the next {days} days"
|
97
84
|
logger.info(f"Retrieving calendar events for {date_range}")
|
98
|
-
|
99
85
|
response = self._get(url, params=params)
|
100
86
|
response.raise_for_status()
|
101
|
-
|
102
87
|
events = response.json().get("items", [])
|
103
88
|
if not events:
|
104
89
|
return f"No events scheduled for {date_range}."
|
105
|
-
|
106
90
|
result = f"Events for {date_range}:\n\n"
|
107
91
|
for event in events:
|
108
92
|
# Extract event date and time
|
@@ -137,62 +121,51 @@ class GoogleCalendarApp(APIApplication):
|
|
137
121
|
event_id = event.get("id", "No ID")
|
138
122
|
|
139
123
|
result += f"- {time_display}: {summary} (ID: {event_id})\n"
|
140
|
-
|
141
124
|
return result
|
142
125
|
|
143
126
|
def get_event(
|
144
127
|
self, event_id: str, max_attendees: int = None, time_zone: str = None
|
145
128
|
) -> str:
|
146
|
-
"""
|
129
|
+
"""
|
130
|
+
Retrieves and formats detailed information about a specific Google Calendar event by its ID
|
147
131
|
|
148
132
|
Args:
|
149
|
-
event_id: The
|
150
|
-
max_attendees: Optional. The maximum number of attendees to include in the response
|
151
|
-
time_zone: Optional.
|
133
|
+
event_id: The unique identifier of the calendar event to retrieve
|
134
|
+
max_attendees: Optional. The maximum number of attendees to include in the response. If None, includes all attendees
|
135
|
+
time_zone: Optional. The time zone to use for formatting dates in the response. If None, uses the calendar's default time zone
|
152
136
|
|
153
137
|
Returns:
|
154
|
-
A formatted event details
|
138
|
+
A formatted string containing comprehensive event details including summary, time, location, description, creator, organizer, recurrence status, and attendee information
|
139
|
+
|
140
|
+
Raises:
|
141
|
+
HTTPError: Raised when the API request fails or returns an error status code
|
142
|
+
JSONDecodeError: Raised when the API response cannot be parsed as JSON
|
143
|
+
|
144
|
+
Tags:
|
145
|
+
retrieve, calendar, event, format, api, important
|
155
146
|
"""
|
156
147
|
url = f"{self.base_api_url}/events/{event_id}"
|
157
|
-
|
158
|
-
# Build query parameters
|
159
148
|
params = {}
|
160
149
|
if max_attendees is not None:
|
161
150
|
params["maxAttendees"] = max_attendees
|
162
151
|
if time_zone:
|
163
152
|
params["timeZone"] = time_zone
|
164
|
-
|
165
153
|
logger.info(f"Retrieving calendar event with ID: {event_id}")
|
166
|
-
|
167
154
|
response = self._get(url, params=params)
|
168
155
|
response.raise_for_status()
|
169
|
-
|
170
156
|
event = response.json()
|
171
|
-
|
172
|
-
# Extract event details
|
173
157
|
summary = event.get("summary", "Untitled event")
|
174
158
|
description = event.get("description", "No description")
|
175
159
|
location = event.get("location", "No location specified")
|
176
|
-
|
177
|
-
# Format dates
|
178
160
|
start = event.get("start", {})
|
179
161
|
end = event.get("end", {})
|
180
|
-
|
181
162
|
start_time = start.get("dateTime", start.get("date", "Unknown"))
|
182
163
|
end_time = end.get("dateTime", end.get("date", "Unknown"))
|
183
|
-
|
184
|
-
# Format datetimes using the helper function
|
185
164
|
start_formatted = self._format_datetime(start_time)
|
186
165
|
end_formatted = self._format_datetime(end_time)
|
187
|
-
|
188
|
-
# Get creator and organizer
|
189
166
|
creator = event.get("creator", {}).get("email", "Unknown")
|
190
167
|
organizer = event.get("organizer", {}).get("email", "Unknown")
|
191
|
-
|
192
|
-
# Check if it's a recurring event
|
193
168
|
recurrence = "Yes" if "recurrence" in event else "No"
|
194
|
-
|
195
|
-
# Get attendees if any
|
196
169
|
attendees = event.get("attendees", [])
|
197
170
|
attendee_info = ""
|
198
171
|
if attendees:
|
@@ -211,8 +184,6 @@ class GoogleCalendarApp(APIApplication):
|
|
211
184
|
|
212
185
|
formatted_status = status_mapping.get(response_status, response_status)
|
213
186
|
attendee_info += f" {i}. {name} ({email}) - {formatted_status}\n"
|
214
|
-
|
215
|
-
# Format the response
|
216
187
|
result = f"Event: {summary}\n"
|
217
188
|
result += f"ID: {event_id}\n"
|
218
189
|
result += f"When: {start_formatted} to {end_formatted}\n"
|
@@ -222,7 +193,6 @@ class GoogleCalendarApp(APIApplication):
|
|
222
193
|
result += f"Organizer: {organizer}\n"
|
223
194
|
result += f"Recurring: {recurrence}\n"
|
224
195
|
result += attendee_info
|
225
|
-
|
226
196
|
return result
|
227
197
|
|
228
198
|
def list_events(
|
@@ -236,69 +206,58 @@ class GoogleCalendarApp(APIApplication):
|
|
236
206
|
time_zone: str = None,
|
237
207
|
page_token: str = None,
|
238
208
|
) -> str:
|
239
|
-
"""
|
209
|
+
"""
|
210
|
+
Retrieves and formats a list of events from Google Calendar with customizable filtering, sorting, and pagination options
|
240
211
|
|
241
212
|
Args:
|
242
213
|
max_results: Maximum number of events to return (default: 10, max: 2500)
|
243
|
-
time_min: Start time
|
244
|
-
time_max: End time
|
245
|
-
q: Free text search terms (searches summary, description, location, attendees
|
246
|
-
order_by:
|
247
|
-
single_events: Whether to expand recurring events (default: True)
|
248
|
-
time_zone: Time zone
|
249
|
-
page_token: Token for retrieving a specific page of results
|
214
|
+
time_min: Start time in ISO format (e.g., '2023-12-01T00:00:00Z'). Defaults to current time if not specified
|
215
|
+
time_max: End time in ISO format (e.g., '2023-12-31T23:59:59Z')
|
216
|
+
q: Free text search terms to filter events (searches across summary, description, location, attendees)
|
217
|
+
order_by: Sort order for results - either 'startTime' (default) or 'updated'
|
218
|
+
single_events: Whether to expand recurring events into individual instances (default: True)
|
219
|
+
time_zone: Time zone for response formatting (defaults to calendar's time zone)
|
220
|
+
page_token: Token for retrieving a specific page of results in paginated responses
|
250
221
|
|
251
222
|
Returns:
|
252
|
-
A formatted list of events or
|
223
|
+
A formatted string containing the list of events with details including summary, ID, start time, and location, or a message if no events are found
|
224
|
+
|
225
|
+
Raises:
|
226
|
+
HTTPError: Raised when the API request fails or returns an error status code
|
227
|
+
|
228
|
+
Tags:
|
229
|
+
list, calendar, events, search, filter, pagination, format, important
|
253
230
|
"""
|
254
231
|
url = f"{self.base_api_url}/events"
|
255
|
-
|
256
|
-
# Build query parameters
|
257
232
|
params = {
|
258
233
|
"maxResults": max_results,
|
259
234
|
"singleEvents": str(single_events).lower(),
|
260
235
|
"orderBy": order_by,
|
261
236
|
}
|
262
|
-
|
263
|
-
# Set time boundaries if provided, otherwise default to now for time_min
|
264
237
|
if time_min:
|
265
238
|
params["timeMin"] = time_min
|
266
239
|
else:
|
267
240
|
# Default to current time if not specified
|
268
241
|
now = datetime.utcnow().isoformat() + "Z" # 'Z' indicates UTC time
|
269
242
|
params["timeMin"] = now
|
270
|
-
|
271
243
|
if time_max:
|
272
244
|
params["timeMax"] = time_max
|
273
|
-
|
274
|
-
# Add optional filters if provided
|
275
245
|
if q:
|
276
246
|
params["q"] = q
|
277
|
-
|
278
247
|
if time_zone:
|
279
248
|
params["timeZone"] = time_zone
|
280
|
-
|
281
249
|
if page_token:
|
282
250
|
params["pageToken"] = page_token
|
283
|
-
|
284
251
|
logger.info(f"Retrieving calendar events with params: {params}")
|
285
|
-
|
286
252
|
response = self._get(url, params=params)
|
287
253
|
response.raise_for_status()
|
288
|
-
|
289
254
|
data = response.json()
|
290
255
|
events = data.get("items", [])
|
291
|
-
|
292
256
|
if not events:
|
293
257
|
return "No events found matching your criteria."
|
294
|
-
|
295
|
-
# Extract calendar information
|
296
258
|
calendar_summary = data.get("summary", "Your Calendar")
|
297
259
|
time_zone_info = data.get("timeZone", "Unknown")
|
298
|
-
|
299
260
|
result = f"Events from {calendar_summary} (Time Zone: {time_zone_info}):\n\n"
|
300
|
-
|
301
|
-
# Process and format each event
|
302
261
|
for i, event in enumerate(events, 1):
|
303
262
|
# Get basic event details
|
304
263
|
event_id = event.get("id", "No ID")
|
@@ -327,75 +286,53 @@ class GoogleCalendarApp(APIApplication):
|
|
327
286
|
# Add a separator between events
|
328
287
|
if i < len(events):
|
329
288
|
result += "\n"
|
330
|
-
|
331
|
-
# Add pagination info if available
|
332
289
|
if "nextPageToken" in data:
|
333
290
|
next_token = data.get("nextPageToken")
|
334
291
|
result += (
|
335
292
|
f"\nMore events available. Use page_token='{next_token}' to see more."
|
336
293
|
)
|
337
|
-
|
338
294
|
return result
|
339
295
|
|
340
296
|
def quick_add_event(self, text: str, send_updates: str = "none") -> str:
|
341
|
-
"""
|
342
|
-
|
343
|
-
This method allows you to quickly create an event using a simple text string,
|
344
|
-
similar to how you would add events in the Google Calendar UI.
|
297
|
+
"""
|
298
|
+
Creates a calendar event using natural language text input and returns a formatted confirmation message with event details.
|
345
299
|
|
346
300
|
Args:
|
347
|
-
text:
|
348
|
-
send_updates:
|
301
|
+
text: Natural language text describing the event (e.g., 'Meeting with John at Coffee Shop tomorrow 3pm-4pm')
|
302
|
+
send_updates: Specifies who should receive event notifications: 'all', 'externalOnly', or 'none' (default)
|
349
303
|
|
350
304
|
Returns:
|
351
|
-
A confirmation message with
|
305
|
+
A formatted string containing the confirmation message with event details including summary, time, location, and event ID
|
306
|
+
|
307
|
+
Raises:
|
308
|
+
HTTPError: Raised when the API request fails or returns an error status code
|
309
|
+
|
310
|
+
Tags:
|
311
|
+
create, calendar, event, quick-add, natural-language, important
|
352
312
|
"""
|
353
313
|
url = f"{self.base_api_url}/events/quickAdd"
|
354
|
-
|
355
|
-
# Use params argument instead of manually constructing URL
|
356
314
|
params = {"text": text, "sendUpdates": send_updates}
|
357
|
-
|
358
315
|
logger.info(f"Creating event via quickAdd: '{text}'")
|
359
|
-
|
360
|
-
# Pass params to _post method
|
361
316
|
response = self._post(url, data=None, params=params)
|
362
317
|
response.raise_for_status()
|
363
|
-
|
364
318
|
event = response.json()
|
365
|
-
|
366
|
-
# Extract event details
|
367
319
|
event_id = event.get("id", "Unknown")
|
368
320
|
summary = event.get("summary", "Untitled event")
|
369
|
-
|
370
|
-
# Format dates
|
371
321
|
start = event.get("start", {})
|
372
322
|
end = event.get("end", {})
|
373
|
-
|
374
323
|
start_time = start.get("dateTime", start.get("date", "Unknown"))
|
375
324
|
end_time = end.get("dateTime", end.get("date", "Unknown"))
|
376
|
-
|
377
|
-
# Format datetimes using the helper function
|
378
325
|
start_formatted = self._format_datetime(start_time)
|
379
326
|
end_formatted = self._format_datetime(end_time)
|
380
|
-
|
381
|
-
# Get location if available
|
382
327
|
location = event.get("location", "No location specified")
|
383
|
-
|
384
|
-
# Format the confirmation message
|
385
328
|
result = "Successfully created event!\n\n"
|
386
329
|
result += f"Summary: {summary}\n"
|
387
330
|
result += f"When: {start_formatted}"
|
388
|
-
|
389
|
-
# Only add end time if it's different from start (for all-day events they might be the same)
|
390
331
|
if start_formatted != end_formatted:
|
391
332
|
result += f" to {end_formatted}"
|
392
|
-
|
393
333
|
result += f"\nWhere: {location}\n"
|
394
334
|
result += f"Event ID: {event_id}\n"
|
395
|
-
|
396
|
-
# Add a note about viewing the event
|
397
335
|
result += f"\nUse get_event('{event_id}') to see full details."
|
398
|
-
|
399
336
|
return result
|
400
337
|
|
401
338
|
def get_event_instances(
|
@@ -408,57 +345,47 @@ class GoogleCalendarApp(APIApplication):
|
|
408
345
|
show_deleted: bool = False,
|
409
346
|
page_token: str = None,
|
410
347
|
) -> str:
|
411
|
-
"""
|
412
|
-
|
413
|
-
This method retrieves all occurrences of a recurring event within a specified time range.
|
348
|
+
"""
|
349
|
+
Retrieves and formats all instances of a recurring calendar event within a specified time range, showing details like time, status, and modifications for each occurrence.
|
414
350
|
|
415
351
|
Args:
|
416
352
|
event_id: ID of the recurring event
|
417
353
|
max_results: Maximum number of event instances to return (default: 25, max: 2500)
|
418
|
-
time_min: Lower bound (inclusive) for event's end time
|
419
|
-
time_max: Upper bound (exclusive) for event's start time
|
420
|
-
time_zone: Time zone used in the response (
|
354
|
+
time_min: Lower bound (inclusive) for event's end time in ISO format
|
355
|
+
time_max: Upper bound (exclusive) for event's start time in ISO format
|
356
|
+
time_zone: Time zone used in the response (defaults to calendar's time zone)
|
421
357
|
show_deleted: Whether to include deleted instances (default: False)
|
422
358
|
page_token: Token for retrieving a specific page of results
|
423
359
|
|
424
360
|
Returns:
|
425
|
-
A formatted list of event instances
|
361
|
+
A formatted string containing a list of event instances with details including time, status, instance ID, and modification information, plus pagination token if applicable.
|
362
|
+
|
363
|
+
Raises:
|
364
|
+
HTTPError: Raised when the API request fails or returns an error status code
|
365
|
+
JSONDecodeError: Raised when the API response cannot be parsed as JSON
|
366
|
+
|
367
|
+
Tags:
|
368
|
+
list, retrieve, calendar, events, recurring, pagination, formatting, important
|
426
369
|
"""
|
427
370
|
url = f"{self.base_api_url}/events/{event_id}/instances"
|
428
|
-
|
429
|
-
# Build query parameters
|
430
371
|
params = {"maxResults": max_results, "showDeleted": str(show_deleted).lower()}
|
431
|
-
|
432
|
-
# Add optional parameters if provided
|
433
372
|
if time_min:
|
434
373
|
params["timeMin"] = time_min
|
435
|
-
|
436
374
|
if time_max:
|
437
375
|
params["timeMax"] = time_max
|
438
|
-
|
439
376
|
if time_zone:
|
440
377
|
params["timeZone"] = time_zone
|
441
|
-
|
442
378
|
if page_token:
|
443
379
|
params["pageToken"] = page_token
|
444
|
-
|
445
380
|
logger.info(f"Retrieving instances of recurring event with ID: {event_id}")
|
446
|
-
|
447
381
|
response = self._get(url, params=params)
|
448
382
|
response.raise_for_status()
|
449
|
-
|
450
383
|
data = response.json()
|
451
384
|
instances = data.get("items", [])
|
452
|
-
|
453
385
|
if not instances:
|
454
386
|
return f"No instances found for recurring event with ID: {event_id}"
|
455
|
-
|
456
|
-
# Extract event summary from the first instance
|
457
387
|
parent_summary = instances[0].get("summary", "Untitled recurring event")
|
458
|
-
|
459
388
|
result = f"Instances of recurring event: {parent_summary}\n\n"
|
460
|
-
|
461
|
-
# Process and format each instance
|
462
389
|
for i, instance in enumerate(instances, 1):
|
463
390
|
# Get instance ID and status
|
464
391
|
instance_id = instance.get("id", "No ID")
|
@@ -500,12 +427,9 @@ class GoogleCalendarApp(APIApplication):
|
|
500
427
|
# Add a separator between instances
|
501
428
|
if i < len(instances):
|
502
429
|
result += "\n"
|
503
|
-
|
504
|
-
# Add pagination info if available
|
505
430
|
if "nextPageToken" in data:
|
506
431
|
next_token = data.get("nextPageToken")
|
507
432
|
result += f"\nMore instances available. Use page_token='{next_token}' to see more."
|
508
|
-
|
509
433
|
return result
|
510
434
|
|
511
435
|
def list_tools(self):
|
@@ -1,9 +1,6 @@
|
|
1
1
|
from typing import Any
|
2
2
|
|
3
|
-
from loguru import logger
|
4
|
-
|
5
3
|
from universal_mcp.applications.application import APIApplication
|
6
|
-
from universal_mcp.exceptions import NotAuthorizedError
|
7
4
|
from universal_mcp.integrations import Integration
|
8
5
|
|
9
6
|
|
@@ -11,83 +8,81 @@ class GoogleDocsApp(APIApplication):
|
|
11
8
|
def __init__(self, integration: Integration) -> None:
|
12
9
|
super().__init__(name="google-docs", integration=integration)
|
13
10
|
self.base_api_url = "https://docs.googleapis.com/v1/documents"
|
14
|
-
|
15
|
-
def _get_headers(self):
|
16
|
-
if not self.integration:
|
17
|
-
raise ValueError("Integration not configured for GoogleDocsApp")
|
18
|
-
credentials = self.integration.get_credentials()
|
19
|
-
if not credentials:
|
20
|
-
logger.warning("No Google credentials found via integration.")
|
21
|
-
action = self.integration.authorize()
|
22
|
-
raise NotAuthorizedError(action)
|
23
|
-
if "headers" in credentials:
|
24
|
-
return credentials["headers"]
|
25
|
-
return {
|
26
|
-
"Authorization": f"Bearer {credentials['access_token']}",
|
27
|
-
"Content-Type": "application/json",
|
28
|
-
}
|
29
|
-
|
11
|
+
|
30
12
|
def create_document(self, title: str) -> dict[str, Any]:
|
31
13
|
"""
|
32
|
-
Creates a new blank Google Document with the specified title.
|
33
|
-
|
14
|
+
Creates a new blank Google Document with the specified title and returns the API response.
|
15
|
+
|
34
16
|
Args:
|
35
|
-
title: The title
|
36
|
-
|
17
|
+
title: The title for the new Google Document to be created
|
18
|
+
|
37
19
|
Returns:
|
38
|
-
|
20
|
+
A dictionary containing the Google Docs API response with document details and metadata
|
21
|
+
|
22
|
+
Raises:
|
23
|
+
HTTPError: If the API request fails due to network issues, authentication errors, or invalid parameters
|
24
|
+
RequestException: If there are connection errors or timeout issues during the API request
|
25
|
+
|
26
|
+
Tags:
|
27
|
+
create, document, api, important, google-docs, http
|
39
28
|
"""
|
40
29
|
url = self.base_api_url
|
41
30
|
document_data = {"title": title}
|
42
31
|
response = self._post(url, data=document_data)
|
43
32
|
response.raise_for_status()
|
44
33
|
return response.json()
|
45
|
-
|
34
|
+
|
46
35
|
def get_document(self, document_id: str) -> dict[str, Any]:
|
47
36
|
"""
|
48
|
-
|
49
|
-
|
37
|
+
Retrieves the latest version of a specified document from the Google Docs API.
|
38
|
+
|
50
39
|
Args:
|
51
|
-
document_id: The
|
52
|
-
|
40
|
+
document_id: The unique identifier of the document to retrieve
|
41
|
+
|
53
42
|
Returns:
|
54
|
-
|
43
|
+
A dictionary containing the document data from the Google Docs API response
|
44
|
+
|
45
|
+
Raises:
|
46
|
+
HTTPError: If the API request fails or the document is not found
|
47
|
+
JSONDecodeError: If the API response cannot be parsed as JSON
|
48
|
+
|
49
|
+
Tags:
|
50
|
+
retrieve, read, api, document, google-docs, important
|
55
51
|
"""
|
56
52
|
url = f"{self.base_api_url}/{document_id}"
|
57
53
|
response = self._get(url)
|
58
54
|
return response.json()
|
59
|
-
|
60
|
-
def add_content(
|
55
|
+
|
56
|
+
def add_content(
|
57
|
+
self, document_id: str, content: str, index: int = 1
|
58
|
+
) -> dict[str, Any]:
|
61
59
|
"""
|
62
|
-
Adds text content
|
63
|
-
|
60
|
+
Adds text content at a specified position in an existing Google Document via the Google Docs API.
|
61
|
+
|
64
62
|
Args:
|
65
|
-
document_id: The
|
66
|
-
content: The text content to
|
67
|
-
index: The position
|
68
|
-
|
63
|
+
document_id: The unique identifier of the Google Document to be updated
|
64
|
+
content: The text content to be inserted into the document
|
65
|
+
index: The zero-based position in the document where the text should be inserted (default: 1)
|
66
|
+
|
69
67
|
Returns:
|
70
|
-
|
68
|
+
A dictionary containing the Google Docs API response after performing the batch update operation
|
69
|
+
|
70
|
+
Raises:
|
71
|
+
HTTPError: When the API request fails, such as invalid document_id or insufficient permissions
|
72
|
+
RequestException: When there are network connectivity issues or API endpoint problems
|
73
|
+
|
74
|
+
Tags:
|
75
|
+
update, insert, document, api, google-docs, batch, content-management, important
|
71
76
|
"""
|
72
77
|
url = f"{self.base_api_url}/{document_id}:batchUpdate"
|
73
78
|
batch_update_data = {
|
74
79
|
"requests": [
|
75
|
-
{
|
76
|
-
"insertText": {
|
77
|
-
"location": {"index": index},
|
78
|
-
"text": content
|
79
|
-
}
|
80
|
-
}
|
80
|
+
{"insertText": {"location": {"index": index}, "text": content}}
|
81
81
|
]
|
82
82
|
}
|
83
|
-
|
84
83
|
response = self._post(url, data=batch_update_data)
|
85
84
|
response.raise_for_status()
|
86
85
|
return response.json()
|
87
|
-
|
86
|
+
|
88
87
|
def list_tools(self):
|
89
|
-
return [
|
90
|
-
self.create_document,
|
91
|
-
self.get_document,
|
92
|
-
self.add_content
|
93
|
-
]
|
88
|
+
return [self.create_document, self.get_document, self.add_content]
|