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.
Files changed (61) hide show
  1. universal_mcp/__init__.py +0 -2
  2. universal_mcp/analytics.py +75 -0
  3. universal_mcp/applications/ahrefs/README.md +76 -0
  4. universal_mcp/applications/ahrefs/app.py +2291 -0
  5. universal_mcp/applications/application.py +95 -5
  6. universal_mcp/applications/calendly/README.md +78 -0
  7. universal_mcp/applications/calendly/__init__.py +0 -0
  8. universal_mcp/applications/calendly/app.py +1195 -0
  9. universal_mcp/applications/coda/README.md +133 -0
  10. universal_mcp/applications/coda/__init__.py +0 -0
  11. universal_mcp/applications/coda/app.py +3671 -0
  12. universal_mcp/applications/e2b/app.py +14 -35
  13. universal_mcp/applications/figma/README.md +74 -0
  14. universal_mcp/applications/figma/__init__.py +0 -0
  15. universal_mcp/applications/figma/app.py +1261 -0
  16. universal_mcp/applications/firecrawl/app.py +29 -32
  17. universal_mcp/applications/github/app.py +127 -85
  18. universal_mcp/applications/google_calendar/app.py +62 -138
  19. universal_mcp/applications/google_docs/app.py +47 -52
  20. universal_mcp/applications/google_drive/app.py +119 -113
  21. universal_mcp/applications/google_mail/app.py +124 -50
  22. universal_mcp/applications/google_sheet/app.py +89 -91
  23. universal_mcp/applications/markitdown/app.py +9 -8
  24. universal_mcp/applications/notion/app.py +254 -134
  25. universal_mcp/applications/perplexity/app.py +13 -45
  26. universal_mcp/applications/reddit/app.py +94 -85
  27. universal_mcp/applications/resend/app.py +12 -23
  28. universal_mcp/applications/{serp → serpapi}/app.py +14 -33
  29. universal_mcp/applications/tavily/app.py +11 -28
  30. universal_mcp/applications/wrike/README.md +71 -0
  31. universal_mcp/applications/wrike/__init__.py +0 -0
  32. universal_mcp/applications/wrike/app.py +1372 -0
  33. universal_mcp/applications/youtube/README.md +82 -0
  34. universal_mcp/applications/youtube/__init__.py +0 -0
  35. universal_mcp/applications/youtube/app.py +1428 -0
  36. universal_mcp/applications/zenquotes/app.py +12 -2
  37. universal_mcp/exceptions.py +9 -2
  38. universal_mcp/integrations/__init__.py +24 -1
  39. universal_mcp/integrations/agentr.py +27 -4
  40. universal_mcp/integrations/integration.py +143 -30
  41. universal_mcp/logger.py +3 -56
  42. universal_mcp/servers/__init__.py +6 -14
  43. universal_mcp/servers/server.py +201 -146
  44. universal_mcp/stores/__init__.py +7 -2
  45. universal_mcp/stores/store.py +103 -40
  46. universal_mcp/tools/__init__.py +3 -0
  47. universal_mcp/tools/adapters.py +43 -0
  48. universal_mcp/tools/func_metadata.py +213 -0
  49. universal_mcp/tools/tools.py +342 -0
  50. universal_mcp/utils/docgen.py +325 -119
  51. universal_mcp/utils/docstring_parser.py +179 -0
  52. universal_mcp/utils/dump_app_tools.py +33 -23
  53. universal_mcp/utils/installation.py +199 -8
  54. universal_mcp/utils/openapi.py +229 -46
  55. {universal_mcp-0.1.7rc2.dist-info → universal_mcp-0.1.8.dist-info}/METADATA +9 -5
  56. universal_mcp-0.1.8.dist-info/RECORD +81 -0
  57. universal_mcp-0.1.7rc2.dist-info/RECORD +0 -58
  58. /universal_mcp/{utils/bridge.py → applications/ahrefs/__init__.py} +0 -0
  59. /universal_mcp/applications/{serp → serpapi}/README.md +0 -0
  60. {universal_mcp-0.1.7rc2.dist-info → universal_mcp-0.1.8.dist-info}/WHEEL +0 -0
  61. {universal_mcp-0.1.7rc2.dist-info → universal_mcp-0.1.8.dist-info}/entry_points.txt +0 -0
@@ -4,7 +4,6 @@ import httpx
4
4
  from loguru import logger
5
5
 
6
6
  from universal_mcp.applications.application import APIApplication
7
- from universal_mcp.exceptions import NotAuthorizedError
8
7
  from universal_mcp.integrations import Integration
9
8
 
10
9
 
@@ -18,73 +17,75 @@ class GoogleDriveApp(APIApplication):
18
17
  super().__init__(name="google-drive", integration=integration)
19
18
  self.base_url = "https://www.googleapis.com/drive/v3"
20
19
 
21
- def _get_headers(self):
22
- if not self.integration:
23
- raise ValueError("Integration not configured for GoogleDriveApp")
24
- credentials = self.integration.get_credentials()
25
- if not credentials:
26
- logger.warning("No Google Drive credentials found via integration.")
27
- action = self.integration.authorize()
28
- raise NotAuthorizedError(action)
29
-
30
- if "headers" in credentials:
31
- return credentials["headers"]
32
- return {
33
- "Authorization": f"Bearer {credentials['access_token']}",
34
- "Content-Type": "application/json",
35
- }
36
-
37
20
  def get_drive_info(self) -> dict[str, Any]:
38
21
  """
39
- Get information about the user's Google Drive, including storage quota and user info.
40
-
22
+ Retrieves detailed information about the user's Google Drive storage and account.
23
+
41
24
  Returns:
42
- A dictionary containing information about the user's Drive.
43
- Includes details like storage quota, user information, and features.
25
+ A dictionary containing Drive information including storage quota (usage, limit) and user details (name, email, etc.).
26
+
27
+ Raises:
28
+ HTTPError: If the API request fails or returns an error status code
29
+ ConnectionError: If there are network connectivity issues
30
+ AuthenticationError: If the authentication credentials are invalid or expired
31
+
32
+ Tags:
33
+ get, info, storage, drive, quota, user, api, important
44
34
  """
45
35
  url = f"{self.base_url}/about"
46
36
  params = {"fields": "storageQuota,user"}
47
-
48
37
  response = self._get(url, params=params)
49
38
  return response.json()
50
39
 
51
- def list_files(self, page_size: int = 10, query: str = None, order_by: str = None) -> dict[str, Any]:
40
+ def list_files(
41
+ self, page_size: int = 10, query: str = None, order_by: str = None
42
+ ) -> dict[str, Any]:
52
43
  """
53
- List files in the user's Google Drive.
54
-
44
+ Lists and retrieves files from Google Drive with optional filtering, pagination, and sorting.
45
+
55
46
  Args:
56
- page_size: Maximum number of files to return (default: 10)
57
- query: Search query using Google Drive query syntax (e.g., "mimeType='image/jpeg'")
58
- order_by: Field to sort by (e.g., "modifiedTime desc")
59
-
47
+ page_size: Maximum number of files to return per page (default: 10)
48
+ query: Optional search query string using Google Drive query syntax (e.g., "mimeType='image/jpeg'")
49
+ order_by: Optional field name to sort results by, with optional direction (e.g., "modifiedTime desc")
50
+
60
51
  Returns:
61
- A dictionary containing the list of files and possibly a nextPageToken.
52
+ Dictionary containing a list of files and metadata, including 'files' array and optional 'nextPageToken' for pagination
53
+
54
+ Raises:
55
+ HTTPError: Raised when the API request fails or returns an error status code
56
+ RequestException: Raised when network connectivity issues occur during the API request
57
+
58
+ Tags:
59
+ list, files, search, google-drive, pagination, important
62
60
  """
63
61
  url = f"{self.base_url}/files"
64
62
  params = {
65
63
  "pageSize": page_size,
66
64
  }
67
-
68
65
  if query:
69
66
  params["q"] = query
70
-
71
67
  if order_by:
72
68
  params["orderBy"] = order_by
73
-
74
69
  response = self._get(url, params=params)
75
70
  response.raise_for_status()
76
71
  return response.json()
77
-
78
72
 
79
73
  def get_file(self, file_id: str) -> dict[str, Any]:
80
74
  """
81
- Get metadata for a specific file.
82
-
75
+ Retrieves detailed metadata for a specific file using its ID.
76
+
83
77
  Args:
84
- file_id: The ID of the file to retrieve
85
-
78
+ file_id: String identifier of the file whose metadata should be retrieved
79
+
86
80
  Returns:
87
- A dictionary containing the file's metadata.
81
+ Dictionary containing the file's metadata including properties such as name, size, type, and other attributes
82
+
83
+ Raises:
84
+ HTTPError: When the API request fails due to invalid file_id or network issues
85
+ JSONDecodeError: When the API response cannot be parsed as JSON
86
+
87
+ Tags:
88
+ retrieve, file, metadata, get, api, important
88
89
  """
89
90
  url = f"{self.base_url}/files/{file_id}"
90
91
  response = self._get(url)
@@ -92,13 +93,19 @@ class GoogleDriveApp(APIApplication):
92
93
 
93
94
  def delete_file(self, file_id: str) -> dict[str, Any]:
94
95
  """
95
- Delete a file from Google Drive.
96
-
96
+ Deletes a specified file from Google Drive and returns a status message.
97
+
97
98
  Args:
98
- file_id: The ID of the file to delete
99
-
99
+ file_id: The unique identifier string of the file to be deleted from Google Drive
100
+
100
101
  Returns:
101
- A simple success message dictionary
102
+ A dictionary containing either a success message {'message': 'File deleted successfully'} or an error message {'error': 'error description'}
103
+
104
+ Raises:
105
+ Exception: When the DELETE request fails due to network issues, invalid file_id, insufficient permissions, or other API errors
106
+
107
+ Tags:
108
+ delete, file-management, google-drive, api, important
102
109
  """
103
110
  url = f"{self.base_url}/files/{file_id}"
104
111
  try:
@@ -106,108 +113,114 @@ class GoogleDriveApp(APIApplication):
106
113
  return {"message": "File deleted successfully"}
107
114
  except Exception as e:
108
115
  return {"error": str(e)}
109
-
116
+
110
117
  def create_file_from_text(
111
118
  self,
112
119
  file_name: str,
113
120
  text_content: str,
114
121
  parent_id: str = None,
115
- mime_type: str = "text/plain"
122
+ mime_type: str = "text/plain",
116
123
  ) -> dict[str, Any]:
117
124
  """
118
- Create a new file from text content in Google Drive.
119
-
125
+ Creates a new file in Google Drive with specified text content and returns the file's metadata.
126
+
120
127
  Args:
121
128
  file_name: Name of the file to create on Google Drive
122
129
  text_content: Plain text content to be written to the file
123
- parent_id: ID of the parent folder to create the file in (optional)
124
- mime_type: MIME type of the file (default: text/plain)
125
-
130
+ parent_id: Optional ID of the parent folder where the file will be created
131
+ mime_type: MIME type of the file (defaults to 'text/plain')
132
+
126
133
  Returns:
127
- A dictionary containing the created file's metadata.
134
+ Dictionary containing metadata of the created file including ID, name, and other Google Drive file properties
135
+
136
+ Raises:
137
+ HTTPStatusError: Raised when the API request fails during file creation or content upload
138
+ UnicodeEncodeError: Raised when the text_content cannot be encoded in UTF-8
139
+
140
+ Tags:
141
+ create, file, upload, drive, text, important, storage, document
128
142
  """
129
- metadata = {
130
- "name": file_name,
131
- "mimeType": mime_type
132
- }
133
-
143
+ metadata = {"name": file_name, "mimeType": mime_type}
134
144
  if parent_id:
135
145
  metadata["parents"] = [parent_id]
136
-
137
146
  create_url = f"{self.base_url}/files"
138
147
  create_response = self._post(create_url, data=metadata)
139
148
  file_data = create_response.json()
140
149
  file_id = file_data.get("id")
141
-
142
- # Step 2: Update the file with text content
143
150
  upload_url = f"https://www.googleapis.com/upload/drive/v3/files/{file_id}?uploadType=media"
144
151
  upload_headers = self._get_headers()
145
152
  upload_headers["Content-Type"] = f"{mime_type}; charset=utf-8"
146
-
147
153
  upload_response = httpx.patch(
148
- upload_url,
149
- headers=upload_headers,
150
- content=text_content.encode("utf-8")
154
+ upload_url, headers=upload_headers, content=text_content.encode("utf-8")
151
155
  )
152
156
  upload_response.raise_for_status()
153
-
154
157
  response_data = upload_response.json()
155
158
  return response_data
156
-
159
+
157
160
  def find_folder_id_by_name(self, folder_name: str) -> str | None:
158
161
  """
159
- Find a folder's ID by its name.
160
-
162
+ Searches for and retrieves a Google Drive folder's ID using its name.
163
+
161
164
  Args:
162
- folder_name: The name of the folder to find
163
-
165
+ folder_name: The name of the folder to search for in Google Drive
166
+
164
167
  Returns:
165
- The folder ID if found, None otherwise
168
+ str | None: The folder's ID if a matching folder is found, None if no folder is found or if an error occurs
169
+
170
+ Raises:
171
+ Exception: Caught internally and logged when API requests fail or response parsing errors occur
172
+
173
+ Tags:
174
+ search, find, google-drive, folder, query, api, utility
166
175
  """
167
176
  query = f"mimeType='application/vnd.google-apps.folder' and name='{folder_name}' and trashed=false"
168
177
  try:
169
178
  response = self._get(
170
179
  f"{self.base_url}/files",
171
- params={"q": query, "fields": "files(id,name)"}
180
+ params={"q": query, "fields": "files(id,name)"},
172
181
  )
173
182
  files = response.json().get("files", [])
174
183
  return files[0]["id"] if files else None
175
184
  except Exception as e:
176
185
  logger.error(f"Error finding folder ID by name: {e}")
177
186
  return None
178
-
187
+
179
188
  def create_folder(self, folder_name: str, parent_id: str = None) -> dict[str, Any]:
180
189
  """
181
- Create a new folder in Google Drive.
182
-
190
+ Creates a new folder in Google Drive with optional parent folder specification
191
+
183
192
  Args:
184
193
  folder_name: Name of the folder to create
185
- parent_id: ID of the parent folder (optional). Can be an actual folder ID
186
- or a folder name that will be looked up.
187
-
194
+ parent_id: ID or name of the parent folder. Can be either a folder ID string or a folder name that will be automatically looked up
195
+
188
196
  Returns:
189
- A dictionary containing the created folder's metadata.
197
+ Dictionary containing the created folder's metadata including its ID, name, and other Drive-specific information
198
+
199
+ Raises:
200
+ ValueError: Raised when a parent folder name is provided but cannot be found in Google Drive
201
+
202
+ Tags:
203
+ create, folder, drive, storage, important, management
190
204
  """
191
205
  import re
192
-
206
+
193
207
  metadata = {
194
208
  "name": folder_name,
195
- "mimeType": "application/vnd.google-apps.folder"
209
+ "mimeType": "application/vnd.google-apps.folder",
196
210
  }
197
-
198
211
  if parent_id:
199
212
  if not re.match(r"^[a-zA-Z0-9_-]{28,33}$", parent_id):
200
213
  found_id = self.find_folder_id_by_name(parent_id)
201
214
  if found_id:
202
215
  metadata["parents"] = [found_id]
203
216
  else:
204
- raise ValueError(f"Could not find parent folder with name: {parent_id}")
217
+ raise ValueError(
218
+ f"Could not find parent folder with name: {parent_id}"
219
+ )
205
220
  else:
206
221
  metadata["parents"] = [parent_id]
207
-
208
222
  url = f"{self.base_url}/files"
209
223
  params = {"supportsAllDrives": "true"}
210
-
211
224
  response = self._post(url, data=metadata, params=params)
212
225
  return response.json()
213
226
 
@@ -216,53 +229,46 @@ class GoogleDriveApp(APIApplication):
216
229
  file_name: str,
217
230
  file_path: str,
218
231
  parent_id: str = None,
219
- mime_type: str = None
232
+ mime_type: str = None,
220
233
  ) -> dict[str, Any]:
221
234
  """
222
- Upload a file to Google Drive from a local binary file.
223
-
224
- This method is suitable for files under 5MB in size. It automatically
225
- reads the file from the provided path.
226
-
235
+ Uploads a file to Google Drive by creating a file metadata entry and uploading the binary content.
236
+
227
237
  Args:
228
238
  file_name: Name to give the file on Google Drive
229
239
  file_path: Path to the local file to upload
230
- parent_id: ID of the parent folder to create the file in (optional)
231
- mime_type: MIME type of the file
232
- Examples: 'image/jpeg', 'image/png', 'application/pdf', 'text/csv'
233
-
240
+ parent_id: Optional ID of the parent folder to create the file in
241
+ mime_type: MIME type of the file (e.g., 'image/jpeg', 'image/png', 'application/pdf')
242
+
234
243
  Returns:
235
- A dictionary containing the created file's metadata.
244
+ Dictionary containing the uploaded file's metadata from Google Drive
245
+
246
+ Raises:
247
+ FileNotFoundError: When the specified file_path does not exist or is not accessible
248
+ HTTPError: When the API request fails or returns an error status code
249
+ IOError: When there are issues reading the file content
250
+
251
+ Tags:
252
+ upload, file-handling, google-drive, api, important, binary, storage
236
253
  """
237
-
238
-
239
- metadata = {
240
- "name": file_name,
241
- "mimeType": mime_type
242
- }
243
-
254
+ metadata = {"name": file_name, "mimeType": mime_type}
244
255
  if parent_id:
245
256
  metadata["parents"] = [parent_id]
246
-
247
257
  create_url = f"{self.base_url}/files"
248
258
  create_response = self._post(create_url, data=metadata)
249
259
  file_data = create_response.json()
250
260
  file_id = file_data.get("id")
251
-
252
- with open(file_path, 'rb') as file_content:
261
+ with open(file_path, "rb") as file_content:
253
262
  binary_content = file_content.read()
254
-
263
+
255
264
  upload_url = f"https://www.googleapis.com/upload/drive/v3/files/{file_id}?uploadType=media"
256
265
  upload_headers = self._get_headers()
257
266
  upload_headers["Content-Type"] = mime_type
258
-
267
+
259
268
  upload_response = httpx.patch(
260
- upload_url,
261
- headers=upload_headers,
262
- content=binary_content
269
+ upload_url, headers=upload_headers, content=binary_content
263
270
  )
264
271
  upload_response.raise_for_status()
265
-
266
272
  response_data = upload_response.json()
267
273
  return response_data
268
274
 
@@ -277,4 +283,4 @@ class GoogleDriveApp(APIApplication):
277
283
  self.create_folder,
278
284
  self.get_file,
279
285
  self.delete_file,
280
- ]
286
+ ]
@@ -13,32 +13,25 @@ class GoogleMailApp(APIApplication):
13
13
  super().__init__(name="google-mail", integration=integration)
14
14
  self.base_api_url = "https://gmail.googleapis.com/gmail/v1/users/me"
15
15
 
16
- def _get_headers(self):
17
- if not self.integration:
18
- raise ValueError("Integration not configured for GmailApp")
19
- credentials = self.integration.get_credentials()
20
- if not credentials:
21
- logger.warning("No Gmail credentials found via integration.")
22
- action = self.integration.authorize()
23
- raise NotAuthorizedError(action)
24
-
25
- if "headers" in credentials:
26
- return credentials["headers"]
27
- return {
28
- "Authorization": f"Bearer {credentials['access_token']}",
29
- "Content-Type": "application/json",
30
- }
31
-
32
16
  def send_email(self, to: str, subject: str, body: str) -> str:
33
- """Send an email
17
+ """
18
+ Sends an email using the Gmail API and returns a confirmation or error message.
34
19
 
35
20
  Args:
36
21
  to: The email address of the recipient
37
- subject: The subject of the email
38
- body: The body of the email
22
+ subject: The subject line of the email
23
+ body: The main content of the email message
39
24
 
40
25
  Returns:
41
- A confirmation message
26
+ A string containing either a success confirmation message or an error description
27
+
28
+ Raises:
29
+ NotAuthorizedError: When Gmail API authentication is not valid or has expired
30
+ KeyError: When required configuration keys are missing
31
+ Exception: For any other unexpected errors during the email sending process
32
+
33
+ Tags:
34
+ send, email, api, communication, important
42
35
  """
43
36
  try:
44
37
  url = f"{self.base_api_url}/messages/send"
@@ -89,15 +82,24 @@ class GoogleMailApp(APIApplication):
89
82
  raise
90
83
 
91
84
  def create_draft(self, to: str, subject: str, body: str) -> str:
92
- """Create a draft email
85
+ """
86
+ Creates a draft email message in Gmail using the Gmail API and returns a confirmation status.
93
87
 
94
88
  Args:
95
89
  to: The email address of the recipient
96
- subject: The subject of the email
97
- body: The body of the email
90
+ subject: The subject line of the draft email
91
+ body: The main content/message of the draft email
98
92
 
99
93
  Returns:
100
- A confirmation message with the draft ID
94
+ A string containing either a success message with the draft ID or an error message describing the failure
95
+
96
+ Raises:
97
+ NotAuthorizedError: When the user's Gmail API authorization is invalid or expired
98
+ KeyError: When required configuration keys are missing
99
+ Exception: For general API errors, network issues, or other unexpected problems
100
+
101
+ Tags:
102
+ create, email, draft, gmail, api, important
101
103
  """
102
104
  try:
103
105
  url = f"{self.base_api_url}/drafts"
@@ -129,13 +131,22 @@ class GoogleMailApp(APIApplication):
129
131
  return f"Error creating draft: {type(e).__name__} - {str(e)}"
130
132
 
131
133
  def send_draft(self, draft_id: str) -> str:
132
- """Send an existing draft email
134
+ """
135
+ Sends an existing draft email using the Gmail API and returns a confirmation message.
133
136
 
134
137
  Args:
135
- draft_id: The ID of the draft to send
138
+ draft_id: The unique identifier of the Gmail draft to be sent
136
139
 
137
140
  Returns:
138
- A confirmation message
141
+ A string containing either a success message with the sent message ID or an error message detailing the failure reason
142
+
143
+ Raises:
144
+ NotAuthorizedError: When the user's Gmail API authorization is invalid or expired
145
+ KeyError: When required configuration keys are missing from the API response
146
+ Exception: For other unexpected errors during the API request or response handling
147
+
148
+ Tags:
149
+ send, email, api, communication, important, draft
139
150
  """
140
151
  try:
141
152
  url = f"{self.base_api_url}/drafts/send"
@@ -165,14 +176,23 @@ class GoogleMailApp(APIApplication):
165
176
  return f"Error sending draft: {type(e).__name__} - {str(e)}"
166
177
 
167
178
  def get_draft(self, draft_id: str, format: str = "full") -> str:
168
- """Get a specific draft email by ID
179
+ """
180
+ Retrieves and formats a specific draft email from Gmail by its ID
169
181
 
170
182
  Args:
171
- draft_id: The ID of the draft to retrieve
172
- format: The format to return the draft in (minimal, full, raw, metadata)
183
+ draft_id: String identifier of the draft email to retrieve
184
+ format: Output format of the draft (options: minimal, full, raw, metadata). Defaults to 'full'
173
185
 
174
186
  Returns:
175
- The draft information or an error message
187
+ A formatted string containing the draft email details (ID, recipient, subject) or an error message if retrieval fails
188
+
189
+ Raises:
190
+ NotAuthorizedError: When the user's Gmail authorization is invalid or expired
191
+ KeyError: When required configuration keys or response data fields are missing
192
+ Exception: For any other unexpected errors during draft retrieval
193
+
194
+ Tags:
195
+ retrieve, email, gmail, draft, api, format, important
176
196
  """
177
197
  try:
178
198
  url = f"{self.base_api_url}/drafts/{draft_id}"
@@ -223,15 +243,24 @@ class GoogleMailApp(APIApplication):
223
243
  def list_drafts(
224
244
  self, max_results: int = 20, q: str = None, include_spam_trash: bool = False
225
245
  ) -> str:
226
- """List drafts in the user's mailbox
246
+ """
247
+ Retrieves and formats a list of email drafts from the user's Gmail mailbox with optional filtering and pagination.
227
248
 
228
249
  Args:
229
250
  max_results: Maximum number of drafts to return (max 500, default 20)
230
- q: Search query to filter drafts (same format as Gmail search)
231
- include_spam_trash: Include drafts from spam and trash folders
251
+ q: Search query string to filter drafts using Gmail search syntax (default None)
252
+ include_spam_trash: Boolean flag to include drafts from spam and trash folders (default False)
232
253
 
233
254
  Returns:
234
- A formatted list of drafts or an error message
255
+ A formatted string containing the list of draft IDs and count information, or an error message if the request fails
256
+
257
+ Raises:
258
+ NotAuthorizedError: When the Gmail API authentication is missing or invalid
259
+ KeyError: When required configuration keys are missing
260
+ Exception: For general errors during API communication or data processing
261
+
262
+ Tags:
263
+ list, email, drafts, gmail, api, search, query, pagination, important
235
264
  """
236
265
  try:
237
266
  url = f"{self.base_api_url}/drafts"
@@ -286,13 +315,22 @@ class GoogleMailApp(APIApplication):
286
315
  return f"Error listing drafts: {type(e).__name__} - {str(e)}"
287
316
 
288
317
  def get_message(self, message_id: str) -> str:
289
- """Get a specific email message by ID
318
+ """
319
+ Retrieves and formats a specific email message from Gmail API by its ID, including sender, recipient, date, subject, and message preview.
290
320
 
291
321
  Args:
292
- message_id: The ID of the message to retrieve
322
+ message_id: The unique identifier of the Gmail message to retrieve
293
323
 
294
324
  Returns:
295
- The message information or an error message
325
+ A formatted string containing the message details (ID, From, To, Date, Subject, Preview) or an error message if the retrieval fails
326
+
327
+ Raises:
328
+ NotAuthorizedError: When the request lacks proper Gmail API authorization
329
+ KeyError: When required configuration keys or message fields are missing
330
+ Exception: For general API communication errors or unexpected issues
331
+
332
+ Tags:
333
+ retrieve, email, format, api, gmail, message, important
296
334
  """
297
335
  try:
298
336
  url = f"{self.base_api_url}/messages/{message_id}"
@@ -350,15 +388,24 @@ class GoogleMailApp(APIApplication):
350
388
  def list_messages(
351
389
  self, max_results: int = 20, q: str = None, include_spam_trash: bool = False
352
390
  ) -> str:
353
- """List messages in the user's mailbox
391
+ """
392
+ Retrieves and formats a list of messages from the user's Gmail mailbox with optional filtering and pagination support.
354
393
 
355
394
  Args:
356
395
  max_results: Maximum number of messages to return (max 500, default 20)
357
- q: Search query to filter messages (same format as Gmail search)
358
- include_spam_trash: Include messages from spam and trash folders
396
+ q: Search query string to filter messages using Gmail search syntax
397
+ include_spam_trash: Boolean flag to include messages from spam and trash folders (default False)
359
398
 
360
399
  Returns:
361
- A formatted list of messages or an error message
400
+ A formatted string containing the list of message IDs and thread IDs, or an error message if the operation fails
401
+
402
+ Raises:
403
+ NotAuthorizedError: When the Gmail API authentication is invalid or missing
404
+ KeyError: When required configuration keys are missing
405
+ Exception: For general API errors, network issues, or other unexpected problems
406
+
407
+ Tags:
408
+ list, messages, gmail, search, query, pagination, important
362
409
  """
363
410
  try:
364
411
  url = f"{self.base_api_url}/messages"
@@ -417,10 +464,21 @@ class GoogleMailApp(APIApplication):
417
464
  return f"Error listing messages: {type(e).__name__} - {str(e)}"
418
465
 
419
466
  def list_labels(self) -> str:
420
- """List all labels in the user's Gmail account
467
+ """
468
+ Retrieves and formats a list of all labels (both system and user-created) from the user's Gmail account, organizing them by type and sorting them alphabetically.
469
+
470
+ Args:
471
+ None: This method takes no arguments
421
472
 
422
473
  Returns:
423
- A formatted list of labels or an error message
474
+ A formatted string containing a list of Gmail labels categorized by type (system and user), with their IDs, or an error message if the operation fails.
475
+
476
+ Raises:
477
+ NotAuthorizedError: Raised when the user's Gmail authorization is invalid or missing
478
+ Exception: Raised when any other unexpected error occurs during the API request or data processing
479
+
480
+ Tags:
481
+ list, gmail, labels, fetch, organize, important, management
424
482
  """
425
483
  try:
426
484
  url = f"{self.base_api_url}/labels"
@@ -486,13 +544,21 @@ class GoogleMailApp(APIApplication):
486
544
  return f"Error listing labels: {type(e).__name__} - {str(e)}"
487
545
 
488
546
  def create_label(self, name: str) -> str:
489
- """Create a new Gmail label
547
+ """
548
+ Creates a new Gmail label with specified visibility settings and returns creation status details.
490
549
 
491
550
  Args:
492
551
  name: The display name of the label to create
493
552
 
494
553
  Returns:
495
- A confirmation message with the new label details
554
+ A formatted string containing the creation status, including the new label's name and ID if successful, or an error message if the creation fails
555
+
556
+ Raises:
557
+ NotAuthorizedError: Raised when the request lacks proper Gmail API authorization
558
+ Exception: Raised for any other unexpected errors during label creation
559
+
560
+ Tags:
561
+ create, label, gmail, management, important
496
562
  """
497
563
  try:
498
564
  url = f"{self.base_api_url}/labels"
@@ -531,13 +597,21 @@ class GoogleMailApp(APIApplication):
531
597
  return f"Error creating label: {type(e).__name__} - {str(e)}"
532
598
 
533
599
  def get_profile(self) -> str:
534
- """Retrieve the user's Gmail profile information.
600
+ """
601
+ Retrieves and formats the user's Gmail profile information including email address, message count, thread count, and history ID.
535
602
 
536
- This method fetches the user's email address, message count, thread count,
537
- and current history ID from the Gmail API.
603
+ Args:
604
+ None: This method takes no arguments besides self
538
605
 
539
606
  Returns:
540
- A formatted string containing the user's profile information or an error message
607
+ A formatted string containing the user's Gmail profile information or an error message if the request fails
608
+
609
+ Raises:
610
+ NotAuthorizedError: Raised when the user's credentials are invalid or authorization is required
611
+ Exception: Raised for any other unexpected errors during the API request or data processing
612
+
613
+ Tags:
614
+ fetch, profile, gmail, user-info, api-request, important
541
615
  """
542
616
  try:
543
617
  url = f"{self.base_api_url}/profile"