universal-mcp-applications 0.1.33__py3-none-any.whl → 0.1.39rc16__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 (119) hide show
  1. universal_mcp/applications/BEST_PRACTICES.md +1 -1
  2. universal_mcp/applications/ahrefs/app.py +92 -238
  3. universal_mcp/applications/airtable/app.py +36 -135
  4. universal_mcp/applications/apollo/app.py +124 -477
  5. universal_mcp/applications/asana/app.py +605 -1755
  6. universal_mcp/applications/aws_s3/app.py +63 -119
  7. universal_mcp/applications/bill/app.py +644 -2055
  8. universal_mcp/applications/box/app.py +1246 -4159
  9. universal_mcp/applications/braze/app.py +410 -1476
  10. universal_mcp/applications/browser_use/README.md +15 -1
  11. universal_mcp/applications/browser_use/__init__.py +1 -0
  12. universal_mcp/applications/browser_use/app.py +91 -26
  13. universal_mcp/applications/cal_com_v2/app.py +207 -625
  14. universal_mcp/applications/calendly/app.py +103 -242
  15. universal_mcp/applications/canva/app.py +75 -140
  16. universal_mcp/applications/clickup/app.py +331 -798
  17. universal_mcp/applications/coda/app.py +240 -520
  18. universal_mcp/applications/confluence/app.py +497 -1285
  19. universal_mcp/applications/contentful/app.py +40 -155
  20. universal_mcp/applications/crustdata/app.py +44 -123
  21. universal_mcp/applications/dialpad/app.py +451 -924
  22. universal_mcp/applications/digitalocean/app.py +2071 -6082
  23. universal_mcp/applications/domain_checker/app.py +3 -54
  24. universal_mcp/applications/e2b/app.py +17 -68
  25. universal_mcp/applications/elevenlabs/README.md +27 -3
  26. universal_mcp/applications/elevenlabs/app.py +741 -74
  27. universal_mcp/applications/exa/README.md +8 -4
  28. universal_mcp/applications/exa/app.py +415 -186
  29. universal_mcp/applications/falai/README.md +5 -7
  30. universal_mcp/applications/falai/app.py +156 -232
  31. universal_mcp/applications/figma/app.py +91 -175
  32. universal_mcp/applications/file_system/app.py +2 -13
  33. universal_mcp/applications/firecrawl/app.py +198 -176
  34. universal_mcp/applications/fireflies/app.py +59 -281
  35. universal_mcp/applications/fpl/app.py +92 -529
  36. universal_mcp/applications/fpl/utils/fixtures.py +15 -49
  37. universal_mcp/applications/fpl/utils/helper.py +25 -89
  38. universal_mcp/applications/fpl/utils/league_utils.py +20 -64
  39. universal_mcp/applications/ghost_content/app.py +70 -179
  40. universal_mcp/applications/github/app.py +30 -67
  41. universal_mcp/applications/gong/app.py +142 -302
  42. universal_mcp/applications/google_calendar/app.py +26 -78
  43. universal_mcp/applications/google_docs/README.md +15 -14
  44. universal_mcp/applications/google_docs/app.py +103 -206
  45. universal_mcp/applications/google_drive/app.py +194 -793
  46. universal_mcp/applications/google_gemini/app.py +68 -59
  47. universal_mcp/applications/google_mail/README.md +1 -0
  48. universal_mcp/applications/google_mail/app.py +93 -214
  49. universal_mcp/applications/google_searchconsole/app.py +25 -58
  50. universal_mcp/applications/google_sheet/README.md +2 -1
  51. universal_mcp/applications/google_sheet/app.py +226 -624
  52. universal_mcp/applications/google_sheet/helper.py +26 -53
  53. universal_mcp/applications/hashnode/app.py +57 -269
  54. universal_mcp/applications/heygen/README.md +10 -32
  55. universal_mcp/applications/heygen/app.py +339 -811
  56. universal_mcp/applications/http_tools/app.py +10 -32
  57. universal_mcp/applications/hubspot/README.md +1 -1
  58. universal_mcp/applications/hubspot/app.py +7508 -99
  59. universal_mcp/applications/jira/app.py +2419 -8334
  60. universal_mcp/applications/klaviyo/app.py +739 -1621
  61. universal_mcp/applications/linkedin/README.md +18 -1
  62. universal_mcp/applications/linkedin/app.py +729 -251
  63. universal_mcp/applications/mailchimp/app.py +696 -1851
  64. universal_mcp/applications/markitdown/app.py +8 -20
  65. universal_mcp/applications/miro/app.py +333 -815
  66. universal_mcp/applications/ms_teams/app.py +420 -1407
  67. universal_mcp/applications/neon/app.py +144 -250
  68. universal_mcp/applications/notion/app.py +38 -53
  69. universal_mcp/applications/onedrive/app.py +26 -48
  70. universal_mcp/applications/openai/app.py +43 -166
  71. universal_mcp/applications/outlook/README.md +22 -9
  72. universal_mcp/applications/outlook/app.py +403 -141
  73. universal_mcp/applications/perplexity/README.md +2 -1
  74. universal_mcp/applications/perplexity/app.py +161 -20
  75. universal_mcp/applications/pipedrive/app.py +1021 -3331
  76. universal_mcp/applications/posthog/app.py +272 -541
  77. universal_mcp/applications/reddit/app.py +65 -164
  78. universal_mcp/applications/resend/app.py +72 -139
  79. universal_mcp/applications/retell/app.py +23 -50
  80. universal_mcp/applications/rocketlane/app.py +252 -965
  81. universal_mcp/applications/scraper/app.py +114 -142
  82. universal_mcp/applications/semanticscholar/app.py +36 -78
  83. universal_mcp/applications/semrush/app.py +44 -78
  84. universal_mcp/applications/sendgrid/app.py +826 -1576
  85. universal_mcp/applications/sentry/app.py +444 -1079
  86. universal_mcp/applications/serpapi/app.py +44 -146
  87. universal_mcp/applications/sharepoint/app.py +27 -49
  88. universal_mcp/applications/shopify/app.py +1748 -4486
  89. universal_mcp/applications/shortcut/app.py +275 -536
  90. universal_mcp/applications/slack/app.py +43 -125
  91. universal_mcp/applications/spotify/app.py +206 -405
  92. universal_mcp/applications/supabase/app.py +174 -283
  93. universal_mcp/applications/tavily/app.py +2 -2
  94. universal_mcp/applications/trello/app.py +853 -2816
  95. universal_mcp/applications/twilio/app.py +27 -62
  96. universal_mcp/applications/twitter/api_segments/compliance_api.py +4 -14
  97. universal_mcp/applications/twitter/api_segments/dm_conversations_api.py +6 -18
  98. universal_mcp/applications/twitter/api_segments/likes_api.py +1 -3
  99. universal_mcp/applications/twitter/api_segments/lists_api.py +5 -15
  100. universal_mcp/applications/twitter/api_segments/trends_api.py +1 -3
  101. universal_mcp/applications/twitter/api_segments/tweets_api.py +9 -31
  102. universal_mcp/applications/twitter/api_segments/usage_api.py +1 -5
  103. universal_mcp/applications/twitter/api_segments/users_api.py +14 -42
  104. universal_mcp/applications/whatsapp/app.py +35 -186
  105. universal_mcp/applications/whatsapp/audio.py +2 -6
  106. universal_mcp/applications/whatsapp/whatsapp.py +17 -51
  107. universal_mcp/applications/whatsapp_business/app.py +86 -299
  108. universal_mcp/applications/wrike/app.py +80 -153
  109. universal_mcp/applications/yahoo_finance/app.py +19 -65
  110. universal_mcp/applications/youtube/app.py +120 -306
  111. universal_mcp/applications/zenquotes/app.py +3 -3
  112. {universal_mcp_applications-0.1.33.dist-info → universal_mcp_applications-0.1.39rc16.dist-info}/METADATA +4 -2
  113. {universal_mcp_applications-0.1.33.dist-info → universal_mcp_applications-0.1.39rc16.dist-info}/RECORD +115 -119
  114. {universal_mcp_applications-0.1.33.dist-info → universal_mcp_applications-0.1.39rc16.dist-info}/WHEEL +1 -1
  115. universal_mcp/applications/hubspot/api_segments/__init__.py +0 -0
  116. universal_mcp/applications/hubspot/api_segments/api_segment_base.py +0 -54
  117. universal_mcp/applications/hubspot/api_segments/crm_api.py +0 -7337
  118. universal_mcp/applications/hubspot/api_segments/marketing_api.py +0 -1467
  119. {universal_mcp_applications-0.1.33.dist-info → universal_mcp_applications-0.1.39rc16.dist-info}/licenses/LICENSE +0 -0
@@ -1,5 +1,4 @@
1
1
  from typing import Any
2
-
3
2
  from loguru import logger
4
3
  from universal_mcp.applications.application import APIApplication
5
4
  from universal_mcp.integrations import Integration
@@ -11,18 +10,15 @@ class GithubApp(APIApplication):
11
10
  self.base_api_url = "https://api.github.com/repos"
12
11
  self.base_url = "https://api.github.com"
13
12
 
14
- def _get_headers(self):
13
+ async def _aget_headers(self):
15
14
  if not self.integration:
16
15
  raise ValueError("Integration not configured")
17
- credentials = self.integration.get_credentials()
16
+ credentials = await self.integration.get_credentials_async()
18
17
  if "headers" in credentials:
19
18
  return credentials["headers"]
20
- return {
21
- "Authorization": f"Bearer {credentials['access_token']}",
22
- "Accept": "application/vnd.github.v3+json",
23
- }
19
+ return {"Authorization": f"Bearer {credentials['access_token']}", "Accept": "application/vnd.github.v3+json"}
24
20
 
25
- def star_repository(self, repo_full_name: str) -> str:
21
+ async def star_repository(self, repo_full_name: str) -> str:
26
22
  """
27
23
  Stars a GitHub repository for the authenticated user. This user-centric action takes the full repository name ('owner/repo') and returns a simple string message confirming the outcome, unlike other functions that list or create repository content like issues or pull requests.
28
24
 
@@ -40,7 +36,7 @@ class GithubApp(APIApplication):
40
36
  star, github, api, action, social, repository, important
41
37
  """
42
38
  url = f"https://api.github.com/user/starred/{repo_full_name}"
43
- response = self._put(url, data={})
39
+ response = await self._aput(url, data={})
44
40
  if response.status_code == 204:
45
41
  return f"Successfully starred repository {repo_full_name}"
46
42
  elif response.status_code == 404:
@@ -49,7 +45,7 @@ class GithubApp(APIApplication):
49
45
  logger.error(response.text)
50
46
  return f"Error starring repository: {response.text}"
51
47
 
52
- def list_recent_commits(self, repo_full_name: str) -> str:
48
+ async def list_recent_commits(self, repo_full_name: str) -> str:
53
49
  """
54
50
  Fetches and formats the 12 most recent commits from a repository. It returns a human-readable string summarizing each commit's hash, author, and message, providing a focused overview of recent code changes, unlike functions that list branches, issues, or pull requests.
55
51
 
@@ -68,21 +64,20 @@ class GithubApp(APIApplication):
68
64
  """
69
65
  repo_full_name = repo_full_name.strip()
70
66
  url = f"{self.base_api_url}/{repo_full_name}/commits"
71
- response = self._get(url)
67
+ response = await self._aget(url)
72
68
  response.raise_for_status()
73
69
  commits = response.json()
74
70
  if not commits:
75
71
  return f"No commits found for repository {repo_full_name}"
76
72
  result = f"Recent commits for {repo_full_name}:\n\n"
77
- for commit in commits[:12]: # Limit to 12 commits
73
+ for commit in commits[:12]:
78
74
  sha = commit.get("sha", "")[:7]
79
75
  message = commit.get("commit", {}).get("message", "").split("\n")[0]
80
76
  author = commit.get("commit", {}).get("author", {}).get("name", "Unknown")
81
-
82
77
  result += f"- {sha}: {message} (by {author})\n"
83
78
  return result
84
79
 
85
- def list_branches(self, repo_full_name: str) -> str:
80
+ async def list_branches(self, repo_full_name: str) -> str:
86
81
  """
87
82
  Fetches all branches for a specified GitHub repository and formats them into a human-readable string. This method is distinct from others like `search_issues`, as it returns a formatted list for display rather than raw JSON data for programmatic use.
88
83
 
@@ -101,7 +96,7 @@ class GithubApp(APIApplication):
101
96
  """
102
97
  repo_full_name = repo_full_name.strip()
103
98
  url = f"{self.base_api_url}/{repo_full_name}/branches"
104
- response = self._get(url)
99
+ response = await self._aget(url)
105
100
  response.raise_for_status()
106
101
  branches = response.json()
107
102
  if not branches:
@@ -112,7 +107,7 @@ class GithubApp(APIApplication):
112
107
  result += f"- {branch_name}\n"
113
108
  return result
114
109
 
115
- def list_pull_requests(self, repo_full_name: str, state: str = "open") -> str:
110
+ async def list_pull_requests(self, repo_full_name: str, state: str = "open") -> str:
116
111
  """
117
112
  Fetches pull requests for a repository, filtered by state (e.g., 'open'). It returns a formatted string summarizing each PR's details, distinguishing it from `get_pull_request` (single PR) and `search_issues` (raw issue data).
118
113
 
@@ -132,7 +127,7 @@ class GithubApp(APIApplication):
132
127
  repo_full_name = repo_full_name.strip()
133
128
  url = f"{self.base_api_url}/{repo_full_name}/pulls"
134
129
  params = {"state": state}
135
- response = self._get(url, params=params)
130
+ response = await self._aget(url, params=params)
136
131
  response.raise_for_status()
137
132
  pull_requests = response.json()
138
133
  if not pull_requests:
@@ -143,20 +138,11 @@ class GithubApp(APIApplication):
143
138
  pr_number = pr.get("number", "Unknown")
144
139
  pr_state = pr.get("state", "Unknown")
145
140
  pr_user = pr.get("user", {}).get("login", "Unknown")
146
-
147
- result += (
148
- f"- PR #{pr_number}: {pr_title} (by {pr_user}, Status: {pr_state})\n"
149
- )
141
+ result += f"- PR #{pr_number}: {pr_title} (by {pr_user}, Status: {pr_state})\n"
150
142
  return result
151
143
 
152
- def search_issues(
153
- self,
154
- repo_full_name: str,
155
- state: str = "open",
156
- assignee: str = None,
157
- labels: str = None,
158
- per_page: int = 30,
159
- page: int = 1,
144
+ async def search_issues(
145
+ self, repo_full_name: str, state: str = "open", assignee: str = None, labels: str = None, per_page: int = 30, page: int = 1
160
146
  ) -> list[dict[str, Any]]:
161
147
  """
162
148
  Fetches issues from a GitHub repository using specified filters (state, assignee, labels) and pagination. It returns the raw API response as a list of dictionaries, providing detailed issue data for programmatic processing, distinct from other methods that return formatted strings.
@@ -186,11 +172,11 @@ class GithubApp(APIApplication):
186
172
  params["assignee"] = assignee
187
173
  if labels:
188
174
  params["labels"] = labels
189
- response = self._get(url, params=params)
175
+ response = await self._aget(url, params=params)
190
176
  response.raise_for_status()
191
177
  return response.json()
192
178
 
193
- def get_pull_request(self, repo_full_name: str, pull_number: int) -> str:
179
+ async def get_pull_request(self, repo_full_name: str, pull_number: int) -> str:
194
180
  """
195
181
  Fetches a specific pull request from a repository using its unique number. It returns a human-readable string summarizing the PR's title, creator, status, and description, unlike `list_pull_requests` which retrieves a list of multiple PRs.
196
182
 
@@ -210,7 +196,7 @@ class GithubApp(APIApplication):
210
196
  """
211
197
  repo_full_name = repo_full_name.strip()
212
198
  url = f"{self.base_api_url}/{repo_full_name}/pulls/{pull_number}"
213
- response = self._get(url)
199
+ response = await self._aget(url)
214
200
  response.raise_for_status()
215
201
  pr = response.json()
216
202
  pr_title = pr.get("title", "No Title")
@@ -218,15 +204,10 @@ class GithubApp(APIApplication):
218
204
  pr_state = pr.get("state", "Unknown")
219
205
  pr_user = pr.get("user", {}).get("login", "Unknown")
220
206
  pr_body = pr.get("body", "No description provided.")
221
- result = (
222
- f"Pull Request #{pr_number}: {pr_title}\n"
223
- f"Created by: {pr_user}\n"
224
- f"Status: {pr_state}\n"
225
- f"Description: {pr_body}\n"
226
- )
207
+ result = f"Pull Request #{pr_number}: {pr_title}\nCreated by: {pr_user}\nStatus: {pr_state}\nDescription: {pr_body}\n"
227
208
  return result
228
209
 
229
- def create_pull_request(
210
+ async def create_pull_request(
230
211
  self,
231
212
  repo_full_name: str,
232
213
  head: str,
@@ -262,12 +243,7 @@ class GithubApp(APIApplication):
262
243
  """
263
244
  repo_full_name = repo_full_name.strip()
264
245
  url = f"{self.base_api_url}/{repo_full_name}/pulls"
265
- pull_request_data = {
266
- "head": head,
267
- "base": base,
268
- "maintainer_can_modify": maintainer_can_modify,
269
- "draft": draft,
270
- }
246
+ pull_request_data = {"head": head, "base": base, "maintainer_can_modify": maintainer_can_modify, "draft": draft}
271
247
  if issue is not None:
272
248
  pull_request_data["issue"] = issue
273
249
  else:
@@ -276,13 +252,11 @@ class GithubApp(APIApplication):
276
252
  pull_request_data["title"] = title
277
253
  if body is not None:
278
254
  pull_request_data["body"] = body
279
- response = self._post(url, pull_request_data)
255
+ response = await self._apost(url, pull_request_data)
280
256
  response.raise_for_status()
281
257
  return response.json()
282
258
 
283
- def create_issue(
284
- self, repo_full_name: str, title: str, body: str = "", labels=None
285
- ) -> str:
259
+ async def create_issue(self, repo_full_name: str, title: str, body: str = "", labels=None) -> str:
286
260
  """
287
261
  Creates a new issue in a GitHub repository using a title, body, and optional labels. It returns a formatted confirmation string with the new issue's number and URL, differing from `update_issue` which modifies existing issues and `search_issues` which returns raw API data.
288
262
 
@@ -306,26 +280,18 @@ class GithubApp(APIApplication):
306
280
  issue_data = {"title": title, "body": body}
307
281
  if labels:
308
282
  if isinstance(labels, str):
309
- labels_list = [
310
- label.strip() for label in labels.split(",") if label.strip()
311
- ]
283
+ labels_list = [label.strip() for label in labels.split(",") if label.strip()]
312
284
  issue_data["labels"] = labels_list
313
285
  else:
314
286
  issue_data["labels"] = labels
315
- response = self._post(url, issue_data)
287
+ response = await self._apost(url, issue_data)
316
288
  response.raise_for_status()
317
289
  issue = response.json()
318
290
  issue_number = issue.get("number", "Unknown")
319
291
  issue_url = issue.get("html_url", "")
320
- return (
321
- f"Successfully created issue #{issue_number}:\n"
322
- f"Title: {title}\n"
323
- f"URL: {issue_url}"
324
- )
325
-
326
- def list_repo_activities(
327
- self, repo_full_name: str, direction: str = "desc", per_page: int = 30
328
- ) -> str:
292
+ return f"Successfully created issue #{issue_number}:\nTitle: {title}\nURL: {issue_url}"
293
+
294
+ async def list_repo_activities(self, repo_full_name: str, direction: str = "desc", per_page: int = 30) -> str:
329
295
  """
330
296
  Fetches recent events for a GitHub repository and formats them into a human-readable string. It summarizes activities with actors and timestamps, providing a general event feed, unlike other `list_*` functions which retrieve specific resources like commits or issues.
331
297
 
@@ -347,24 +313,21 @@ class GithubApp(APIApplication):
347
313
  repo_full_name = repo_full_name.strip()
348
314
  url = f"{self.base_api_url}/{repo_full_name}/activity"
349
315
  params = {"direction": direction, "per_page": per_page}
350
- response = self._get(url, params=params)
316
+ response = await self._aget(url, params=params)
351
317
  response.raise_for_status()
352
318
  activities = response.json()
353
319
  if not activities:
354
320
  return f"No activities found for repository {repo_full_name}"
355
321
  result = f"Repository activities for {repo_full_name}:\n\n"
356
322
  for activity in activities:
357
- # Extract common fields
358
323
  timestamp = activity.get("timestamp", "Unknown time")
359
324
  actor_name = "Unknown user"
360
325
  if "actor" in activity and activity["actor"]:
361
326
  actor_name = activity["actor"].get("login", "Unknown user")
362
-
363
- # Create a simple description of the activity
364
327
  result += f"- {actor_name} performed an activity at {timestamp}\n"
365
328
  return result
366
329
 
367
- def update_issue(
330
+ async def update_issue(
368
331
  self,
369
332
  repo_full_name: str,
370
333
  issue_number: int,