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,18 +1,16 @@
1
- from typing import Any # For type hinting
2
-
1
+ from typing import Any
3
2
  import httpx
4
3
  from loguru import logger
5
4
  from universal_mcp.applications.application import APIApplication
6
- from universal_mcp.exceptions import NotAuthorizedError # For auth errors
7
- from universal_mcp.integrations import Integration # For integration type hint
8
-
9
- from serpapi import SerpApiClient as SerpApiSearch # Added SerpApiError
5
+ from universal_mcp.exceptions import NotAuthorizedError
6
+ from universal_mcp.integrations import Integration
7
+ from serpapi import SerpApiClient as SerpApiSearch
10
8
 
11
9
 
12
10
  class SerpapiApp(APIApplication):
13
11
  def __init__(self, integration: Integration | None = None, **kwargs: Any) -> None:
14
12
  super().__init__(name="serpapi", integration=integration, **kwargs)
15
- self._serpapi_api_key: str | None = None # Cache for the API key
13
+ self._serpapi_api_key: str | None = None
16
14
  self.base_url = "https://serpapi.com/search"
17
15
 
18
16
  @property
@@ -23,54 +21,33 @@ class SerpapiApp(APIApplication):
23
21
  if self._serpapi_api_key is None:
24
22
  if not self.integration:
25
23
  logger.error("SerpApi App: Integration not configured.")
26
- raise NotAuthorizedError(
27
- "Integration not configured for SerpApi App. Cannot retrieve API key."
28
- )
29
-
24
+ raise NotAuthorizedError("Integration not configured for SerpApi App. Cannot retrieve API key.")
30
25
  try:
31
26
  credentials = self.integration.get_credentials()
32
27
  except NotAuthorizedError as e:
33
- logger.error(
34
- f"SerpApi App: Authorization error when fetching credentials: {e.message}"
35
- )
36
- raise # Re-raise the original NotAuthorizedError
28
+ logger.error(f"SerpApi App: Authorization error when fetching credentials: {e.message}")
29
+ raise
37
30
  except Exception as e:
38
- logger.error(
39
- f"SerpApi App: Unexpected error when fetching credentials: {e}",
40
- exc_info=True,
41
- )
31
+ logger.error(f"SerpApi App: Unexpected error when fetching credentials: {e}", exc_info=True)
42
32
  raise NotAuthorizedError(f"Failed to get SerpApi credentials: {e}")
43
-
44
- api_key = (
45
- credentials.get("api_key")
46
- or credentials.get("API_KEY") # Check common variations
47
- or credentials.get("apiKey")
48
- )
49
-
33
+ api_key = credentials.get("api_key") or credentials.get("API_KEY") or credentials.get("apiKey")
50
34
  if not api_key:
51
35
  logger.error("SerpApi App: API key not found in credentials.")
52
- action_message = "API key for SerpApi is missing. Please ensure it's set in the store (e.g., SERPAPI_API_KEY in credentials)."
53
- if hasattr(self.integration, "authorize") and callable(
54
- self.integration.authorize
55
- ):
36
+ action_message = (
37
+ "API key for SerpApi is missing. Please ensure it's set in the store (e.g., SERPAPI_API_KEY in credentials)."
38
+ )
39
+ if hasattr(self.integration, "authorize") and callable(self.integration.authorize):
56
40
  try:
57
41
  auth_details = self.integration.authorize()
58
42
  if isinstance(auth_details, str):
59
43
  action_message = auth_details
60
44
  elif isinstance(auth_details, dict) and "url" in auth_details:
61
- action_message = (
62
- f"Please authorize via: {auth_details['url']}"
63
- )
64
- elif (
65
- isinstance(auth_details, dict) and "message" in auth_details
66
- ):
45
+ action_message = f"Please authorize via: {auth_details['url']}"
46
+ elif isinstance(auth_details, dict) and "message" in auth_details:
67
47
  action_message = auth_details["message"]
68
48
  except Exception as auth_e:
69
- logger.warning(
70
- f"Could not retrieve specific authorization action for SerpApi: {auth_e}"
71
- )
49
+ logger.warning(f"Could not retrieve specific authorization action for SerpApi: {auth_e}")
72
50
  raise NotAuthorizedError(action_message)
73
-
74
51
  self._serpapi_api_key = api_key
75
52
  logger.info("SerpApi API Key successfully retrieved and cached.")
76
53
  return self._serpapi_api_key
@@ -93,27 +70,15 @@ class SerpapiApp(APIApplication):
93
70
  search, async, web-scraping, api, serpapi, important
94
71
  """
95
72
  request_params = params or {}
96
-
97
73
  try:
98
- current_api_key = self.serpapi_api_key # This can raise NotAuthorizedError
74
+ current_api_key = self.serpapi_api_key
99
75
  logger.info("Attempting SerpApi search.")
100
-
101
- serpapi_call_params = {
102
- "api_key": current_api_key,
103
- "engine": "google_light", # Fastest engine by default
104
- **request_params, # Include any additional parameters from the user
105
- }
106
-
107
- # SerpApiSearch (SerpApiClient) uses the 'requests' library and its get_dict() is synchronous.
108
- # If true async behavior is needed, this call should be wrapped with asyncio.to_thread.
76
+ serpapi_call_params = {"api_key": current_api_key, "engine": "google_light", **request_params}
109
77
  search_client = SerpApiSearch(serpapi_call_params)
110
78
  data = search_client.get_dict()
111
-
112
- # Check for errors returned in the API response body
113
79
  if "error" in data:
114
80
  error_message = data["error"]
115
81
  logger.error(f"SerpApi API returned an error: {error_message}")
116
- # Keywords indicating authorization/authentication issues
117
82
  auth_error_keywords = [
118
83
  "invalid api key",
119
84
  "authorization failed",
@@ -122,76 +87,42 @@ class SerpapiApp(APIApplication):
122
87
  "account disabled",
123
88
  "private api key is missing",
124
89
  ]
125
- if any(
126
- keyword in error_message.lower() for keyword in auth_error_keywords
127
- ):
90
+ if any((keyword in error_message.lower() for keyword in auth_error_keywords)):
128
91
  raise NotAuthorizedError(f"SerpApi Error: {error_message}")
129
- return f"SerpApi API Error: {error_message}" # Other API errors (e.g., missing parameters)
130
-
131
- # Process organic search results if available
92
+ return f"SerpApi API Error: {error_message}"
132
93
  if "organic_results" in data:
133
94
  formatted_results = []
134
95
  for result in data.get("organic_results", []):
135
96
  title = result.get("title", "No title")
136
97
  link = result.get("link", "No link")
137
98
  snippet = result.get("snippet", "No snippet")
138
- formatted_results.append(
139
- f"Title: {title}\nLink: {link}\nSnippet: {snippet}\n"
140
- )
141
- return (
142
- "\n".join(formatted_results)
143
- if formatted_results
144
- else "No organic results found."
145
- )
99
+ formatted_results.append(f"Title: {title}\nLink: {link}\nSnippet: {snippet}\n")
100
+ return "\n".join(formatted_results) if formatted_results else "No organic results found."
146
101
  else:
147
102
  return "No organic results found."
148
-
149
- except (
150
- NotAuthorizedError
151
- ): # Catches from self.serpapi_api_key or explicit raise above
103
+ except NotAuthorizedError:
152
104
  logger.error("SerpApi search failed due to an authorization error.")
153
- raise # Re-raise to be handled by the MCP framework
154
-
155
- except httpx.HTTPStatusError as e: # Kept from original for robustness, though SerpApiClient uses 'requests'
105
+ raise
106
+ except httpx.HTTPStatusError as e:
156
107
  logger.warning(
157
108
  f"SerpApi search encountered httpx.HTTPStatusError (unexpected with default SerpApiClient): {e.response.status_code}",
158
109
  exc_info=True,
159
110
  )
160
111
  if e.response.status_code == 429:
161
112
  return "Error: Rate limit exceeded (HTTP 429). Please try again later."
162
- elif (
163
- e.response.status_code == 401
164
- ): # Key was fetched but rejected by API with HTTP 401
165
- raise NotAuthorizedError(
166
- "Error: Invalid API key (HTTP 401). Please check your SERPAPI_API_KEY."
167
- )
113
+ elif e.response.status_code == 401:
114
+ raise NotAuthorizedError("Error: Invalid API key (HTTP 401). Please check your SERPAPI_API_KEY.")
168
115
  else:
169
116
  return f"HTTP Error: {e.response.status_code} - {e.response.text}"
170
-
171
- except Exception as e: # General catch-all, similar to E2B's final catch
117
+ except Exception as e:
172
118
  error_message_lower = str(e).lower()
173
119
  logger.error(f"Unexpected error during SerpApi search: {e}", exc_info=True)
174
- # Infer auth error from generic exception message
175
- auth_error_keywords = [
176
- "authentication",
177
- "api key",
178
- "unauthorized",
179
- "401",
180
- "forbidden",
181
- "invalid key",
182
- ]
183
- if any(keyword in error_message_lower for keyword in auth_error_keywords):
184
- raise NotAuthorizedError(
185
- f"SerpApi authentication/authorization failed: {str(e)}"
186
- )
120
+ auth_error_keywords = ["authentication", "api key", "unauthorized", "401", "forbidden", "invalid key"]
121
+ if any((keyword in error_message_lower for keyword in auth_error_keywords)):
122
+ raise NotAuthorizedError(f"SerpApi authentication/authorization failed: {str(e)}")
187
123
  return f"An unexpected error occurred during search: {str(e)}"
188
124
 
189
- async def google_maps_search(
190
- self,
191
- q: str | None = None,
192
- ll: str | None = None,
193
- place_id: str | None = None,
194
- ) -> dict[str, Any]:
125
+ async def google_maps_search(self, q: str | None = None, ll: str | None = None, place_id: str | None = None) -> dict[str, Any]:
195
126
  """
196
127
  Executes a Google Maps search via SerpApi using a query, coordinates, or place ID. It enhances the results by adding a `google_maps_url` to each location, distinguishing it from `get_google_maps_reviews` which retrieves reviews for a known place.
197
128
 
@@ -210,43 +141,23 @@ class SerpapiApp(APIApplication):
210
141
  Tags:
211
142
  google-maps, search, location, places, important
212
143
  """
213
-
214
144
  query_params = {}
215
- query_params = {
216
- "engine": "google_maps",
217
- "api_key": self.serpapi_api_key,
218
- }
219
-
145
+ query_params = {"engine": "google_maps", "api_key": self.serpapi_api_key}
220
146
  if q is not None:
221
147
  query_params["q"] = q
222
-
223
148
  if ll is not None:
224
149
  query_params["ll"] = ll
225
-
226
150
  if place_id is not None:
227
151
  query_params["place_id"] = place_id
228
-
229
- response = self._get(
230
- self.base_url,
231
- params=query_params,
232
- )
152
+ response = await self._aget(self.base_url, params=query_params)
233
153
  data = self._handle_response(response)
234
-
235
- # Add Google Maps URLs for each place in the results
236
154
  if "local_results" in data:
237
155
  for place in data["local_results"]:
238
156
  if "place_id" in place:
239
- place["google_maps_url"] = (
240
- f"https://www.google.com/maps/place/?q=place_id:{place['place_id']}"
241
- )
242
-
157
+ place["google_maps_url"] = f"https://www.google.com/maps/place/?q=place_id:{place['place_id']}"
243
158
  return data
244
159
 
245
- async def get_google_maps_reviews(
246
- self,
247
- data_id: str,
248
- hl: str | None = None,
249
- ) -> dict[str, Any]:
160
+ async def get_google_maps_reviews(self, data_id: str, hl: str | None = None) -> dict[str, Any]:
250
161
  """
251
162
  Fetches Google Maps reviews for a specific location via SerpApi using its unique `data_id`. This function uses the `google_maps_reviews` engine, unlike `google_maps_search` which finds locations. Results can be returned in a specified language, defaulting to English.
252
163
 
@@ -264,28 +175,14 @@ class SerpapiApp(APIApplication):
264
175
  Tags:
265
176
  google-maps, reviews, ratings, places, important
266
177
  """
267
-
268
178
  query_params = {}
269
- query_params = {
270
- "engine": "google_maps_reviews",
271
- "data_id": data_id,
272
- "api_key": self.serpapi_api_key,
273
- }
274
-
179
+ query_params = {"engine": "google_maps_reviews", "data_id": data_id, "api_key": self.serpapi_api_key}
275
180
  if hl is not None:
276
181
  query_params["hl"] = hl
277
182
  else:
278
183
  query_params["hl"] = "en"
279
-
280
- response = self._get(
281
- self.base_url,
282
- params=query_params,
283
- )
184
+ response = await self._aget(self.base_url, params=query_params)
284
185
  return self._handle_response(response)
285
186
 
286
187
  def list_tools(self) -> list[callable]:
287
- return [
288
- self.web_search,
289
- self.google_maps_search,
290
- self.get_google_maps_reviews,
291
- ]
188
+ return [self.web_search, self.google_maps_search, self.get_google_maps_reviews]
@@ -1,17 +1,19 @@
1
- # SharepointApp MCP Server
1
+ # SharePoint Application
2
2
 
3
- An MCP Server for the SharepointApp API.
3
+ This application provides tools for interacting with the Microsoft SharePoint API via Microsoft Graph. It allows you to manage files, folders, and retrieve information about your SharePoint drive.
4
4
 
5
- ## 🛠️ Tool List
5
+ ## Available Tools
6
6
 
7
- This is automatically generated from OpenAPI schema for the SharepointApp API.
8
-
9
-
10
- | Tool | Description |
11
- |------|-------------|
12
- | `list_folders` | Retrieves a list of immediate subfolder names within a specified SharePoint directory. If no path is provided, it defaults to the root drive. This function is distinct from `list_files`, as it exclusively lists directories, not files. |
13
- | `create_folder_and_list` | Creates a new folder with a given name inside a specified parent directory (or the root). It then returns an updated list of all folder names within that same directory, effectively confirming that the creation operation was successful. |
14
- | `list_files` | Retrieves metadata for all files in a specified folder. For each file, it returns key details like name, URL, size, and timestamps. This function exclusively lists file properties, distinguishing it from `list_folders` (which lists directories) and `get_document_content` (which retrieves file content). |
15
- | `upload_text_file` | Uploads string content to create a new file in a specified SharePoint folder. To confirm the operation, it returns an updated list of all files and their metadata from that directory, including the newly created file. |
16
- | `get_document_content` | Retrieves a file's content from a specified SharePoint path. It returns a dictionary containing the file's name and size, decoding text files as a string and Base64-encoding binary files. Unlike `list_files`, which only fetches metadata, this function provides the actual file content. |
17
- | `delete_document` | Permanently deletes a specified file from a SharePoint drive using its full path. This is the sole destructive file operation, contrasting with functions that read or create files. It returns `True` on successful deletion and raises an exception on failure, such as if the file is not found. |
7
+ - `get_my_profile`: Fetches the profile for the currently authenticated user.
8
+ - `get_drive_info`: Fetches high-level information about the user's SharePoint drive.
9
+ - `search_files`: Searches for files and folders in the user's SharePoint.
10
+ - `get_item_metadata`: Fetches metadata for a specific file or folder.
11
+ - `create_folder`: Creates a new folder.
12
+ - `delete_item`: Deletes a file or folder.
13
+ - `download_file`: Retrieves a download URL for a file.
14
+ - `upload_file`: Uploads a local file.
15
+ - `list_folders`: Lists all folders in a specified directory.
16
+ - `list_files`: Lists all files in a specified directory.
17
+ - `create_folder_and_list`: Creates a folder and then lists the contents of the parent directory.
18
+ - `upload_text_file`: Uploads content from a string to a new text file.
19
+ - `get_document_content`: Retrieves the content of a file.