universal-mcp 0.1.13rc1__py3-none-any.whl → 0.1.13rc2__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-0.1.13rc1.dist-info → universal_mcp-0.1.13rc2.dist-info}/METADATA +1 -1
- universal_mcp-0.1.13rc2.dist-info/RECORD +38 -0
- universal_mcp/applications/ahrefs/README.md +0 -76
- universal_mcp/applications/ahrefs/__init__.py +0 -0
- universal_mcp/applications/ahrefs/app.py +0 -2291
- universal_mcp/applications/cal_com_v2/README.md +0 -175
- universal_mcp/applications/cal_com_v2/__init__.py +0 -0
- universal_mcp/applications/cal_com_v2/app.py +0 -5390
- universal_mcp/applications/calendly/README.md +0 -78
- universal_mcp/applications/calendly/__init__.py +0 -0
- universal_mcp/applications/calendly/app.py +0 -1195
- universal_mcp/applications/clickup/README.md +0 -160
- universal_mcp/applications/clickup/__init__.py +0 -0
- universal_mcp/applications/clickup/app.py +0 -5009
- universal_mcp/applications/coda/README.md +0 -133
- universal_mcp/applications/coda/__init__.py +0 -0
- universal_mcp/applications/coda/app.py +0 -3671
- universal_mcp/applications/curstdata/README.md +0 -50
- universal_mcp/applications/curstdata/__init__.py +0 -0
- universal_mcp/applications/curstdata/app.py +0 -551
- universal_mcp/applications/e2b/README.md +0 -37
- universal_mcp/applications/e2b/app.py +0 -65
- universal_mcp/applications/elevenlabs/README.md +0 -84
- universal_mcp/applications/elevenlabs/__init__.py +0 -0
- universal_mcp/applications/elevenlabs/app.py +0 -1402
- universal_mcp/applications/falai/README.md +0 -42
- universal_mcp/applications/falai/__init__.py +0 -0
- universal_mcp/applications/falai/app.py +0 -332
- universal_mcp/applications/figma/README.md +0 -74
- universal_mcp/applications/figma/__init__.py +0 -0
- universal_mcp/applications/figma/app.py +0 -1261
- universal_mcp/applications/firecrawl/README.md +0 -45
- universal_mcp/applications/firecrawl/app.py +0 -268
- universal_mcp/applications/github/README.md +0 -47
- universal_mcp/applications/github/app.py +0 -429
- universal_mcp/applications/gong/README.md +0 -88
- universal_mcp/applications/gong/__init__.py +0 -0
- universal_mcp/applications/gong/app.py +0 -2297
- universal_mcp/applications/google_calendar/app.py +0 -442
- universal_mcp/applications/google_docs/README.md +0 -40
- universal_mcp/applications/google_docs/app.py +0 -88
- universal_mcp/applications/google_drive/README.md +0 -44
- universal_mcp/applications/google_drive/app.py +0 -286
- universal_mcp/applications/google_mail/README.md +0 -47
- universal_mcp/applications/google_mail/app.py +0 -664
- universal_mcp/applications/google_sheet/README.md +0 -42
- universal_mcp/applications/google_sheet/app.py +0 -150
- universal_mcp/applications/heygen/README.md +0 -69
- universal_mcp/applications/heygen/__init__.py +0 -0
- universal_mcp/applications/heygen/app.py +0 -956
- universal_mcp/applications/mailchimp/README.md +0 -306
- universal_mcp/applications/mailchimp/__init__.py +0 -0
- universal_mcp/applications/mailchimp/app.py +0 -10937
- universal_mcp/applications/markitdown/app.py +0 -44
- universal_mcp/applications/neon/README.md +0 -99
- universal_mcp/applications/neon/__init__.py +0 -0
- universal_mcp/applications/neon/app.py +0 -1924
- universal_mcp/applications/notion/README.md +0 -55
- universal_mcp/applications/notion/__init__.py +0 -0
- universal_mcp/applications/notion/app.py +0 -527
- universal_mcp/applications/perplexity/README.md +0 -37
- universal_mcp/applications/perplexity/app.py +0 -65
- universal_mcp/applications/reddit/README.md +0 -45
- universal_mcp/applications/reddit/app.py +0 -379
- universal_mcp/applications/replicate/README.md +0 -65
- universal_mcp/applications/replicate/__init__.py +0 -0
- universal_mcp/applications/replicate/app.py +0 -980
- universal_mcp/applications/resend/README.md +0 -38
- universal_mcp/applications/resend/app.py +0 -37
- universal_mcp/applications/retell_ai/README.md +0 -46
- universal_mcp/applications/retell_ai/__init__.py +0 -0
- universal_mcp/applications/retell_ai/app.py +0 -333
- universal_mcp/applications/rocketlane/README.md +0 -42
- universal_mcp/applications/rocketlane/__init__.py +0 -0
- universal_mcp/applications/rocketlane/app.py +0 -194
- universal_mcp/applications/serpapi/README.md +0 -37
- universal_mcp/applications/serpapi/app.py +0 -73
- universal_mcp/applications/shortcut/README.md +0 -153
- universal_mcp/applications/shortcut/__init__.py +0 -0
- universal_mcp/applications/shortcut/app.py +0 -3880
- universal_mcp/applications/spotify/README.md +0 -116
- universal_mcp/applications/spotify/__init__.py +0 -0
- universal_mcp/applications/spotify/app.py +0 -2526
- universal_mcp/applications/supabase/README.md +0 -112
- universal_mcp/applications/supabase/__init__.py +0 -0
- universal_mcp/applications/supabase/app.py +0 -2970
- universal_mcp/applications/tavily/README.md +0 -38
- universal_mcp/applications/tavily/app.py +0 -51
- universal_mcp/applications/wrike/README.md +0 -71
- universal_mcp/applications/wrike/__init__.py +0 -0
- universal_mcp/applications/wrike/app.py +0 -1372
- universal_mcp/applications/youtube/README.md +0 -82
- universal_mcp/applications/youtube/__init__.py +0 -0
- universal_mcp/applications/youtube/app.py +0 -1428
- universal_mcp/applications/zenquotes/README.md +0 -37
- universal_mcp/applications/zenquotes/app.py +0 -31
- universal_mcp-0.1.13rc1.dist-info/RECORD +0 -132
- {universal_mcp-0.1.13rc1.dist-info → universal_mcp-0.1.13rc2.dist-info}/WHEEL +0 -0
- {universal_mcp-0.1.13rc1.dist-info → universal_mcp-0.1.13rc2.dist-info}/entry_points.txt +0 -0
@@ -1,442 +0,0 @@
|
|
1
|
-
from datetime import datetime, timedelta
|
2
|
-
|
3
|
-
from loguru import logger
|
4
|
-
|
5
|
-
from universal_mcp.applications.application import APIApplication
|
6
|
-
from universal_mcp.integrations import Integration
|
7
|
-
|
8
|
-
|
9
|
-
class GoogleCalendarApp(APIApplication):
|
10
|
-
def __init__(self, integration: Integration) -> None:
|
11
|
-
super().__init__(name="google-calendar", integration=integration)
|
12
|
-
self.base_api_url = "https://www.googleapis.com/calendar/v3/calendars/primary"
|
13
|
-
|
14
|
-
def _format_datetime(self, dt_string: str) -> str:
|
15
|
-
"""Format a datetime string from ISO format to a human-readable format.
|
16
|
-
|
17
|
-
Args:
|
18
|
-
dt_string: A datetime string in ISO format (e.g., "2023-06-01T10:00:00Z")
|
19
|
-
|
20
|
-
Returns:
|
21
|
-
A formatted datetime string (e.g., "2023-06-01 10:00 AM") or the original string with
|
22
|
-
"(All day)" appended if it's just a date
|
23
|
-
"""
|
24
|
-
if not dt_string or dt_string == "Unknown":
|
25
|
-
return "Unknown"
|
26
|
-
|
27
|
-
# Check if it's just a date (all-day event) or a datetime
|
28
|
-
if "T" in dt_string:
|
29
|
-
# It's a datetime - parse and format it
|
30
|
-
try:
|
31
|
-
# Handle Z (UTC) suffix by replacing with +00:00 timezone
|
32
|
-
if dt_string.endswith("Z"):
|
33
|
-
dt_string = dt_string.replace("Z", "+00:00")
|
34
|
-
|
35
|
-
# Parse the ISO datetime string
|
36
|
-
dt = datetime.fromisoformat(dt_string)
|
37
|
-
|
38
|
-
# Format to a more readable form
|
39
|
-
return dt.strftime("%Y-%m-%d %I:%M %p")
|
40
|
-
except ValueError:
|
41
|
-
# In case of parsing error, return the original
|
42
|
-
logger.warning(f"Could not parse datetime string: {dt_string}")
|
43
|
-
return dt_string
|
44
|
-
else:
|
45
|
-
# It's just a date (all-day event)
|
46
|
-
return f"{dt_string} (All day)"
|
47
|
-
|
48
|
-
def get_today_events(
|
49
|
-
self, days: int = 1, max_results: int = None, time_zone: str = None
|
50
|
-
) -> str:
|
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.
|
53
|
-
|
54
|
-
Args:
|
55
|
-
days: Number of days to retrieve events for (default: 1, which is just today)
|
56
|
-
max_results: Maximum number of events to return (optional)
|
57
|
-
time_zone: Time zone used in the response (optional, default is calendar's time zone)
|
58
|
-
|
59
|
-
Returns:
|
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
|
67
|
-
"""
|
68
|
-
today = datetime.utcnow().date()
|
69
|
-
end_date = today + timedelta(days=days)
|
70
|
-
time_min = f"{today.isoformat()}T00:00:00Z"
|
71
|
-
time_max = f"{end_date.isoformat()}T00:00:00Z"
|
72
|
-
url = f"{self.base_api_url}/events"
|
73
|
-
params = {
|
74
|
-
"timeMin": time_min,
|
75
|
-
"timeMax": time_max,
|
76
|
-
"singleEvents": "true",
|
77
|
-
"orderBy": "startTime",
|
78
|
-
}
|
79
|
-
if max_results is not None:
|
80
|
-
params["maxResults"] = max_results
|
81
|
-
if time_zone:
|
82
|
-
params["timeZone"] = time_zone
|
83
|
-
date_range = "today" if days == 1 else f"the next {days} days"
|
84
|
-
logger.info(f"Retrieving calendar events for {date_range}")
|
85
|
-
response = self._get(url, params=params)
|
86
|
-
response.raise_for_status()
|
87
|
-
events = response.json().get("items", [])
|
88
|
-
if not events:
|
89
|
-
return f"No events scheduled for {date_range}."
|
90
|
-
result = f"Events for {date_range}:\n\n"
|
91
|
-
for event in events:
|
92
|
-
# Extract event date and time
|
93
|
-
start = event.get("start", {})
|
94
|
-
event_date = (
|
95
|
-
start.get("date", start.get("dateTime", "")).split("T")[0]
|
96
|
-
if "T" in start.get("dateTime", "")
|
97
|
-
else start.get("date", "")
|
98
|
-
)
|
99
|
-
|
100
|
-
# Extract and format time
|
101
|
-
start_time = start.get("dateTime", start.get("date", "All day"))
|
102
|
-
|
103
|
-
# Format the time display
|
104
|
-
if "T" in start_time: # It's a datetime
|
105
|
-
formatted_time = self._format_datetime(start_time)
|
106
|
-
# For multi-day view, keep the date; for single day, just show time
|
107
|
-
if days > 1:
|
108
|
-
time_display = formatted_time
|
109
|
-
else:
|
110
|
-
# Extract just the time part
|
111
|
-
time_display = (
|
112
|
-
formatted_time.split(" ")[1]
|
113
|
-
+ " "
|
114
|
-
+ formatted_time.split(" ")[2]
|
115
|
-
)
|
116
|
-
else: # It's an all-day event
|
117
|
-
time_display = f"{event_date} (All day)" if days > 1 else "All day"
|
118
|
-
|
119
|
-
# Get event details
|
120
|
-
summary = event.get("summary", "Untitled event")
|
121
|
-
event_id = event.get("id", "No ID")
|
122
|
-
|
123
|
-
result += f"- {time_display}: {summary} (ID: {event_id})\n"
|
124
|
-
return result
|
125
|
-
|
126
|
-
def get_event(
|
127
|
-
self, event_id: str, max_attendees: int = None, time_zone: str = None
|
128
|
-
) -> str:
|
129
|
-
"""
|
130
|
-
Retrieves and formats detailed information about a specific Google Calendar event by its ID
|
131
|
-
|
132
|
-
Args:
|
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
|
136
|
-
|
137
|
-
Returns:
|
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
|
146
|
-
"""
|
147
|
-
url = f"{self.base_api_url}/events/{event_id}"
|
148
|
-
params = {}
|
149
|
-
if max_attendees is not None:
|
150
|
-
params["maxAttendees"] = max_attendees
|
151
|
-
if time_zone:
|
152
|
-
params["timeZone"] = time_zone
|
153
|
-
logger.info(f"Retrieving calendar event with ID: {event_id}")
|
154
|
-
response = self._get(url, params=params)
|
155
|
-
response.raise_for_status()
|
156
|
-
event = response.json()
|
157
|
-
summary = event.get("summary", "Untitled event")
|
158
|
-
description = event.get("description", "No description")
|
159
|
-
location = event.get("location", "No location specified")
|
160
|
-
start = event.get("start", {})
|
161
|
-
end = event.get("end", {})
|
162
|
-
start_time = start.get("dateTime", start.get("date", "Unknown"))
|
163
|
-
end_time = end.get("dateTime", end.get("date", "Unknown"))
|
164
|
-
start_formatted = self._format_datetime(start_time)
|
165
|
-
end_formatted = self._format_datetime(end_time)
|
166
|
-
creator = event.get("creator", {}).get("email", "Unknown")
|
167
|
-
organizer = event.get("organizer", {}).get("email", "Unknown")
|
168
|
-
recurrence = "Yes" if "recurrence" in event else "No"
|
169
|
-
attendees = event.get("attendees", [])
|
170
|
-
attendee_info = ""
|
171
|
-
if attendees:
|
172
|
-
attendee_info = "\nAttendees:\n"
|
173
|
-
for i, attendee in enumerate(attendees, 1):
|
174
|
-
email = attendee.get("email", "No email")
|
175
|
-
name = attendee.get("displayName", email)
|
176
|
-
response_status = attendee.get("responseStatus", "Unknown")
|
177
|
-
|
178
|
-
status_mapping = {
|
179
|
-
"accepted": "Accepted",
|
180
|
-
"declined": "Declined",
|
181
|
-
"tentative": "Maybe",
|
182
|
-
"needsAction": "Not responded",
|
183
|
-
}
|
184
|
-
|
185
|
-
formatted_status = status_mapping.get(response_status, response_status)
|
186
|
-
attendee_info += f" {i}. {name} ({email}) - {formatted_status}\n"
|
187
|
-
result = f"Event: {summary}\n"
|
188
|
-
result += f"ID: {event_id}\n"
|
189
|
-
result += f"When: {start_formatted} to {end_formatted}\n"
|
190
|
-
result += f"Where: {location}\n"
|
191
|
-
result += f"Description: {description}\n"
|
192
|
-
result += f"Creator: {creator}\n"
|
193
|
-
result += f"Organizer: {organizer}\n"
|
194
|
-
result += f"Recurring: {recurrence}\n"
|
195
|
-
result += attendee_info
|
196
|
-
return result
|
197
|
-
|
198
|
-
def list_events(
|
199
|
-
self,
|
200
|
-
max_results: int = 10,
|
201
|
-
time_min: str = None,
|
202
|
-
time_max: str = None,
|
203
|
-
q: str = None,
|
204
|
-
order_by: str = "startTime",
|
205
|
-
single_events: bool = True,
|
206
|
-
time_zone: str = None,
|
207
|
-
page_token: str = None,
|
208
|
-
) -> str:
|
209
|
-
"""
|
210
|
-
Retrieves and formats a list of events from Google Calendar with customizable filtering, sorting, and pagination options
|
211
|
-
|
212
|
-
Args:
|
213
|
-
max_results: Maximum number of events to return (default: 10, max: 2500)
|
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
|
221
|
-
|
222
|
-
Returns:
|
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
|
230
|
-
"""
|
231
|
-
url = f"{self.base_api_url}/events"
|
232
|
-
params = {
|
233
|
-
"maxResults": max_results,
|
234
|
-
"singleEvents": str(single_events).lower(),
|
235
|
-
"orderBy": order_by,
|
236
|
-
}
|
237
|
-
if time_min:
|
238
|
-
params["timeMin"] = time_min
|
239
|
-
else:
|
240
|
-
# Default to current time if not specified
|
241
|
-
now = datetime.utcnow().isoformat() + "Z" # 'Z' indicates UTC time
|
242
|
-
params["timeMin"] = now
|
243
|
-
if time_max:
|
244
|
-
params["timeMax"] = time_max
|
245
|
-
if q:
|
246
|
-
params["q"] = q
|
247
|
-
if time_zone:
|
248
|
-
params["timeZone"] = time_zone
|
249
|
-
if page_token:
|
250
|
-
params["pageToken"] = page_token
|
251
|
-
logger.info(f"Retrieving calendar events with params: {params}")
|
252
|
-
response = self._get(url, params=params)
|
253
|
-
response.raise_for_status()
|
254
|
-
data = response.json()
|
255
|
-
events = data.get("items", [])
|
256
|
-
if not events:
|
257
|
-
return "No events found matching your criteria."
|
258
|
-
calendar_summary = data.get("summary", "Your Calendar")
|
259
|
-
time_zone_info = data.get("timeZone", "Unknown")
|
260
|
-
result = f"Events from {calendar_summary} (Time Zone: {time_zone_info}):\n\n"
|
261
|
-
for i, event in enumerate(events, 1):
|
262
|
-
# Get basic event details
|
263
|
-
event_id = event.get("id", "No ID")
|
264
|
-
summary = event.get("summary", "Untitled event")
|
265
|
-
|
266
|
-
# Get event times and format them
|
267
|
-
start = event.get("start", {})
|
268
|
-
start_time = start.get("dateTime", start.get("date", "Unknown"))
|
269
|
-
|
270
|
-
# Format the start time using the helper function
|
271
|
-
start_formatted = self._format_datetime(start_time)
|
272
|
-
|
273
|
-
# Get location if available
|
274
|
-
location = event.get("location", "No location specified")
|
275
|
-
|
276
|
-
# Check if it's a recurring event
|
277
|
-
is_recurring = "recurrence" in event
|
278
|
-
recurring_info = " (Recurring)" if is_recurring else ""
|
279
|
-
|
280
|
-
# Format the event information
|
281
|
-
result += f"{i}. {summary}{recurring_info}\n"
|
282
|
-
result += f" ID: {event_id}\n"
|
283
|
-
result += f" When: {start_formatted}\n"
|
284
|
-
result += f" Where: {location}\n"
|
285
|
-
|
286
|
-
# Add a separator between events
|
287
|
-
if i < len(events):
|
288
|
-
result += "\n"
|
289
|
-
if "nextPageToken" in data:
|
290
|
-
next_token = data.get("nextPageToken")
|
291
|
-
result += (
|
292
|
-
f"\nMore events available. Use page_token='{next_token}' to see more."
|
293
|
-
)
|
294
|
-
return result
|
295
|
-
|
296
|
-
def quick_add_event(self, text: str, send_updates: str = "none") -> str:
|
297
|
-
"""
|
298
|
-
Creates a calendar event using natural language text input and returns a formatted confirmation message with event details.
|
299
|
-
|
300
|
-
Args:
|
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)
|
303
|
-
|
304
|
-
Returns:
|
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
|
312
|
-
"""
|
313
|
-
url = f"{self.base_api_url}/events/quickAdd"
|
314
|
-
params = {"text": text, "sendUpdates": send_updates}
|
315
|
-
logger.info(f"Creating event via quickAdd: '{text}'")
|
316
|
-
response = self._post(url, data=None, params=params)
|
317
|
-
response.raise_for_status()
|
318
|
-
event = response.json()
|
319
|
-
event_id = event.get("id", "Unknown")
|
320
|
-
summary = event.get("summary", "Untitled event")
|
321
|
-
start = event.get("start", {})
|
322
|
-
end = event.get("end", {})
|
323
|
-
start_time = start.get("dateTime", start.get("date", "Unknown"))
|
324
|
-
end_time = end.get("dateTime", end.get("date", "Unknown"))
|
325
|
-
start_formatted = self._format_datetime(start_time)
|
326
|
-
end_formatted = self._format_datetime(end_time)
|
327
|
-
location = event.get("location", "No location specified")
|
328
|
-
result = "Successfully created event!\n\n"
|
329
|
-
result += f"Summary: {summary}\n"
|
330
|
-
result += f"When: {start_formatted}"
|
331
|
-
if start_formatted != end_formatted:
|
332
|
-
result += f" to {end_formatted}"
|
333
|
-
result += f"\nWhere: {location}\n"
|
334
|
-
result += f"Event ID: {event_id}\n"
|
335
|
-
result += f"\nUse get_event('{event_id}') to see full details."
|
336
|
-
return result
|
337
|
-
|
338
|
-
def get_event_instances(
|
339
|
-
self,
|
340
|
-
event_id: str,
|
341
|
-
max_results: int = 25,
|
342
|
-
time_min: str = None,
|
343
|
-
time_max: str = None,
|
344
|
-
time_zone: str = None,
|
345
|
-
show_deleted: bool = False,
|
346
|
-
page_token: str = None,
|
347
|
-
) -> str:
|
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.
|
350
|
-
|
351
|
-
Args:
|
352
|
-
event_id: ID of the recurring event
|
353
|
-
max_results: Maximum number of event instances to return (default: 25, max: 2500)
|
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)
|
357
|
-
show_deleted: Whether to include deleted instances (default: False)
|
358
|
-
page_token: Token for retrieving a specific page of results
|
359
|
-
|
360
|
-
Returns:
|
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
|
369
|
-
"""
|
370
|
-
url = f"{self.base_api_url}/events/{event_id}/instances"
|
371
|
-
params = {"maxResults": max_results, "showDeleted": str(show_deleted).lower()}
|
372
|
-
if time_min:
|
373
|
-
params["timeMin"] = time_min
|
374
|
-
if time_max:
|
375
|
-
params["timeMax"] = time_max
|
376
|
-
if time_zone:
|
377
|
-
params["timeZone"] = time_zone
|
378
|
-
if page_token:
|
379
|
-
params["pageToken"] = page_token
|
380
|
-
logger.info(f"Retrieving instances of recurring event with ID: {event_id}")
|
381
|
-
response = self._get(url, params=params)
|
382
|
-
response.raise_for_status()
|
383
|
-
data = response.json()
|
384
|
-
instances = data.get("items", [])
|
385
|
-
if not instances:
|
386
|
-
return f"No instances found for recurring event with ID: {event_id}"
|
387
|
-
parent_summary = instances[0].get("summary", "Untitled recurring event")
|
388
|
-
result = f"Instances of recurring event: {parent_summary}\n\n"
|
389
|
-
for i, instance in enumerate(instances, 1):
|
390
|
-
# Get instance ID and status
|
391
|
-
instance_id = instance.get("id", "No ID")
|
392
|
-
status = instance.get("status", "confirmed")
|
393
|
-
|
394
|
-
# Format status for display
|
395
|
-
status_display = ""
|
396
|
-
if status == "cancelled":
|
397
|
-
status_display = " [CANCELLED]"
|
398
|
-
elif status == "tentative":
|
399
|
-
status_display = " [TENTATIVE]"
|
400
|
-
|
401
|
-
# Get instance time
|
402
|
-
start = instance.get("start", {})
|
403
|
-
original_start_time = instance.get("originalStartTime", {})
|
404
|
-
|
405
|
-
# Determine if this is a modified instance
|
406
|
-
is_modified = original_start_time and "dateTime" in original_start_time
|
407
|
-
modified_indicator = " [MODIFIED]" if is_modified else ""
|
408
|
-
|
409
|
-
# Get the time information
|
410
|
-
start_time = start.get("dateTime", start.get("date", "Unknown"))
|
411
|
-
|
412
|
-
# Format the time using the helper function
|
413
|
-
formatted_time = self._format_datetime(start_time)
|
414
|
-
|
415
|
-
# Format the instance information
|
416
|
-
result += f"{i}. {formatted_time}{status_display}{modified_indicator}\n"
|
417
|
-
result += f" Instance ID: {instance_id}\n"
|
418
|
-
|
419
|
-
# Show original start time if modified
|
420
|
-
if is_modified:
|
421
|
-
orig_time = original_start_time.get(
|
422
|
-
"dateTime", original_start_time.get("date", "Unknown")
|
423
|
-
)
|
424
|
-
orig_formatted = self._format_datetime(orig_time)
|
425
|
-
result += f" Original time: {orig_formatted}\n"
|
426
|
-
|
427
|
-
# Add a separator between instances
|
428
|
-
if i < len(instances):
|
429
|
-
result += "\n"
|
430
|
-
if "nextPageToken" in data:
|
431
|
-
next_token = data.get("nextPageToken")
|
432
|
-
result += f"\nMore instances available. Use page_token='{next_token}' to see more."
|
433
|
-
return result
|
434
|
-
|
435
|
-
def list_tools(self):
|
436
|
-
return [
|
437
|
-
self.get_event,
|
438
|
-
self.get_today_events,
|
439
|
-
self.list_events,
|
440
|
-
self.quick_add_event,
|
441
|
-
self.get_event_instances,
|
442
|
-
]
|
@@ -1,40 +0,0 @@
|
|
1
|
-
|
2
|
-
# Google Docs MCP Server
|
3
|
-
|
4
|
-
An MCP Server for the Google Docs API.
|
5
|
-
|
6
|
-
## Supported Integrations
|
7
|
-
|
8
|
-
- AgentR
|
9
|
-
- API Key (Coming Soon)
|
10
|
-
- OAuth (Coming Soon)
|
11
|
-
|
12
|
-
## Tools
|
13
|
-
|
14
|
-
This is automatically generated from OpenAPI schema for the Google Docs API.
|
15
|
-
|
16
|
-
## Supported Integrations
|
17
|
-
|
18
|
-
This tool can be integrated with any service that supports HTTP requests.
|
19
|
-
|
20
|
-
## Tool List
|
21
|
-
|
22
|
-
| Tool | Description |
|
23
|
-
|------|-------------|
|
24
|
-
| add_content | Adds text content to an existing Google Document. |
|
25
|
-
| create_document | Creates a new blank Google Document with the specified title. |
|
26
|
-
| get_document | Gets the latest version of the specified document. |
|
27
|
-
| validate | Function for validate |
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
## Usage
|
32
|
-
|
33
|
-
- Login to AgentR
|
34
|
-
- Follow the quickstart guide to setup MCP Server for your client
|
35
|
-
- Visit Apps Store and enable the Google Docs app
|
36
|
-
- Restart the MCP Server
|
37
|
-
|
38
|
-
### Local Development
|
39
|
-
|
40
|
-
- Follow the README to test with the local MCP Server
|
@@ -1,88 +0,0 @@
|
|
1
|
-
from typing import Any
|
2
|
-
|
3
|
-
from universal_mcp.applications.application import APIApplication
|
4
|
-
from universal_mcp.integrations import Integration
|
5
|
-
|
6
|
-
|
7
|
-
class GoogleDocsApp(APIApplication):
|
8
|
-
def __init__(self, integration: Integration) -> None:
|
9
|
-
super().__init__(name="google-docs", integration=integration)
|
10
|
-
self.base_api_url = "https://docs.googleapis.com/v1/documents"
|
11
|
-
|
12
|
-
def create_document(self, title: str) -> dict[str, Any]:
|
13
|
-
"""
|
14
|
-
Creates a new blank Google Document with the specified title and returns the API response.
|
15
|
-
|
16
|
-
Args:
|
17
|
-
title: The title for the new Google Document to be created
|
18
|
-
|
19
|
-
Returns:
|
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
|
28
|
-
"""
|
29
|
-
url = self.base_api_url
|
30
|
-
document_data = {"title": title}
|
31
|
-
response = self._post(url, data=document_data)
|
32
|
-
response.raise_for_status()
|
33
|
-
return response.json()
|
34
|
-
|
35
|
-
def get_document(self, document_id: str) -> dict[str, Any]:
|
36
|
-
"""
|
37
|
-
Retrieves the latest version of a specified document from the Google Docs API.
|
38
|
-
|
39
|
-
Args:
|
40
|
-
document_id: The unique identifier of the document to retrieve
|
41
|
-
|
42
|
-
Returns:
|
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
|
51
|
-
"""
|
52
|
-
url = f"{self.base_api_url}/{document_id}"
|
53
|
-
response = self._get(url)
|
54
|
-
return response.json()
|
55
|
-
|
56
|
-
def add_content(
|
57
|
-
self, document_id: str, content: str, index: int = 1
|
58
|
-
) -> dict[str, Any]:
|
59
|
-
"""
|
60
|
-
Adds text content at a specified position in an existing Google Document via the Google Docs API.
|
61
|
-
|
62
|
-
Args:
|
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
|
-
|
67
|
-
Returns:
|
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
|
76
|
-
"""
|
77
|
-
url = f"{self.base_api_url}/{document_id}:batchUpdate"
|
78
|
-
batch_update_data = {
|
79
|
-
"requests": [
|
80
|
-
{"insertText": {"location": {"index": index}, "text": content}}
|
81
|
-
]
|
82
|
-
}
|
83
|
-
response = self._post(url, data=batch_update_data)
|
84
|
-
response.raise_for_status()
|
85
|
-
return response.json()
|
86
|
-
|
87
|
-
def list_tools(self):
|
88
|
-
return [self.create_document, self.get_document, self.add_content]
|
@@ -1,44 +0,0 @@
|
|
1
|
-
|
2
|
-
# Google Drive MCP Server
|
3
|
-
|
4
|
-
An MCP Server for the Google Drive API.
|
5
|
-
|
6
|
-
## Supported Integrations
|
7
|
-
|
8
|
-
- AgentR
|
9
|
-
- API Key (Coming Soon)
|
10
|
-
- OAuth (Coming Soon)
|
11
|
-
|
12
|
-
## Tools
|
13
|
-
|
14
|
-
This is automatically generated from OpenAPI schema for the Google Drive API.
|
15
|
-
|
16
|
-
## Supported Integrations
|
17
|
-
|
18
|
-
This tool can be integrated with any service that supports HTTP requests.
|
19
|
-
|
20
|
-
## Tool List
|
21
|
-
|
22
|
-
| Tool | Description |
|
23
|
-
|------|-------------|
|
24
|
-
| get_drive_info | Get information about the user's Google Drive, including storage quota and user info. |
|
25
|
-
| list_files | List files in the user's Google Drive. |
|
26
|
-
| create_file_from_text | Create a new file from text content in Google Drive. |
|
27
|
-
| upload_a_file | Upload a file to Google Drive from a local binary file. |
|
28
|
-
| find_folder_id_by_name | Find a folder's ID by its name. |
|
29
|
-
| create_folder | Create a new folder in Google Drive. |
|
30
|
-
| get_file | Get metadata for a specific file. |
|
31
|
-
| delete_file | Delete a file from Google Drive. |
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
## Usage
|
36
|
-
|
37
|
-
- Login to AgentR
|
38
|
-
- Follow the quickstart guide to setup MCP Server for your client
|
39
|
-
- Visit Apps Store and enable the Google Drive app
|
40
|
-
- Restart the MCP Server
|
41
|
-
|
42
|
-
### Local Development
|
43
|
-
|
44
|
-
- Follow the README to test with the local MCP Server
|