universal-mcp-applications 0.1.33__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 (113) 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 +86 -24
  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 +98 -202
  41. universal_mcp/applications/google_drive/app.py +194 -793
  42. universal_mcp/applications/google_gemini/app.py +27 -62
  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 +171 -624
  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 +5 -0
  56. universal_mcp/applications/linkedin/app.py +332 -227
  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/app.py +26 -48
  64. universal_mcp/applications/openai/app.py +42 -165
  65. universal_mcp/applications/outlook/README.md +22 -9
  66. universal_mcp/applications/outlook/app.py +403 -141
  67. universal_mcp/applications/perplexity/README.md +2 -1
  68. universal_mcp/applications/perplexity/app.py +162 -20
  69. universal_mcp/applications/pipedrive/app.py +1021 -3331
  70. universal_mcp/applications/posthog/app.py +272 -541
  71. universal_mcp/applications/reddit/app.py +61 -160
  72. universal_mcp/applications/resend/app.py +41 -107
  73. universal_mcp/applications/retell/app.py +23 -50
  74. universal_mcp/applications/rocketlane/app.py +250 -963
  75. universal_mcp/applications/scraper/app.py +67 -125
  76. universal_mcp/applications/semanticscholar/app.py +36 -78
  77. universal_mcp/applications/semrush/app.py +43 -77
  78. universal_mcp/applications/sendgrid/app.py +826 -1576
  79. universal_mcp/applications/sentry/app.py +444 -1079
  80. universal_mcp/applications/serpapi/app.py +40 -143
  81. universal_mcp/applications/sharepoint/app.py +27 -49
  82. universal_mcp/applications/shopify/app.py +1743 -4479
  83. universal_mcp/applications/shortcut/app.py +272 -534
  84. universal_mcp/applications/slack/app.py +41 -123
  85. universal_mcp/applications/spotify/app.py +206 -405
  86. universal_mcp/applications/supabase/app.py +174 -283
  87. universal_mcp/applications/tavily/app.py +2 -2
  88. universal_mcp/applications/trello/app.py +853 -2816
  89. universal_mcp/applications/twilio/app.py +14 -50
  90. universal_mcp/applications/twitter/api_segments/compliance_api.py +4 -14
  91. universal_mcp/applications/twitter/api_segments/dm_conversations_api.py +6 -18
  92. universal_mcp/applications/twitter/api_segments/likes_api.py +1 -3
  93. universal_mcp/applications/twitter/api_segments/lists_api.py +5 -15
  94. universal_mcp/applications/twitter/api_segments/trends_api.py +1 -3
  95. universal_mcp/applications/twitter/api_segments/tweets_api.py +9 -31
  96. universal_mcp/applications/twitter/api_segments/usage_api.py +1 -5
  97. universal_mcp/applications/twitter/api_segments/users_api.py +14 -42
  98. universal_mcp/applications/whatsapp/app.py +35 -186
  99. universal_mcp/applications/whatsapp/audio.py +2 -6
  100. universal_mcp/applications/whatsapp/whatsapp.py +17 -51
  101. universal_mcp/applications/whatsapp_business/app.py +86 -299
  102. universal_mcp/applications/wrike/app.py +80 -153
  103. universal_mcp/applications/yahoo_finance/app.py +19 -65
  104. universal_mcp/applications/youtube/app.py +120 -306
  105. universal_mcp/applications/zenquotes/app.py +3 -3
  106. {universal_mcp_applications-0.1.33.dist-info → universal_mcp_applications-0.1.39rc8.dist-info}/METADATA +4 -2
  107. {universal_mcp_applications-0.1.33.dist-info → universal_mcp_applications-0.1.39rc8.dist-info}/RECORD +109 -113
  108. {universal_mcp_applications-0.1.33.dist-info → universal_mcp_applications-0.1.39rc8.dist-info}/WHEEL +1 -1
  109. universal_mcp/applications/hubspot/api_segments/__init__.py +0 -0
  110. universal_mcp/applications/hubspot/api_segments/api_segment_base.py +0 -54
  111. universal_mcp/applications/hubspot/api_segments/crm_api.py +0 -7337
  112. universal_mcp/applications/hubspot/api_segments/marketing_api.py +0 -1467
  113. {universal_mcp_applications-0.1.33.dist-info → universal_mcp_applications-0.1.39rc8.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,5 @@
1
1
  from collections.abc import Callable
2
2
  from typing import Any
3
-
4
3
  from loguru import logger
5
4
  from universal_mcp.applications.application import GraphQLApplication
6
5
  from universal_mcp.exceptions import NotAuthorizedError
@@ -8,24 +7,14 @@ from universal_mcp.integrations import Integration
8
7
 
9
8
 
10
9
  class ContentfulApp(GraphQLApplication):
11
- def __init__(
12
- self,
13
- integration: Integration | None = None,
14
- **kwargs: Any,
15
- ) -> None:
10
+ def __init__(self, integration: Integration | None = None, **kwargs: Any) -> None:
16
11
  self.space_id: str | None = None
17
12
  self.environment_id: str = "master"
18
13
  self._access_token: str | None = None
19
14
  self._is_eu_customer: bool = False
20
15
  self._credentials_loaded: bool = False
21
16
  default_base_url = "https://graphql.contentful.com"
22
-
23
- super().__init__(
24
- name="contentful",
25
- base_url=default_base_url,
26
- integration=integration,
27
- **kwargs,
28
- )
17
+ super().__init__(name="contentful", base_url=default_base_url, integration=integration, **kwargs)
29
18
 
30
19
  def _load_credentials_and_construct_url(self) -> bool:
31
20
  """
@@ -37,70 +26,39 @@ class ContentfulApp(GraphQLApplication):
37
26
  """
38
27
  if self._credentials_loaded:
39
28
  return True
40
-
41
29
  logger.debug("Attempting to load Contentful credentials and construct URL...")
42
-
43
30
  if not self.integration:
44
- logger.error(
45
- "Contentful integration not configured. Cannot load credentials or construct URL."
46
- )
47
- # Mark as 'loaded' to prevent retries, even though it failed.
31
+ logger.error("Contentful integration not configured. Cannot load credentials or construct URL.")
48
32
  self._credentials_loaded = True
49
33
  return False
50
-
51
34
  try:
52
35
  credentials = self.integration.get_credentials()
53
36
  except NotAuthorizedError as e:
54
- logger.error(
55
- f"Authorization required or credentials unavailable for Contentful: {e.message}"
56
- )
57
- self._credentials_loaded = True # Prevent retries
37
+ logger.error(f"Authorization required or credentials unavailable for Contentful: {e.message}")
38
+ self._credentials_loaded = True
58
39
  return False
59
40
  except Exception as e:
60
- logger.error(
61
- f"Failed to get credentials from integration: {e}", exc_info=True
62
- )
63
- self._credentials_loaded = True # Prevent retries
41
+ logger.error(f"Failed to get credentials from integration: {e}", exc_info=True)
42
+ self._credentials_loaded = True
64
43
  return False
65
-
66
44
  self.space_id = credentials.get("space_id")
67
- self._access_token = credentials.get("access_token") or credentials.get(
68
- "api_key"
69
- )
70
- self.environment_id = credentials.get(
71
- "environment_id", "master"
72
- ) # Use default if not specified
73
- self._is_eu_customer = credentials.get(
74
- "is_eu_customer", False
75
- ) # Use default if not specified
76
-
45
+ self._access_token = credentials.get("access_token") or credentials.get("api_key")
46
+ self.environment_id = credentials.get("environment_id", "master")
47
+ self._is_eu_customer = credentials.get("is_eu_customer", False)
77
48
  missing_creds = []
78
49
  if not self.space_id:
79
50
  missing_creds.append("'space_id'")
80
51
  if not self._access_token:
81
52
  missing_creds.append("'access_token' or 'api_key'")
82
-
83
53
  if missing_creds:
84
- logger.error(
85
- f"Missing required Contentful credentials in integration: {', '.join(missing_creds)}. "
86
- "API calls will fail."
87
- )
88
- self._credentials_loaded = True # Prevent retries
54
+ logger.error(f"Missing required Contentful credentials in integration: {', '.join(missing_creds)}. API calls will fail.")
55
+ self._credentials_loaded = True
89
56
  return False
90
-
91
- contentful_api_domain = (
92
- "graphql.eu.contentful.com"
93
- if self._is_eu_customer
94
- else "graphql.contentful.com"
95
- )
57
+ contentful_api_domain = "graphql.eu.contentful.com" if self._is_eu_customer else "graphql.contentful.com"
96
58
  self.base_url = f"https://{contentful_api_domain}/content/v1/spaces/{self.space_id}/environments/{self.environment_id}"
97
-
98
59
  self._client = None
99
-
100
60
  logger.info(
101
- f"Contentful credentials loaded and URL constructed successfully. "
102
- f"Space: '{self.space_id}', Environment: '{self.environment_id}'. "
103
- f"Base URL: {self.base_url}"
61
+ f"Contentful credentials loaded and URL constructed successfully. Space: '{self.space_id}', Environment: '{self.environment_id}'. Base URL: {self.base_url}"
104
62
  )
105
63
  self._credentials_loaded = True
106
64
  return True
@@ -114,7 +72,7 @@ class ContentfulApp(GraphQLApplication):
114
72
  return ""
115
73
  if len(parts) == 1 and parts[0] == s and s:
116
74
  return s[0].lower() + s[1:] if len(s) > 1 else s.lower()
117
- return parts[0].lower() + "".join(word.capitalize() for word in parts[1:])
75
+ return parts[0].lower() + "".join((word.capitalize() for word in parts[1:]))
118
76
 
119
77
  @staticmethod
120
78
  def _to_pascal_case(s: str) -> str:
@@ -125,57 +83,36 @@ class ContentfulApp(GraphQLApplication):
125
83
  return ""
126
84
  if len(parts) == 1 and parts[0] == s and s:
127
85
  return s[0].upper() + s[1:] if len(s) > 1 else s.upper()
128
- return "".join(word.capitalize() for word in parts)
86
+ return "".join((word.capitalize() for word in parts))
129
87
 
130
88
  def _ensure_loaded(self) -> bool:
131
89
  """Internal helper to trigger lazy loading and check status."""
132
90
  if not self._credentials_loaded:
133
91
  return self._load_credentials_and_construct_url()
134
- # If already marked as loaded, check if essential parts are actually set (robustness check)
135
92
  return bool(self.base_url and self.space_id and self._access_token)
136
93
 
137
- # --- Tool Methods ---
138
-
139
- def get_entry(
140
- self,
141
- content_type_id: str,
142
- entry_id: str,
143
- fields_to_select: str,
144
- locale: str | None = None,
145
- preview: bool = False,
94
+ async def get_entry(
95
+ self, content_type_id: str, entry_id: str, fields_to_select: str, locale: str | None = None, preview: bool = False
146
96
  ) -> dict[str, Any]:
147
97
  """
148
98
  Fetches a single entry of a specified content type by its ID.
149
99
  (See original docstring for details)
150
100
  """
151
101
  if not self._ensure_loaded():
152
- return {
153
- "error": "Failed to initialize ContentfulApp. Check credentials and configuration."
154
- }
155
-
102
+ return {"error": "Failed to initialize ContentfulApp. Check credentials and configuration."}
156
103
  query_field = self._to_camel_case(content_type_id)
157
- logger.debug(
158
- f"Fetching entry for content_type_id='{content_type_id}' (query field='{query_field}'), entry_id='{entry_id}'"
159
- )
160
- query_gql = f"""
161
- query GetEntryById($id: String!, $locale: String, $preview: Boolean) {{
162
- {query_field}(id: $id, locale: $locale, preview: $preview) {{
163
- {fields_to_select}
164
- }}
165
- }}
166
- """
104
+ logger.debug(f"Fetching entry for content_type_id='{content_type_id}' (query field='{query_field}'), entry_id='{entry_id}'")
105
+ query_gql = f"\n query GetEntryById($id: String!, $locale: String, $preview: Boolean) {{\n {query_field}(id: $id, locale: $locale, preview: $preview) {{\n {fields_to_select}\n }}\n }}\n "
167
106
  variables: dict[str, Any] = {"id": entry_id, "preview": preview}
168
107
  if locale:
169
108
  variables["locale"] = locale
170
-
171
- # Call the base class query method which uses the configured client
172
109
  try:
173
110
  return self.query(query_gql, variables=variables)
174
111
  except Exception as e:
175
112
  logger.error(f"Error executing get_entry query: {e}", exc_info=True)
176
113
  return {"error": f"Failed to get entry: {e}"}
177
114
 
178
- def get_entries_collection(
115
+ async def get_entries_collection(
179
116
  self,
180
117
  content_type_id: str,
181
118
  fields_to_select_for_item: str,
@@ -191,28 +128,14 @@ class ContentfulApp(GraphQLApplication):
191
128
  (See original docstring for details)
192
129
  """
193
130
  if not self._ensure_loaded():
194
- return {
195
- "error": "Failed to initialize ContentfulApp. Check credentials and configuration."
196
- }
197
-
131
+ return {"error": "Failed to initialize ContentfulApp. Check credentials and configuration."}
198
132
  collection_field = self._to_camel_case(content_type_id) + "Collection"
199
133
  filter_type = self._to_pascal_case(content_type_id) + "Filter"
200
134
  order_enum_type = self._to_pascal_case(content_type_id) + "Order"
201
135
  logger.debug(
202
- f"Fetching collection for content_type_id='{content_type_id}' "
203
- f"(collection field='{collection_field}', filter type='{filter_type}', order enum='{order_enum_type}')"
136
+ f"Fetching collection for content_type_id='{content_type_id}' (collection field='{collection_field}', filter type='{filter_type}', order enum='{order_enum_type}')"
204
137
  )
205
- query_gql = f"""
206
- query GetEntries(
207
- $limit: Int, $skip: Int, $where: {filter_type}, $order: [{order_enum_type}!], $locale: String, $preview: Boolean
208
- ) {{
209
- {collection_field}(
210
- limit: $limit, skip: $skip, where: $where, order: $order, locale: $locale, preview: $preview
211
- ) {{
212
- total skip limit items {{ {fields_to_select_for_item} }}
213
- }}
214
- }}
215
- """
138
+ query_gql = f"\n query GetEntries(\n $limit: Int, $skip: Int, $where: {filter_type}, $order: [{order_enum_type}!], $locale: String, $preview: Boolean\n ) {{\n {collection_field}(\n limit: $limit, skip: $skip, where: $where, order: $order, locale: $locale, preview: $preview\n ) {{\n total skip limit items {{ {fields_to_select_for_item} }}\n }}\n }}\n "
216
139
  variables: dict[str, Any] = {"preview": preview}
217
140
  if limit is not None:
218
141
  variables["limit"] = limit
@@ -224,16 +147,13 @@ class ContentfulApp(GraphQLApplication):
224
147
  variables["order"] = order
225
148
  if locale:
226
149
  variables["locale"] = locale
227
-
228
150
  try:
229
151
  return self.query(query_gql, variables=variables)
230
152
  except Exception as e:
231
- logger.error(
232
- f"Error executing get_entries_collection query: {e}", exc_info=True
233
- )
153
+ logger.error(f"Error executing get_entries_collection query: {e}", exc_info=True)
234
154
  return {"error": f"Failed to get entries collection: {e}"}
235
155
 
236
- def get_asset(
156
+ async def get_asset(
237
157
  self,
238
158
  asset_id: str,
239
159
  fields_to_select: str = "sys { id } url title description fileName contentType size width height",
@@ -244,25 +164,17 @@ class ContentfulApp(GraphQLApplication):
244
164
  (See original docstring for details)
245
165
  """
246
166
  if not self._ensure_loaded():
247
- return {
248
- "error": "Failed to initialize ContentfulApp. Check credentials and configuration."
249
- }
250
-
167
+ return {"error": "Failed to initialize ContentfulApp. Check credentials and configuration."}
251
168
  logger.debug(f"Fetching asset_id='{asset_id}'")
252
- query_gql = f"""
253
- query GetAssetById($id: String!, $preview: Boolean) {{
254
- asset(id: $id, preview: $preview) {{ {fields_to_select} }}
255
- }}
256
- """
169
+ query_gql = f"\n query GetAssetById($id: String!, $preview: Boolean) {{\n asset(id: $id, preview: $preview) {{ {fields_to_select} }}\n }}\n "
257
170
  variables: dict[str, Any] = {"id": asset_id, "preview": preview}
258
-
259
171
  try:
260
172
  return self.query(query_gql, variables=variables)
261
173
  except Exception as e:
262
174
  logger.error(f"Error executing get_asset query: {e}", exc_info=True)
263
175
  return {"error": f"Failed to get asset: {e}"}
264
176
 
265
- def get_assets_collection(
177
+ async def get_assets_collection(
266
178
  self,
267
179
  fields_to_select_for_item: str = "sys { id } url title description fileName contentType size width height",
268
180
  limit: int | None = None,
@@ -277,22 +189,9 @@ class ContentfulApp(GraphQLApplication):
277
189
  (See original docstring for details)
278
190
  """
279
191
  if not self._ensure_loaded():
280
- return {
281
- "error": "Failed to initialize ContentfulApp. Check credentials and configuration."
282
- }
283
-
192
+ return {"error": "Failed to initialize ContentfulApp. Check credentials and configuration."}
284
193
  logger.debug("Fetching assets collection")
285
- query_gql = f"""
286
- query GetAssets(
287
- $limit: Int, $skip: Int, $where: AssetFilter, $order: [AssetOrder!], $locale: String, $preview: Boolean
288
- ) {{
289
- assetCollection(
290
- limit: $limit, skip: $skip, where: $where, order: $order, locale: $locale, preview: $preview
291
- ) {{
292
- total skip limit items {{ {fields_to_select_for_item} }}
293
- }}
294
- }}
295
- """
194
+ query_gql = f"\n query GetAssets(\n $limit: Int, $skip: Int, $where: AssetFilter, $order: [AssetOrder!], $locale: String, $preview: Boolean\n ) {{\n assetCollection(\n limit: $limit, skip: $skip, where: $where, order: $order, locale: $locale, preview: $preview\n ) {{\n total skip limit items {{ {fields_to_select_for_item} }}\n }}\n }}\n "
296
195
  variables: dict[str, Any] = {"preview": preview}
297
196
  if limit is not None:
298
197
  variables["limit"] = limit
@@ -304,18 +203,13 @@ class ContentfulApp(GraphQLApplication):
304
203
  variables["order"] = order
305
204
  if locale:
306
205
  variables["locale"] = locale
307
-
308
206
  try:
309
207
  return self.query(query_gql, variables=variables)
310
208
  except Exception as e:
311
- logger.error(
312
- f"Error executing get_assets_collection query: {e}", exc_info=True
313
- )
209
+ logger.error(f"Error executing get_assets_collection query: {e}", exc_info=True)
314
210
  return {"error": f"Failed to get assets collection: {e}"}
315
211
 
316
- def execute_graphql_query(
317
- self, query_string: str, variables: dict[str, Any] | None = None
318
- ) -> dict[str, Any]:
212
+ async def execute_graphql_query(self, query_string: str, variables: dict[str, Any] | None = None) -> dict[str, Any]:
319
213
  """
320
214
  Executes an arbitrary GraphQL query against the configured Contentful space/environment.
321
215
 
@@ -330,10 +224,7 @@ class ContentfulApp(GraphQLApplication):
330
224
  important
331
225
  """
332
226
  if not self._ensure_loaded():
333
- return {
334
- "error": "Failed to initialize ContentfulApp. Check credentials and configuration."
335
- }
336
-
227
+ return {"error": "Failed to initialize ContentfulApp. Check credentials and configuration."}
337
228
  logger.debug(f"Executing custom GraphQL query with variables: {variables}")
338
229
  try:
339
230
  return self.query(query_string, variables=variables)
@@ -343,10 +234,4 @@ class ContentfulApp(GraphQLApplication):
343
234
 
344
235
  def list_tools(self) -> list[Callable]:
345
236
  """Returns a list of methods exposed as tools."""
346
- return [
347
- self.get_entry,
348
- self.get_entries_collection,
349
- self.get_asset,
350
- self.get_assets_collection,
351
- self.execute_graphql_query,
352
- ]
237
+ return [self.get_entry, self.get_entries_collection, self.get_asset, self.get_assets_collection, self.execute_graphql_query]