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