universal-mcp-applications 0.1.22__py3-none-any.whl → 0.1.39rc8__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.

Potentially problematic release.


This version of universal-mcp-applications might be problematic. Click here for more details.

Files changed (120) hide show
  1. universal_mcp/applications/ahrefs/app.py +92 -238
  2. universal_mcp/applications/airtable/app.py +23 -122
  3. universal_mcp/applications/apollo/app.py +122 -475
  4. universal_mcp/applications/asana/app.py +605 -1755
  5. universal_mcp/applications/aws_s3/app.py +36 -103
  6. universal_mcp/applications/bill/app.py +644 -2055
  7. universal_mcp/applications/box/app.py +1246 -4159
  8. universal_mcp/applications/braze/app.py +410 -1476
  9. universal_mcp/applications/browser_use/README.md +15 -1
  10. universal_mcp/applications/browser_use/__init__.py +1 -0
  11. universal_mcp/applications/browser_use/app.py +94 -37
  12. universal_mcp/applications/cal_com_v2/app.py +207 -625
  13. universal_mcp/applications/calendly/app.py +103 -242
  14. universal_mcp/applications/canva/app.py +75 -140
  15. universal_mcp/applications/clickup/app.py +331 -798
  16. universal_mcp/applications/coda/app.py +240 -520
  17. universal_mcp/applications/confluence/app.py +497 -1285
  18. universal_mcp/applications/contentful/app.py +36 -151
  19. universal_mcp/applications/crustdata/app.py +42 -121
  20. universal_mcp/applications/dialpad/app.py +451 -924
  21. universal_mcp/applications/digitalocean/app.py +2071 -6082
  22. universal_mcp/applications/domain_checker/app.py +3 -54
  23. universal_mcp/applications/e2b/app.py +14 -64
  24. universal_mcp/applications/elevenlabs/app.py +9 -47
  25. universal_mcp/applications/exa/README.md +8 -4
  26. universal_mcp/applications/exa/app.py +408 -186
  27. universal_mcp/applications/falai/app.py +24 -101
  28. universal_mcp/applications/figma/app.py +91 -175
  29. universal_mcp/applications/file_system/app.py +2 -13
  30. universal_mcp/applications/firecrawl/app.py +186 -163
  31. universal_mcp/applications/fireflies/app.py +59 -281
  32. universal_mcp/applications/fpl/app.py +92 -529
  33. universal_mcp/applications/fpl/utils/fixtures.py +15 -49
  34. universal_mcp/applications/fpl/utils/helper.py +25 -89
  35. universal_mcp/applications/fpl/utils/league_utils.py +20 -64
  36. universal_mcp/applications/ghost_content/app.py +66 -175
  37. universal_mcp/applications/github/app.py +28 -65
  38. universal_mcp/applications/gong/app.py +140 -300
  39. universal_mcp/applications/google_calendar/app.py +26 -78
  40. universal_mcp/applications/google_docs/app.py +324 -354
  41. universal_mcp/applications/google_drive/app.py +194 -793
  42. universal_mcp/applications/google_gemini/app.py +29 -64
  43. universal_mcp/applications/google_mail/README.md +1 -0
  44. universal_mcp/applications/google_mail/app.py +93 -214
  45. universal_mcp/applications/google_searchconsole/app.py +25 -58
  46. universal_mcp/applications/google_sheet/app.py +174 -623
  47. universal_mcp/applications/google_sheet/helper.py +26 -53
  48. universal_mcp/applications/hashnode/app.py +57 -269
  49. universal_mcp/applications/heygen/app.py +77 -155
  50. universal_mcp/applications/http_tools/app.py +10 -32
  51. universal_mcp/applications/hubspot/README.md +1 -1
  52. universal_mcp/applications/hubspot/app.py +7508 -99
  53. universal_mcp/applications/jira/app.py +2419 -8334
  54. universal_mcp/applications/klaviyo/app.py +737 -1619
  55. universal_mcp/applications/linkedin/README.md +23 -4
  56. universal_mcp/applications/linkedin/app.py +861 -155
  57. universal_mcp/applications/mailchimp/app.py +696 -1851
  58. universal_mcp/applications/markitdown/app.py +8 -20
  59. universal_mcp/applications/miro/app.py +333 -815
  60. universal_mcp/applications/ms_teams/app.py +85 -207
  61. universal_mcp/applications/neon/app.py +144 -250
  62. universal_mcp/applications/notion/app.py +36 -51
  63. universal_mcp/applications/onedrive/README.md +24 -0
  64. universal_mcp/applications/onedrive/__init__.py +1 -0
  65. universal_mcp/applications/onedrive/app.py +316 -0
  66. universal_mcp/applications/openai/app.py +42 -165
  67. universal_mcp/applications/outlook/README.md +22 -9
  68. universal_mcp/applications/outlook/app.py +606 -262
  69. universal_mcp/applications/perplexity/README.md +2 -1
  70. universal_mcp/applications/perplexity/app.py +162 -20
  71. universal_mcp/applications/pipedrive/app.py +1021 -3331
  72. universal_mcp/applications/posthog/app.py +272 -541
  73. universal_mcp/applications/reddit/app.py +88 -204
  74. universal_mcp/applications/resend/app.py +41 -107
  75. universal_mcp/applications/retell/app.py +23 -50
  76. universal_mcp/applications/rocketlane/app.py +250 -963
  77. universal_mcp/applications/scraper/README.md +7 -4
  78. universal_mcp/applications/scraper/app.py +245 -283
  79. universal_mcp/applications/semanticscholar/app.py +36 -78
  80. universal_mcp/applications/semrush/app.py +43 -77
  81. universal_mcp/applications/sendgrid/app.py +826 -1576
  82. universal_mcp/applications/sentry/app.py +444 -1079
  83. universal_mcp/applications/serpapi/app.py +40 -143
  84. universal_mcp/applications/sharepoint/README.md +16 -14
  85. universal_mcp/applications/sharepoint/app.py +245 -154
  86. universal_mcp/applications/shopify/app.py +1743 -4479
  87. universal_mcp/applications/shortcut/app.py +272 -534
  88. universal_mcp/applications/slack/app.py +58 -109
  89. universal_mcp/applications/spotify/app.py +206 -405
  90. universal_mcp/applications/supabase/app.py +174 -283
  91. universal_mcp/applications/tavily/app.py +2 -2
  92. universal_mcp/applications/trello/app.py +853 -2816
  93. universal_mcp/applications/twilio/app.py +14 -50
  94. universal_mcp/applications/twitter/api_segments/compliance_api.py +4 -14
  95. universal_mcp/applications/twitter/api_segments/dm_conversations_api.py +6 -18
  96. universal_mcp/applications/twitter/api_segments/likes_api.py +1 -3
  97. universal_mcp/applications/twitter/api_segments/lists_api.py +5 -15
  98. universal_mcp/applications/twitter/api_segments/trends_api.py +1 -3
  99. universal_mcp/applications/twitter/api_segments/tweets_api.py +9 -31
  100. universal_mcp/applications/twitter/api_segments/usage_api.py +1 -5
  101. universal_mcp/applications/twitter/api_segments/users_api.py +14 -42
  102. universal_mcp/applications/whatsapp/app.py +35 -186
  103. universal_mcp/applications/whatsapp/audio.py +2 -6
  104. universal_mcp/applications/whatsapp/whatsapp.py +17 -51
  105. universal_mcp/applications/whatsapp_business/app.py +86 -299
  106. universal_mcp/applications/wrike/app.py +80 -153
  107. universal_mcp/applications/yahoo_finance/app.py +19 -65
  108. universal_mcp/applications/youtube/app.py +120 -306
  109. universal_mcp/applications/zenquotes/app.py +4 -4
  110. {universal_mcp_applications-0.1.22.dist-info → universal_mcp_applications-0.1.39rc8.dist-info}/METADATA +4 -2
  111. {universal_mcp_applications-0.1.22.dist-info → universal_mcp_applications-0.1.39rc8.dist-info}/RECORD +113 -117
  112. {universal_mcp_applications-0.1.22.dist-info → universal_mcp_applications-0.1.39rc8.dist-info}/WHEEL +1 -1
  113. universal_mcp/applications/hubspot/api_segments/__init__.py +0 -0
  114. universal_mcp/applications/hubspot/api_segments/api_segment_base.py +0 -54
  115. universal_mcp/applications/hubspot/api_segments/crm_api.py +0 -7337
  116. universal_mcp/applications/hubspot/api_segments/marketing_api.py +0 -1467
  117. universal_mcp/applications/unipile/README.md +0 -28
  118. universal_mcp/applications/unipile/__init__.py +0 -1
  119. universal_mcp/applications/unipile/app.py +0 -1077
  120. {universal_mcp_applications-0.1.22.dist-info → universal_mcp_applications-0.1.39rc8.dist-info}/licenses/LICENSE +0 -0
@@ -1,5 +1,4 @@
1
1
  from typing import Any
2
-
3
2
  from universal_mcp.applications.application import APIApplication
4
3
  from universal_mcp.integrations import Integration
5
4
 
@@ -15,13 +14,9 @@ class NotionApp(APIApplication):
15
14
  credentials = self.integration.get_credentials()
16
15
  if "headers" in credentials:
17
16
  return credentials["headers"]
18
- return {
19
- "Authorization": f"Bearer {credentials['access_token']}",
20
- "Accept": "application/json",
21
- "Notion-Version": "2022-06-28",
22
- }
17
+ return {"Authorization": f"Bearer {credentials['access_token']}", "Accept": "application/json", "Notion-Version": "2022-06-28"}
23
18
 
24
- def retrieve_a_user(self, id, request_body=None) -> dict[str, Any]:
19
+ async def retrieve_a_user(self, id, request_body=None) -> dict[str, Any]:
25
20
  """
26
21
  Retrieves a user's details from the server using their unique identifier.
27
22
 
@@ -43,13 +38,11 @@ class NotionApp(APIApplication):
43
38
  raise ValueError("Missing required parameter 'id'")
44
39
  url = f"{self.base_url}/v1/users/{id}"
45
40
  query_params = {}
46
- response = self._get(url, params=query_params)
41
+ response = await self._aget(url, params=query_params)
47
42
  response.raise_for_status()
48
43
  return response.json()
49
44
 
50
- def list_all_users(
51
- self,
52
- ) -> dict[str, Any]:
45
+ async def list_all_users(self) -> dict[str, Any]:
53
46
  """
54
47
  Retrieves a complete list of users from the API endpoint.
55
48
 
@@ -68,13 +61,11 @@ class NotionApp(APIApplication):
68
61
  """
69
62
  url = f"{self.base_url}/v1/users"
70
63
  query_params = {}
71
- response = self._get(url, params=query_params)
64
+ response = await self._aget(url, params=query_params)
72
65
  response.raise_for_status()
73
66
  return response.json()
74
67
 
75
- def retrieve_your_token_sbot_user(
76
- self,
77
- ) -> dict[str, Any]:
68
+ async def retrieve_your_token_sbot_user(self) -> dict[str, Any]:
78
69
  """
79
70
  Retrieves the current user's authentication token information from the SBOT service.
80
71
 
@@ -90,11 +81,11 @@ class NotionApp(APIApplication):
90
81
  """
91
82
  url = f"{self.base_url}/v1/users/me"
92
83
  query_params = {}
93
- response = self._get(url, params=query_params)
84
+ response = await self._aget(url, params=query_params)
94
85
  response.raise_for_status()
95
86
  return response.json()
96
87
 
97
- def retrieve_a_database(self, id) -> dict[str, Any]:
88
+ async def retrieve_a_database(self, id) -> dict[str, Any]:
98
89
  """
99
90
  Retrieves detailed information about a specific database using its unique identifier.
100
91
 
@@ -115,11 +106,11 @@ class NotionApp(APIApplication):
115
106
  raise ValueError("Missing required parameter 'id'")
116
107
  url = f"{self.base_url}/v1/databases/{id}"
117
108
  query_params = {}
118
- response = self._get(url, params=query_params)
109
+ response = await self._aget(url, params=query_params)
119
110
  response.raise_for_status()
120
111
  return response.json()
121
112
 
122
- def update_a_database(self, id, request_body=None) -> dict[str, Any]:
113
+ async def update_a_database(self, id, request_body=None) -> dict[str, Any]:
123
114
  """
124
115
  Updates a database entry with the specified ID using a PATCH request.
125
116
 
@@ -145,7 +136,7 @@ class NotionApp(APIApplication):
145
136
  response.raise_for_status()
146
137
  return response.json()
147
138
 
148
- def query_a_database(self, id, request_body=None) -> dict[str, Any]:
139
+ async def query_a_database(self, id, request_body=None) -> dict[str, Any]:
149
140
  """
150
141
  Executes a database query operation using a specified database ID and optional request parameters
151
142
 
@@ -167,11 +158,11 @@ class NotionApp(APIApplication):
167
158
  raise ValueError("Missing required parameter 'id'")
168
159
  url = f"{self.base_url}/v1/databases/{id}/query"
169
160
  query_params = {}
170
- response = self._post(url, data=request_body, params=query_params)
161
+ response = await self._apost(url, data=request_body, params=query_params)
171
162
  response.raise_for_status()
172
163
  return response.json()
173
164
 
174
- def create_a_database(self, request_body=None) -> dict[str, Any]:
165
+ async def create_a_database(self, request_body=None) -> dict[str, Any]:
175
166
  """
176
167
  Creates a new database on the server by sending a POST request to the database endpoint.
177
168
 
@@ -191,11 +182,11 @@ class NotionApp(APIApplication):
191
182
  """
192
183
  url = f"{self.base_url}/v1/databases/"
193
184
  query_params = {}
194
- response = self._post(url, data=request_body, params=query_params)
185
+ response = await self._apost(url, data=request_body, params=query_params)
195
186
  response.raise_for_status()
196
187
  return response.json()
197
188
 
198
- def create_a_page(self, request_body=None) -> dict[str, Any]:
189
+ async def create_a_page(self, request_body=None) -> dict[str, Any]:
199
190
  """
200
191
  Creates a new page by sending a POST request to the API endpoint.
201
192
 
@@ -214,11 +205,11 @@ class NotionApp(APIApplication):
214
205
  """
215
206
  url = f"{self.base_url}/v1/pages/"
216
207
  query_params = {}
217
- response = self._post(url, data=request_body, params=query_params)
208
+ response = await self._apost(url, data=request_body, params=query_params)
218
209
  response.raise_for_status()
219
210
  return response.json()
220
211
 
221
- def retrieve_a_page(self, id) -> dict[str, Any]:
212
+ async def retrieve_a_page(self, id) -> dict[str, Any]:
222
213
  """
223
214
  Retrieves a specific page's data from a remote server using its unique identifier.
224
215
 
@@ -239,11 +230,11 @@ class NotionApp(APIApplication):
239
230
  raise ValueError("Missing required parameter 'id'")
240
231
  url = f"{self.base_url}/v1/pages/{id}"
241
232
  query_params = {}
242
- response = self._get(url, params=query_params)
233
+ response = await self._aget(url, params=query_params)
243
234
  response.raise_for_status()
244
235
  return response.json()
245
236
 
246
- def update_page_properties(self, id, request_body=None) -> dict[str, Any]:
237
+ async def update_page_properties(self, id, request_body=None) -> dict[str, Any]:
247
238
  """
248
239
  Updates the properties of a page with the specified ID using the provided request body.
249
240
 
@@ -270,7 +261,7 @@ class NotionApp(APIApplication):
270
261
  response.raise_for_status()
271
262
  return response.json()
272
263
 
273
- def retrieve_a_page_property_item(self, page_id, property_id) -> dict[str, Any]:
264
+ async def retrieve_a_page_property_item(self, page_id, property_id) -> dict[str, Any]:
274
265
  """
275
266
  Retrieves a specific property item from a Notion page using the page ID and property ID.
276
267
 
@@ -294,11 +285,11 @@ class NotionApp(APIApplication):
294
285
  raise ValueError("Missing required parameter 'property_id'")
295
286
  url = f"{self.base_url}/v1/pages/{page_id}/properties/{property_id}"
296
287
  query_params = {}
297
- response = self._get(url, params=query_params)
288
+ response = await self._aget(url, params=query_params)
298
289
  response.raise_for_status()
299
290
  return response.json()
300
291
 
301
- def retrieve_block_children(self, id, page_size=None) -> dict[str, Any]:
292
+ async def retrieve_block_children(self, id, page_size=None) -> dict[str, Any]:
302
293
  """
303
294
  Retrieves all child blocks for a specified parent block using its ID via the API.
304
295
 
@@ -320,11 +311,11 @@ class NotionApp(APIApplication):
320
311
  raise ValueError("Missing required parameter 'id'")
321
312
  url = f"{self.base_url}/v1/blocks/{id}/children"
322
313
  query_params = {k: v for k, v in [("page_size", page_size)] if v is not None}
323
- response = self._get(url, params=query_params)
314
+ response = await self._aget(url, params=query_params)
324
315
  response.raise_for_status()
325
316
  return response.json()
326
317
 
327
- def append_block_children(self, id, request_body=None) -> dict[str, Any]:
318
+ async def append_block_children(self, id, request_body=None) -> dict[str, Any]:
328
319
  """
329
320
  Appends child elements to a specified block and returns the updated block data.
330
321
 
@@ -350,7 +341,7 @@ class NotionApp(APIApplication):
350
341
  response.raise_for_status()
351
342
  return response.json()
352
343
 
353
- def retrieve_a_block(self, id) -> dict[str, Any]:
344
+ async def retrieve_a_block(self, id) -> dict[str, Any]:
354
345
  """
355
346
  Retrieves a specific block of data from the API using its unique identifier.
356
347
 
@@ -371,11 +362,11 @@ class NotionApp(APIApplication):
371
362
  raise ValueError("Missing required parameter 'id'")
372
363
  url = f"{self.base_url}/v1/blocks/{id}"
373
364
  query_params = {}
374
- response = self._get(url, params=query_params)
365
+ response = await self._aget(url, params=query_params)
375
366
  response.raise_for_status()
376
367
  return response.json()
377
368
 
378
- def delete_a_block(self, id) -> dict[str, Any]:
369
+ async def delete_a_block(self, id) -> dict[str, Any]:
379
370
  """
380
371
  Deletes a specified block by its ID and returns the server response.
381
372
 
@@ -396,11 +387,11 @@ class NotionApp(APIApplication):
396
387
  raise ValueError("Missing required parameter 'id'")
397
388
  url = f"{self.base_url}/v1/blocks/{id}"
398
389
  query_params = {}
399
- response = self._delete(url, params=query_params)
390
+ response = await self._adelete(url, params=query_params)
400
391
  response.raise_for_status()
401
392
  return response.json()
402
393
 
403
- def update_a_block(self, id, request_body=None) -> dict[str, Any]:
394
+ async def update_a_block(self, id, request_body=None) -> dict[str, Any]:
404
395
  """
405
396
  Updates a specific block resource via a PATCH request to the API endpoint
406
397
 
@@ -426,7 +417,7 @@ class NotionApp(APIApplication):
426
417
  response.raise_for_status()
427
418
  return response.json()
428
419
 
429
- def search(self, request_body=None) -> dict[str, Any]:
420
+ async def search(self, request_body=None) -> dict[str, Any]:
430
421
  """
431
422
  Executes a search operation by sending a POST request to the search endpoint and returns the results
432
423
 
@@ -445,13 +436,11 @@ class NotionApp(APIApplication):
445
436
  """
446
437
  url = f"{self.base_url}/v1/search"
447
438
  query_params = {}
448
- response = self._post(url, data=request_body, params=query_params)
439
+ response = await self._apost(url, data=request_body, params=query_params)
449
440
  response.raise_for_status()
450
441
  return response.json()
451
442
 
452
- def retrieve_comments(
453
- self, block_id=None, page_size=None, request_body=None
454
- ) -> dict[str, Any]:
443
+ async def retrieve_comments(self, block_id=None, page_size=None, request_body=None) -> dict[str, Any]:
455
444
  """
456
445
  Retrieves comments from a remote server with optional block filtering and pagination support.
457
446
 
@@ -471,16 +460,12 @@ class NotionApp(APIApplication):
471
460
  retrieve, fetch, comments, api, pagination, http
472
461
  """
473
462
  url = f"{self.base_url}/v1/comments"
474
- query_params = {
475
- k: v
476
- for k, v in [("block_id", block_id), ("page_size", page_size)]
477
- if v is not None
478
- }
479
- response = self._get(url, params=query_params)
463
+ query_params = {k: v for k, v in [("block_id", block_id), ("page_size", page_size)] if v is not None}
464
+ response = await self._aget(url, params=query_params)
480
465
  response.raise_for_status()
481
466
  return response.json()
482
467
 
483
- def add_comment_to_page(self, request_body=None) -> dict[str, Any]:
468
+ async def add_comment_to_page(self, request_body=None) -> dict[str, Any]:
484
469
  """
485
470
  Adds a comment to a page by making an HTTP POST request to the comments endpoint.
486
471
 
@@ -499,7 +484,7 @@ class NotionApp(APIApplication):
499
484
  """
500
485
  url = f"{self.base_url}/v1/comments"
501
486
  query_params = {}
502
- response = self._post(url, data=request_body, params=query_params)
487
+ response = await self._apost(url, data=request_body, params=query_params)
503
488
  response.raise_for_status()
504
489
  return response.json()
505
490
 
@@ -0,0 +1,24 @@
1
+ # OnedriveApp MCP Server
2
+
3
+ An MCP Server for the OnedriveApp API.
4
+
5
+ ## 🛠️ Tool List
6
+
7
+ This is automatically generated from OpenAPI schema for the OnedriveApp API.
8
+
9
+
10
+ | Tool | Description |
11
+ |------|-------------|
12
+ | `get_drive_info` | Fetches high-level information about the user's entire OneDrive. It returns drive-wide details like the owner and storage quota, differing from `get_item_metadata` which describes a specific item, and `get_my_profile` which retrieves general user account information. |
13
+ | `search_files` | Searches the user's entire OneDrive for files and folders matching a specified text query. This function performs a comprehensive search from the drive's root, distinguishing it from `list_files` or `list_folders` which only browse the contents of a single directory. |
14
+ | `get_item_metadata` | Fetches detailed metadata for a specific file or folder using its unique ID. It returns properties like name, size, and type. Unlike `get_document_content`, it doesn't retrieve the file's actual content, focusing solely on the item's attributes for quick inspection without a full download. |
15
+ | `create_folder` | Creates a new folder with a specified name within a parent directory, which defaults to the root. Returns metadata for the new folder. Unlike `create_folder_and_list`, this function only creates the folder and returns its specific metadata, not the parent directory's contents. |
16
+ | `delete_item` | Permanently deletes a specified file or folder from OneDrive using its unique item ID. This versatile function can remove any type of drive item, distinguished from functions that only list or create specific types. A successful deletion returns an empty response, confirming the item's removal. |
17
+ | `download_file` | Retrieves a temporary, pre-authenticated download URL for a specific file using its item ID. This function provides a link for subsequent download, differing from `get_document_content` which directly fetches the file's raw content. The URL is returned within a dictionary. |
18
+ | `upload_file` | Uploads a local binary file (under 4MB) from a given path to a specified OneDrive folder. Unlike `upload_text_file`, which uploads string content, this function reads from the filesystem. The destination filename can be customized, and it returns the new file's metadata upon completion. |
19
+ | `get_my_profile` | Fetches the profile for the currently authenticated user, specifically retrieving their ID and user principal name. This function confirms user identity, distinguishing it from `get_drive_info`, which returns details about the OneDrive storage space (e.g., quota) rather than the user's personal profile. |
20
+ | `list_folders` | Retrieves a list of only the folders within a specified parent directory in OneDrive. Unlike `_list_drive_items` which returns all items, this function filters the results to exclude files. Defaults to the root directory if no parent `item_id` is provided. |
21
+ | `list_files` | Retrieves a list of files within a specified OneDrive folder, defaulting to the root. Unlike `_list_drive_items` which fetches all items, this function filters the results to exclusively return items identified as files, excluding any subdirectories. |
22
+ | `create_folder_and_list` | Performs a composite action: creates a new folder, then lists all items (files and folders) within that parent directory. This confirms creation by returning the parent's updated contents, distinct from `create_folder` which only returns the new folder's metadata. |
23
+ | `upload_text_file` | Creates and uploads a new file to OneDrive directly from a string of text content. Unlike `upload_file`, which requires a local file path, this function is specifically for creating a text file from in-memory string data, with a customizable name and destination folder. |
24
+ | `get_document_content` | Retrieves the content of a specific file by its item ID and returns it directly as base64-encoded data. This function is distinct from `download_file`, which only provides a temporary URL for the content, and from `get_item_metadata`, which returns file attributes without the content itself. The function fetches the content by following the file's pre-authenticated download URL. |
@@ -0,0 +1 @@
1
+ from .app import OnedriveApp
@@ -0,0 +1,316 @@
1
+ import base64
2
+ import os
3
+ from typing import Any
4
+ from loguru import logger
5
+ from universal_mcp.applications.application import APIApplication
6
+ from universal_mcp.integrations import Integration
7
+
8
+
9
+ class OnedriveApp(APIApplication):
10
+ """
11
+ Application for interacting with Microsoft OneDrive API (via Microsoft Graph).
12
+ Provides tools to manage files, folders, and access Drive information.
13
+ """
14
+
15
+ def __init__(self, integration: Integration | None = None, **kwargs) -> None:
16
+ super().__init__(name="onedrive", integration=integration, **kwargs)
17
+ self.base_url = "https://graph.microsoft.com/v1.0"
18
+
19
+ async def get_my_profile(self) -> dict[str, Any]:
20
+ """
21
+ Fetches the profile for the currently authenticated user, specifically retrieving their ID and user principal name. This function confirms user identity, distinguishing it from `get_drive_info`, which returns details about the OneDrive storage space (e.g., quota) rather than the user's personal profile.
22
+
23
+ Returns:
24
+ dict[str, Any]: A dictionary containing the user's id and userPrincipalName.
25
+
26
+ Raises:
27
+ HTTPStatusError: If the API request fails.
28
+
29
+ Tags:
30
+ profile, user, account
31
+ """
32
+ url = f"{self.base_url}/me"
33
+ query_params = {"$select": "id,userPrincipalName"}
34
+ response = await self._aget(url, params=query_params)
35
+ return self._handle_response(response)
36
+
37
+ async def get_drive_info(self) -> dict[str, Any]:
38
+ """
39
+ Fetches high-level information about the user's entire OneDrive. It returns drive-wide details like the owner and storage quota, differing from `get_item_metadata` which describes a specific item, and `get_my_profile` which retrieves general user account information.
40
+
41
+ Returns:
42
+ A dictionary containing drive information.
43
+
44
+ Tags:
45
+ drive, storage, quota, info
46
+ """
47
+ url = f"{self.base_url}/me/drive"
48
+ response = await self._aget(url)
49
+ return self._handle_response(response)
50
+
51
+ def _list_drive_items(self, item_id: str = "root") -> dict[str, Any]:
52
+ """
53
+ Lists the files and folders in the current user's OneDrive.
54
+
55
+ Args:
56
+ item_id (str, optional): The ID of the folder to list. Defaults to 'root'.
57
+
58
+ Returns:
59
+ A dictionary containing the list of files and folders.
60
+ """
61
+ url = f"{self.base_url}/me/drive/items/{item_id}/children"
62
+ response = self._get(url)
63
+ return self._handle_response(response)
64
+
65
+ async def search_files(self, query: str) -> dict[str, Any]:
66
+ """
67
+ Searches the user's entire OneDrive for files and folders matching a specified text query. This function performs a comprehensive search from the drive's root, distinguishing it from `list_files` or `list_folders` which only browse the contents of a single directory.
68
+
69
+ Args:
70
+ query (str): The search query.
71
+
72
+ Returns:
73
+ A dictionary containing the search results.
74
+
75
+ Tags:
76
+ search, find, query, files, important
77
+ """
78
+ if not query:
79
+ raise ValueError("Search query cannot be empty.")
80
+ url = f"{self.base_url}/me/drive/root/search(q='{query}')"
81
+ response = await self._aget(url)
82
+ return self._handle_response(response)
83
+
84
+ async def get_item_metadata(self, item_id: str) -> dict[str, Any]:
85
+ """
86
+ Fetches detailed metadata for a specific file or folder using its unique ID. It returns properties like name, size, and type. Unlike `get_document_content`, it doesn't retrieve the file's actual content, focusing solely on the item's attributes for quick inspection without a full download.
87
+
88
+ Args:
89
+ item_id (str): The ID of the file or folder.
90
+
91
+ Returns:
92
+ A dictionary containing the item's metadata.
93
+
94
+ Tags:
95
+ metadata, info, file, folder
96
+ """
97
+ if not item_id:
98
+ raise ValueError("Missing required parameter 'item_id'.")
99
+ url = f"{self.base_url}/me/drive/items/{item_id}"
100
+ response = await self._aget(url)
101
+ return self._handle_response(response)
102
+
103
+ async def create_folder(self, name: str, parent_id: str = "root") -> dict[str, Any]:
104
+ """
105
+ Creates a new folder with a specified name within a parent directory, which defaults to the root. Returns metadata for the new folder. Unlike `create_folder_and_list`, this function only creates the folder and returns its specific metadata, not the parent directory's contents.
106
+
107
+ Args:
108
+ name (str): The name of the new folder.
109
+ parent_id (str, optional): The ID of the parent folder. Defaults to 'root'.
110
+
111
+ Returns:
112
+ A dictionary containing the new folder's metadata.
113
+
114
+ Tags:
115
+ create, folder, directory, important
116
+ """
117
+ if not name:
118
+ raise ValueError("Folder name cannot be empty.")
119
+ url = f"{self.base_url}/me/drive/items/{parent_id}/children"
120
+ data = {"name": name, "folder": {}, "@microsoft.graph.conflictBehavior": "rename"}
121
+ response = await self._apost(url, data=data)
122
+ return self._handle_response(response)
123
+
124
+ async def delete_item(self, item_id: str) -> dict[str, Any]:
125
+ """
126
+ Permanently deletes a specified file or folder from OneDrive using its unique item ID. This versatile function can remove any type of drive item, distinguished from functions that only list or create specific types. A successful deletion returns an empty response, confirming the item's removal.
127
+
128
+ Args:
129
+ item_id (str): The ID of the item to delete.
130
+
131
+ Returns:
132
+ An empty dictionary if successful.
133
+
134
+ Tags:
135
+ delete, remove, file, folder, important
136
+ """
137
+ if not item_id:
138
+ raise ValueError("Missing required parameter 'item_id'.")
139
+ url = f"{self.base_url}/me/drive/items/{item_id}"
140
+ response = await self._adelete(url)
141
+ return self._handle_response(response)
142
+
143
+ async def download_file(self, item_id: str) -> dict[str, Any]:
144
+ """
145
+ Retrieves a temporary, pre-authenticated download URL for a specific file using its item ID. This function provides a link for subsequent download, differing from `get_document_content` which directly fetches the file's raw content. The URL is returned within a dictionary.
146
+
147
+ Args:
148
+ item_id (str): The ID of the file to download.
149
+
150
+ Returns:
151
+ A dictionary containing the download URL for the file under the key '@microsoft.graph.downloadUrl'.
152
+
153
+ Tags:
154
+ download, file, get, important
155
+ """
156
+ if not item_id:
157
+ raise ValueError("Missing required parameter 'item_id'.")
158
+ url = f"{self.base_url}/me/drive/items/{item_id}"
159
+ response = await self._aget(url)
160
+ metadata = self._handle_response(response)
161
+ download_url = metadata.get("@microsoft.graph.downloadUrl")
162
+ if not download_url:
163
+ raise ValueError("Could not retrieve download URL for the item.")
164
+ return {"download_url": download_url}
165
+
166
+ async def upload_file(self, file_path: str, parent_id: str = "root", file_name: str | None = None) -> dict[str, Any]:
167
+ """
168
+ Uploads a local binary file (under 4MB) from a given path to a specified OneDrive folder. Unlike `upload_text_file`, which uploads string content, this function reads from the filesystem. The destination filename can be customized, and it returns the new file's metadata upon completion.
169
+
170
+ Args:
171
+ file_path (str): The local path to the file to upload.
172
+ parent_id (str, optional): The ID of the folder to upload the file to. Defaults to 'root'.
173
+ file_name (str, optional): The name to give the uploaded file. If not provided, the local filename is used.
174
+
175
+ Returns:
176
+ A dictionary containing the uploaded file's metadata.
177
+
178
+ Tags:
179
+ upload, file, put, important
180
+ """
181
+ if not os.path.exists(file_path):
182
+ raise FileNotFoundError(f"The file was not found at path: {file_path}")
183
+ if not file_name:
184
+ file_name = os.path.basename(file_path)
185
+ url = f"{self.base_url}/me/drive/items/{parent_id}:/{file_name}:/content"
186
+ with open(file_path, "rb") as f:
187
+ data = f.read()
188
+ response = await self._aput(url, data=data, content_type="application/octet-stream")
189
+ return self._handle_response(response)
190
+
191
+ async def list_folders(self, item_id: str = "root") -> dict[str, Any]:
192
+ """
193
+ Retrieves a list of only the folders within a specified parent directory in OneDrive. Unlike `_list_drive_items` which returns all items, this function filters the results to exclude files. Defaults to the root directory if no parent `item_id` is provided.
194
+
195
+ Args:
196
+ item_id (str, optional): The ID of the folder to list from. Defaults to 'root'.
197
+
198
+ Returns:
199
+ A dictionary containing the list of folders.
200
+
201
+ Tags:
202
+ list, folders, directories, important
203
+ """
204
+ all_items = self._list_drive_items(item_id=item_id)
205
+ folders = [item for item in all_items.get("value", []) if "folder" in item]
206
+ return {"value": folders}
207
+
208
+ async def list_files(self, item_id: str = "root") -> dict[str, Any]:
209
+ """
210
+ Retrieves a list of files within a specified OneDrive folder, defaulting to the root. Unlike `_list_drive_items` which fetches all items, this function filters the results to exclusively return items identified as files, excluding any subdirectories.
211
+
212
+ Args:
213
+ item_id (str, optional): The ID of the folder to list files from. Defaults to 'root'.
214
+
215
+ Returns:
216
+ A dictionary containing the list of files.
217
+
218
+ Tags:
219
+ list, files, documents, important
220
+ """
221
+ all_items = self._list_drive_items(item_id=item_id)
222
+ files = [item for item in all_items.get("value", []) if "file" in item]
223
+ return {"value": files}
224
+
225
+ async def create_folder_and_list(self, name: str, parent_id: str = "root") -> dict[str, Any]:
226
+ """
227
+ Performs a composite action: creates a new folder, then lists all items (files and folders) within that parent directory. This confirms creation by returning the parent's updated contents, distinct from `create_folder` which only returns the new folder's metadata.
228
+
229
+ Args:
230
+ name (str): The name of the new folder.
231
+ parent_id (str, optional): The ID of the parent folder. Defaults to 'root'.
232
+
233
+ Returns:
234
+ A dictionary containing the list of items in the parent folder after creation.
235
+
236
+ Tags:
237
+ create, folder, list, important
238
+ """
239
+ await self.create_folder(name=name, parent_id=parent_id)
240
+ return self._list_drive_items(item_id=parent_id)
241
+
242
+ async def upload_text_file(self, content: str, parent_id: str = "root", file_name: str = "new_file.txt") -> dict[str, Any]:
243
+ """
244
+ Creates and uploads a new file to OneDrive directly from a string of text content. Unlike `upload_file`, which requires a local file path, this function is specifically for creating a text file from in-memory string data, with a customizable name and destination folder.
245
+
246
+ Args:
247
+ content (str): The text content to upload.
248
+ parent_id (str, optional): The ID of the folder to upload the file to. Defaults to 'root'.
249
+ file_name (str, optional): The name to give the uploaded file. Defaults to 'new_file.txt'.
250
+
251
+ Returns:
252
+ A dictionary containing the uploaded file's metadata.
253
+
254
+ Tags:
255
+ upload, text, file, create, important
256
+ """
257
+ if not file_name:
258
+ raise ValueError("File name cannot be empty.")
259
+ url = f"{self.base_url}/me/drive/items/{parent_id}:/{file_name}:/content"
260
+ data = content.encode("utf-8")
261
+ response = await self._aput(url, data=data, content_type="text/plain")
262
+ return self._handle_response(response)
263
+
264
+ async def get_document_content(self, item_id: str) -> dict[str, Any]:
265
+ """
266
+ Retrieves the content of a specific file by its item ID and returns it directly as base64-encoded data. This function is distinct from `download_file`, which only provides a temporary URL for the content, and from `get_item_metadata`, which returns file attributes without the content itself. The function fetches the content by following the file's pre-authenticated download URL.
267
+
268
+ Args:
269
+ item_id (str): The ID of the file.
270
+
271
+ Returns:
272
+ dict[str, Any]: A dictionary containing the file details:
273
+ - 'type' (str): The general type of the file (e.g., "image", "audio", "video", "file").
274
+ - 'data' (str): The base64 encoded content of the file.
275
+ - 'mime_type' (str): The MIME type of the file.
276
+ - 'file_name' (str): The name of the file.
277
+
278
+ Tags:
279
+ get, content, read, file, important
280
+ """
281
+ if not item_id:
282
+ raise ValueError("Missing required parameter 'item_id'.")
283
+ metadata = await self.get_item_metadata(item_id=item_id)
284
+ file_metadata = metadata.get("file")
285
+ if not file_metadata:
286
+ raise ValueError(f"Item with ID '{item_id}' is not a file.")
287
+ file_mime_type = file_metadata.get("mimeType", "application/octet-stream")
288
+ file_name = metadata.get("name")
289
+ download_url = metadata.get("@microsoft.graph.downloadUrl")
290
+ if not download_url:
291
+ logger.error(f"Could not find @microsoft.graph.downloadUrl in metadata for item {item_id}")
292
+ raise ValueError("Could not retrieve download URL for the item.")
293
+ response = await self._aget(download_url)
294
+ response.raise_for_status()
295
+ content = response.content
296
+ attachment_type = file_mime_type.split("/")[0] if "/" in file_mime_type else "file"
297
+ if attachment_type not in ["image", "audio", "video", "text"]:
298
+ attachment_type = "file"
299
+ return {"type": attachment_type, "data": content, "mime_type": file_mime_type, "file_name": file_name}
300
+
301
+ def list_tools(self):
302
+ return [
303
+ self.get_drive_info,
304
+ self.search_files,
305
+ self.get_item_metadata,
306
+ self.create_folder,
307
+ self.delete_item,
308
+ self.download_file,
309
+ self.upload_file,
310
+ self.get_my_profile,
311
+ self.list_folders,
312
+ self.list_files,
313
+ self.create_folder_and_list,
314
+ self.upload_text_file,
315
+ self.get_document_content,
316
+ ]