universal-mcp-applications 0.1.30__py3-none-any.whl → 0.1.36rc1__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 (105) hide show
  1. universal_mcp/applications/ahrefs/app.py +52 -198
  2. universal_mcp/applications/airtable/app.py +23 -122
  3. universal_mcp/applications/apollo/app.py +111 -464
  4. universal_mcp/applications/asana/app.py +417 -1567
  5. universal_mcp/applications/aws_s3/app.py +33 -100
  6. universal_mcp/applications/bill/app.py +546 -1957
  7. universal_mcp/applications/box/app.py +1068 -3981
  8. universal_mcp/applications/braze/app.py +364 -1430
  9. universal_mcp/applications/browser_use/app.py +2 -8
  10. universal_mcp/applications/cal_com_v2/app.py +207 -625
  11. universal_mcp/applications/calendly/app.py +61 -200
  12. universal_mcp/applications/canva/app.py +45 -110
  13. universal_mcp/applications/clickup/app.py +207 -674
  14. universal_mcp/applications/coda/app.py +146 -426
  15. universal_mcp/applications/confluence/app.py +310 -1098
  16. universal_mcp/applications/contentful/app.py +36 -151
  17. universal_mcp/applications/crustdata/app.py +28 -107
  18. universal_mcp/applications/dialpad/app.py +283 -756
  19. universal_mcp/applications/digitalocean/app.py +1766 -5777
  20. universal_mcp/applications/domain_checker/app.py +3 -54
  21. universal_mcp/applications/e2b/app.py +14 -64
  22. universal_mcp/applications/elevenlabs/app.py +9 -47
  23. universal_mcp/applications/exa/app.py +6 -17
  24. universal_mcp/applications/falai/app.py +23 -100
  25. universal_mcp/applications/figma/app.py +53 -137
  26. universal_mcp/applications/file_system/app.py +2 -13
  27. universal_mcp/applications/firecrawl/app.py +51 -152
  28. universal_mcp/applications/fireflies/app.py +59 -281
  29. universal_mcp/applications/fpl/app.py +91 -528
  30. universal_mcp/applications/fpl/utils/fixtures.py +15 -49
  31. universal_mcp/applications/fpl/utils/helper.py +25 -89
  32. universal_mcp/applications/fpl/utils/league_utils.py +20 -64
  33. universal_mcp/applications/ghost_content/app.py +52 -161
  34. universal_mcp/applications/github/app.py +19 -56
  35. universal_mcp/applications/gong/app.py +88 -248
  36. universal_mcp/applications/google_calendar/app.py +16 -68
  37. universal_mcp/applications/google_docs/app.py +88 -188
  38. universal_mcp/applications/google_drive/app.py +140 -462
  39. universal_mcp/applications/google_gemini/app.py +12 -64
  40. universal_mcp/applications/google_mail/app.py +28 -157
  41. universal_mcp/applications/google_searchconsole/app.py +15 -48
  42. universal_mcp/applications/google_sheet/app.py +101 -578
  43. universal_mcp/applications/google_sheet/helper.py +10 -37
  44. universal_mcp/applications/hashnode/app.py +57 -269
  45. universal_mcp/applications/heygen/app.py +44 -122
  46. universal_mcp/applications/http_tools/app.py +10 -32
  47. universal_mcp/applications/hubspot/api_segments/crm_api.py +460 -1573
  48. universal_mcp/applications/hubspot/api_segments/marketing_api.py +74 -262
  49. universal_mcp/applications/hubspot/app.py +23 -87
  50. universal_mcp/applications/jira/app.py +2071 -7986
  51. universal_mcp/applications/klaviyo/app.py +494 -1376
  52. universal_mcp/applications/linkedin/README.md +9 -2
  53. universal_mcp/applications/linkedin/app.py +392 -212
  54. universal_mcp/applications/mailchimp/app.py +450 -1605
  55. universal_mcp/applications/markitdown/app.py +8 -20
  56. universal_mcp/applications/miro/app.py +217 -699
  57. universal_mcp/applications/ms_teams/app.py +64 -186
  58. universal_mcp/applications/neon/app.py +86 -192
  59. universal_mcp/applications/notion/app.py +21 -36
  60. universal_mcp/applications/onedrive/app.py +14 -36
  61. universal_mcp/applications/openai/app.py +42 -165
  62. universal_mcp/applications/outlook/app.py +16 -76
  63. universal_mcp/applications/perplexity/app.py +4 -19
  64. universal_mcp/applications/pipedrive/app.py +832 -3142
  65. universal_mcp/applications/posthog/app.py +163 -432
  66. universal_mcp/applications/reddit/app.py +40 -139
  67. universal_mcp/applications/resend/app.py +41 -107
  68. universal_mcp/applications/retell/app.py +14 -41
  69. universal_mcp/applications/rocketlane/app.py +221 -934
  70. universal_mcp/applications/scraper/README.md +7 -4
  71. universal_mcp/applications/scraper/app.py +216 -102
  72. universal_mcp/applications/semanticscholar/app.py +22 -64
  73. universal_mcp/applications/semrush/app.py +43 -77
  74. universal_mcp/applications/sendgrid/app.py +512 -1262
  75. universal_mcp/applications/sentry/app.py +271 -906
  76. universal_mcp/applications/serpapi/app.py +40 -143
  77. universal_mcp/applications/sharepoint/app.py +15 -37
  78. universal_mcp/applications/shopify/app.py +1551 -4287
  79. universal_mcp/applications/shortcut/app.py +155 -417
  80. universal_mcp/applications/slack/app.py +50 -101
  81. universal_mcp/applications/spotify/app.py +126 -325
  82. universal_mcp/applications/supabase/app.py +104 -213
  83. universal_mcp/applications/tavily/app.py +1 -1
  84. universal_mcp/applications/trello/app.py +693 -2656
  85. universal_mcp/applications/twilio/app.py +14 -50
  86. universal_mcp/applications/twitter/api_segments/compliance_api.py +4 -14
  87. universal_mcp/applications/twitter/api_segments/dm_conversations_api.py +6 -18
  88. universal_mcp/applications/twitter/api_segments/likes_api.py +1 -3
  89. universal_mcp/applications/twitter/api_segments/lists_api.py +5 -15
  90. universal_mcp/applications/twitter/api_segments/trends_api.py +1 -3
  91. universal_mcp/applications/twitter/api_segments/tweets_api.py +9 -31
  92. universal_mcp/applications/twitter/api_segments/usage_api.py +1 -5
  93. universal_mcp/applications/twitter/api_segments/users_api.py +14 -42
  94. universal_mcp/applications/whatsapp/app.py +35 -186
  95. universal_mcp/applications/whatsapp/audio.py +2 -6
  96. universal_mcp/applications/whatsapp/whatsapp.py +17 -51
  97. universal_mcp/applications/whatsapp_business/app.py +70 -283
  98. universal_mcp/applications/wrike/app.py +45 -118
  99. universal_mcp/applications/yahoo_finance/app.py +19 -65
  100. universal_mcp/applications/youtube/app.py +75 -261
  101. universal_mcp/applications/zenquotes/app.py +2 -2
  102. {universal_mcp_applications-0.1.30.dist-info → universal_mcp_applications-0.1.36rc1.dist-info}/METADATA +2 -2
  103. {universal_mcp_applications-0.1.30.dist-info → universal_mcp_applications-0.1.36rc1.dist-info}/RECORD +105 -105
  104. {universal_mcp_applications-0.1.30.dist-info → universal_mcp_applications-0.1.36rc1.dist-info}/WHEEL +0 -0
  105. {universal_mcp_applications-0.1.30.dist-info → universal_mcp_applications-0.1.36rc1.dist-info}/licenses/LICENSE +0 -0
@@ -1,9 +1,7 @@
1
1
  from typing import Any
2
-
3
2
  from universal_mcp.applications.application import APIApplication
4
3
  from universal_mcp.exceptions import NotAuthorizedError, ToolError
5
4
  from universal_mcp.integrations import Integration
6
-
7
5
  import resend
8
6
 
9
7
 
@@ -21,24 +19,14 @@ class ResendApp(APIApplication):
21
19
  if not self.integration:
22
20
  raise NotAuthorizedError("Resend integration not configured.")
23
21
  credentials = self.integration.get_credentials()
24
- api_key = (
25
- credentials.get("api_key")
26
- or credentials.get("API_KEY")
27
- or credentials.get("apiKey")
28
- )
22
+ api_key = credentials.get("api_key") or credentials.get("API_KEY") or credentials.get("apiKey")
29
23
  if not api_key:
30
24
  raise NotAuthorizedError("Resend API key not found in credentials.")
31
25
  self._api_key = api_key
32
26
  resend.api_key = self._api_key
33
27
  return self._api_key
34
28
 
35
- def send_email(
36
- self,
37
- from_email: str,
38
- to_emails: list[str],
39
- subject: str,
40
- text: str,
41
- ) -> dict[str, Any]:
29
+ async def send_email(self, from_email: str, to_emails: list[str], subject: str, text: str) -> dict[str, Any]:
42
30
  """
43
31
  Sends a single email with a specified subject and text body to a list of recipients via the Resend API. Unlike `send_batch_emails`, which processes multiple distinct emails at once, this function is designed for dispatching one individual email composition per API call.
44
32
 
@@ -58,22 +46,14 @@ class ResendApp(APIApplication):
58
46
  send, email, api, communication, important
59
47
  """
60
48
  self.api_key
61
- params: resend.Emails.SendParams = {
62
- "from": from_email,
63
- "to": to_emails,
64
- "subject": subject,
65
- "text": text,
66
- }
49
+ params: resend.Emails.SendParams = {"from": from_email, "to": to_emails, "subject": subject, "text": text}
67
50
  try:
68
51
  email = resend.Emails.send(params)
69
52
  return email
70
53
  except Exception as e:
71
54
  raise ToolError(f"Failed to send email: {e}")
72
55
 
73
- def send_batch_emails(
74
- self,
75
- emails: list[dict[str, Any]],
76
- ) -> dict[str, Any]:
56
+ async def send_batch_emails(self, emails: list[dict[str, Any]]) -> dict[str, Any]:
77
57
  """
78
58
  Sends multiple emails (1-100) in a single API request. Unlike the `send_email` function which handles a single message, this accepts a list of email objects for efficient, high-volume delivery. It validates that the batch size is within the allowed limits before making the API call.
79
59
 
@@ -91,9 +71,7 @@ class ResendApp(APIApplication):
91
71
  """
92
72
  self.api_key
93
73
  if not 1 <= len(emails) <= 100:
94
- raise ToolError(
95
- "The number of emails in a batch must be between 1 and 100."
96
- )
74
+ raise ToolError("The number of emails in a batch must be between 1 and 100.")
97
75
  params: list[resend.Emails.SendParams] = emails
98
76
  try:
99
77
  sent_emails_response = resend.Batch.send(params)
@@ -101,7 +79,7 @@ class ResendApp(APIApplication):
101
79
  except Exception as e:
102
80
  raise ToolError(f"Failed to send batch emails: {e}")
103
81
 
104
- def retrieve_email_by_id(self, email_id: str) -> dict[str, Any]:
82
+ async def retrieve_email_by_id(self, email_id: str) -> dict[str, Any]:
105
83
  """
106
84
  Retrieves the details and status of a single email from the Resend API using its unique identifier. This function allows for looking up a specific email that has already been sent or scheduled, distinct from functions that initiate sending.
107
85
 
@@ -124,7 +102,7 @@ class ResendApp(APIApplication):
124
102
  except Exception as e:
125
103
  raise ToolError(f"Failed to retrieve email: {e}")
126
104
 
127
- def reschedule_email(self, email_id: str, scheduled_at: str) -> dict[str, Any]:
105
+ async def reschedule_email(self, email_id: str, scheduled_at: str) -> dict[str, Any]:
128
106
  """
129
107
  Modifies the delivery time for a specific, previously scheduled email using its ID. It updates the `scheduled_at` attribute to a new ISO 8601 formatted time, effectively rescheduling its dispatch. This differs from `cancel_scheduled_email`, which permanently stops the send.
130
108
 
@@ -142,17 +120,14 @@ class ResendApp(APIApplication):
142
120
  update, email, async_job, management
143
121
  """
144
122
  self.api_key
145
- params: resend.Emails.UpdateParams = {
146
- "id": email_id,
147
- "scheduled_at": scheduled_at,
148
- }
123
+ params: resend.Emails.UpdateParams = {"id": email_id, "scheduled_at": scheduled_at}
149
124
  try:
150
125
  response = resend.Emails.update(params=params)
151
126
  return response
152
127
  except Exception as e:
153
128
  raise ToolError(f"Failed to update scheduled email: {e}")
154
129
 
155
- def cancel_scheduled_email(self, email_id: str) -> dict[str, Any]:
130
+ async def cancel_scheduled_email(self, email_id: str) -> dict[str, Any]:
156
131
  """
157
132
  Cancels a previously scheduled email using its unique ID, preventing it from being sent. This function calls the Resend API's cancellation endpoint, returning a confirmation response. It is distinct from `update_scheduled_email`, which reschedules the email instead of stopping its transmission.
158
133
 
@@ -175,7 +150,7 @@ class ResendApp(APIApplication):
175
150
  except Exception as e:
176
151
  raise ToolError(f"Failed to cancel scheduled email: {e}")
177
152
 
178
- def create_domain(self, name: str) -> dict[str, Any]:
153
+ async def create_domain(self, name: str) -> dict[str, Any]:
179
154
  """
180
155
  Registers a new sending domain with the Resend service using the provided name. This is a prerequisite for sending emails from your own domain and returns a dictionary containing details of the new domain object, which can then be verified and managed with other domain-related functions.
181
156
 
@@ -199,7 +174,7 @@ class ResendApp(APIApplication):
199
174
  except Exception as e:
200
175
  raise ToolError(f"Failed to create domain: {e}")
201
176
 
202
- def get_domain(self, domain_id: str) -> dict[str, Any]:
177
+ async def get_domain(self, domain_id: str) -> dict[str, Any]:
203
178
  """
204
179
  Retrieves the details of a specific domain from the Resend API using its unique ID. Unlike `list_domains`, which fetches all domains, this function targets a single record and returns a dictionary containing the domain's properties, like its verification status and tracking settings.
205
180
 
@@ -222,7 +197,7 @@ class ResendApp(APIApplication):
222
197
  except Exception as e:
223
198
  raise ToolError(f"Failed to retrieve domain: {e}")
224
199
 
225
- def verify_domain(self, domain_id: str) -> dict[str, Any]:
200
+ async def verify_domain(self, domain_id: str) -> dict[str, Any]:
226
201
  """
227
202
  Triggers the verification process for a registered domain using its unique ID. This action is crucial for authorizing the domain to send emails via Resend and returns an API response containing the verification status and necessary DNS records to complete the process.
228
203
 
@@ -245,12 +220,8 @@ class ResendApp(APIApplication):
245
220
  except Exception as e:
246
221
  raise ToolError(f"Failed to verify domain: {e}")
247
222
 
248
- def update_domain_settings(
249
- self,
250
- domain_id: str,
251
- open_tracking: bool | None = None,
252
- click_tracking: bool | None = None,
253
- tls: str | None = None,
223
+ async def update_domain_settings(
224
+ self, domain_id: str, open_tracking: bool | None = None, click_tracking: bool | None = None, tls: str | None = None
254
225
  ) -> dict[str, Any]:
255
226
  """
256
227
  Updates settings for a specific domain identified by its ID. This function can modify configurations like open and click tracking, and TLS enforcement. It returns the updated domain object from the API, raising a ToolError if the update fails. Only the provided settings are modified.
@@ -284,7 +255,7 @@ class ResendApp(APIApplication):
284
255
  except Exception as e:
285
256
  raise ToolError(f"Failed to update domain: {e}")
286
257
 
287
- def list_domains(self) -> list[dict[str, Any]]:
258
+ async def list_domains(self) -> list[dict[str, Any]]:
288
259
  """
289
260
  Fetches a complete list of all domains registered with the Resend account. Unlike `get_domain`, which retrieves a single domain by ID, this provides a comprehensive overview of all configured domains for management and verification tasks.
290
261
 
@@ -304,7 +275,7 @@ class ResendApp(APIApplication):
304
275
  except Exception as e:
305
276
  raise ToolError(f"Failed to list domains: {e}")
306
277
 
307
- def remove_domain(self, domain_id: str) -> dict[str, Any]:
278
+ async def remove_domain(self, domain_id: str) -> dict[str, Any]:
308
279
  """
309
280
  Permanently removes a specific domain from the Resend account using its unique ID. This function makes an authenticated API call to delete the domain, distinguishing it from retrieval (`get_domain`) or modification (`update_domain`) operations, and raises an error if the process fails.
310
281
 
@@ -327,7 +298,7 @@ class ResendApp(APIApplication):
327
298
  except Exception as e:
328
299
  raise ToolError(f"Failed to remove domain: {e}")
329
300
 
330
- def create_api_key(self, name: str) -> dict[str, Any]:
301
+ async def create_api_key(self, name: str) -> dict[str, Any]:
331
302
  """
332
303
  Creates a new API key for authenticating with the Resend service, identified by a specified name. It returns a dictionary containing the new key object, including the generated token required for subsequent API requests.
333
304
 
@@ -351,7 +322,7 @@ class ResendApp(APIApplication):
351
322
  except Exception as e:
352
323
  raise ToolError(f"Failed to create API key: {e}")
353
324
 
354
- def list_api_keys(self) -> list[dict[str, Any]]:
325
+ async def list_api_keys(self) -> list[dict[str, Any]]:
355
326
  """
356
327
  Retrieves a list of all API keys for the authenticated Resend account. This read-only operation allows for auditing and viewing existing credentials, contrasting with `create_api_key` and `remove_api_key` which are used to add or delete keys.
357
328
 
@@ -374,7 +345,7 @@ class ResendApp(APIApplication):
374
345
  except Exception as e:
375
346
  raise ToolError(f"Failed to list API keys: {e}")
376
347
 
377
- def remove_api_key(self, api_key_id: str) -> dict[str, Any]:
348
+ async def remove_api_key(self, api_key_id: str) -> dict[str, Any]:
378
349
  """
379
350
  Deletes a specific Resend API key identified by its unique ID. This function, part of the key management suite alongside `create_api_key` and `list_api_keys`, returns an API confirmation response or raises a `ToolError` if the operation fails.
380
351
 
@@ -397,13 +368,7 @@ class ResendApp(APIApplication):
397
368
  except Exception as e:
398
369
  raise ToolError(f"Failed to remove API key: {e}")
399
370
 
400
- def register_broadcast(
401
- self,
402
- audience_id: str,
403
- from_email: str,
404
- subject: str,
405
- html: str,
406
- ) -> dict[str, Any]:
371
+ async def register_broadcast(self, audience_id: str, from_email: str, subject: str, html: str) -> dict[str, Any]:
407
372
  """
408
373
  Registers a new email broadcast campaign for a specific audience using the Resend API. This function creates the broadcast object but does not send it; use the `send_broadcast` function to dispatch the created campaign to the audience.
409
374
 
@@ -423,19 +388,14 @@ class ResendApp(APIApplication):
423
388
  broadcast, email, important
424
389
  """
425
390
  self.api_key
426
- params: resend.Broadcasts.CreateParams = {
427
- "audience_id": audience_id,
428
- "from": from_email,
429
- "subject": subject,
430
- "html": html,
431
- }
391
+ params: resend.Broadcasts.CreateParams = {"audience_id": audience_id, "from": from_email, "subject": subject, "html": html}
432
392
  try:
433
393
  broadcast = resend.Broadcasts.create(params)
434
394
  return broadcast
435
395
  except Exception as e:
436
396
  raise ToolError(f"Failed to create broadcast: {e}")
437
397
 
438
- def get_broadcast(self, broadcast_id: str) -> dict[str, Any]:
398
+ async def get_broadcast(self, broadcast_id: str) -> dict[str, Any]:
439
399
  """
440
400
  Retrieves a specific broadcast's complete details, including its status and content, by its unique ID. Unlike `list_broadcasts` which retrieves all broadcasts, this function targets a single entry for inspection.
441
401
 
@@ -458,12 +418,7 @@ class ResendApp(APIApplication):
458
418
  except Exception as e:
459
419
  raise ToolError(f"Failed to retrieve broadcast: {e}")
460
420
 
461
- def update_broadcast(
462
- self,
463
- broadcast_id: str,
464
- html: str | None = None,
465
- subject: str | None = None,
466
- ) -> dict[str, Any]:
421
+ async def update_broadcast(self, broadcast_id: str, html: str | None = None, subject: str | None = None) -> dict[str, Any]:
467
422
  """
468
423
  Updates the HTML content and/or subject of an existing broadcast, identified by its ID. Requires that at least one modifiable field (html or subject) is provided. This function alters a broadcast's content, differing from `send_broadcast` which triggers its delivery.
469
424
 
@@ -488,18 +443,14 @@ class ResendApp(APIApplication):
488
443
  if subject is not None:
489
444
  params["subject"] = subject
490
445
  if len(params) == 1:
491
- raise ToolError(
492
- "At least one field (e.g., html, subject) must be provided for the update."
493
- )
446
+ raise ToolError("At least one field (e.g., html, subject) must be provided for the update.")
494
447
  try:
495
448
  updated_broadcast = resend.Broadcasts.update(params)
496
449
  return updated_broadcast
497
450
  except Exception as e:
498
451
  raise ToolError(f"Failed to update broadcast: {e}")
499
452
 
500
- def send_or_schedule_broadcast(
501
- self, broadcast_id: str, scheduled_at: str | None = None
502
- ) -> dict[str, Any]:
453
+ async def send_or_schedule_broadcast(self, broadcast_id: str, scheduled_at: str | None = None) -> dict[str, Any]:
503
454
  """
504
455
  Initiates the delivery of a pre-existing broadcast, identified by its ID, to its target audience. The broadcast can be sent immediately or scheduled for a future time via the optional `scheduled_at` parameter. It returns the API response upon execution.
505
456
 
@@ -526,7 +477,7 @@ class ResendApp(APIApplication):
526
477
  except Exception as e:
527
478
  raise ToolError(f"Failed to send broadcast: {e}")
528
479
 
529
- def remove_draft_broadcast(self, broadcast_id: str) -> dict[str, Any]:
480
+ async def remove_draft_broadcast(self, broadcast_id: str) -> dict[str, Any]:
530
481
  """
531
482
  Deletes a broadcast from the Resend service using its unique ID. This action is restricted to broadcasts that have a 'draft' status and have not been sent, returning the API's response upon successful removal or raising an error if the operation fails.
532
483
 
@@ -549,7 +500,7 @@ class ResendApp(APIApplication):
549
500
  except Exception as e:
550
501
  raise ToolError(f"Failed to remove broadcast: {e}")
551
502
 
552
- def list_broadcasts(self) -> list[dict[str, Any]]:
503
+ async def list_broadcasts(self) -> list[dict[str, Any]]:
553
504
  """
554
505
  Retrieves a list of all broadcasts associated with the authenticated account. Unlike `get_broadcast` which fetches a single item by ID, this function returns a list of dictionaries, each containing the attributes of a specific broadcast. Raises a `ToolError` on API failure.
555
506
 
@@ -569,7 +520,7 @@ class ResendApp(APIApplication):
569
520
  except Exception as e:
570
521
  raise ToolError(f"Failed to list broadcasts: {e}")
571
522
 
572
- def create_audience(self, name: str) -> dict[str, Any]:
523
+ async def create_audience(self, name: str) -> dict[str, Any]:
573
524
  """
574
525
  Creates a new audience, a named list for contacts, within the Resend service. This function requires a name for the audience and returns a dictionary representing the newly created object, enabling subsequent management of contacts within that specific list.
575
526
 
@@ -593,7 +544,7 @@ class ResendApp(APIApplication):
593
544
  except Exception as e:
594
545
  raise ToolError(f"Failed to create audience: {e}")
595
546
 
596
- def get_audience(self, audience_id: str) -> dict[str, Any]:
547
+ async def get_audience(self, audience_id: str) -> dict[str, Any]:
597
548
  """
598
549
  Retrieves the details of a single audience using its unique ID. This provides a targeted lookup for one audience, distinct from `list_audiences` which fetches all available audiences in the account.
599
550
 
@@ -616,7 +567,7 @@ class ResendApp(APIApplication):
616
567
  except Exception as e:
617
568
  raise ToolError(f"Failed to retrieve audience: {e}")
618
569
 
619
- def remove_audience(self, audience_id: str) -> dict[str, Any]:
570
+ async def remove_audience(self, audience_id: str) -> dict[str, Any]:
620
571
  """
621
572
  Deletes a specific audience from the Resend service using its unique identifier. This function wraps the Resend API's remove operation, returning the API's response. Unlike `remove_contact`, which targets individuals, this function removes the entire contact list defined by the audience ID.
622
573
 
@@ -639,7 +590,7 @@ class ResendApp(APIApplication):
639
590
  except Exception as e:
640
591
  raise ToolError(f"Failed to remove audience: {e}")
641
592
 
642
- def list_audiences(self) -> list[dict[str, Any]]:
593
+ async def list_audiences(self) -> list[dict[str, Any]]:
643
594
  """
644
595
  Retrieves a complete list of all audiences from the Resend account. It returns a list of dictionaries, with each containing the details of a specific audience. This function is distinct from `get_audience`, which fetches a single audience by its ID.
645
596
 
@@ -659,13 +610,8 @@ class ResendApp(APIApplication):
659
610
  except Exception as e:
660
611
  raise ToolError(f"Failed to list audiences: {e}")
661
612
 
662
- def create_contact(
663
- self,
664
- audience_id: str,
665
- email: str,
666
- first_name: str | None = None,
667
- last_name: str | None = None,
668
- unsubscribed: bool = False,
613
+ async def create_contact(
614
+ self, audience_id: str, email: str, first_name: str | None = None, last_name: str | None = None, unsubscribed: bool = False
669
615
  ) -> dict[str, Any]:
670
616
  """
671
617
  Creates a new contact with a given email, optional name, and subscription status, adding it to a specific audience. This function populates audience lists, differing from `update_contact` which modifies existing entries, and requires a valid `audience_id` to function.
@@ -687,11 +633,7 @@ class ResendApp(APIApplication):
687
633
  create, contact, management, important
688
634
  """
689
635
  self.api_key
690
- params: resend.Contacts.CreateParams = {
691
- "audience_id": audience_id,
692
- "email": email,
693
- "unsubscribed": unsubscribed,
694
- }
636
+ params: resend.Contacts.CreateParams = {"audience_id": audience_id, "email": email, "unsubscribed": unsubscribed}
695
637
  if first_name:
696
638
  params["first_name"] = first_name
697
639
  if last_name:
@@ -702,9 +644,7 @@ class ResendApp(APIApplication):
702
644
  except Exception as e:
703
645
  raise ToolError(f"Failed to create contact: {e}")
704
646
 
705
- def get_contact(
706
- self, audience_id: str, contact_id: str | None = None, email: str | None = None
707
- ) -> dict[str, Any]:
647
+ async def get_contact(self, audience_id: str, contact_id: str | None = None, email: str | None = None) -> dict[str, Any]:
708
648
  """
709
649
  Fetches a single contact's details from a specified audience by its unique ID or email address. The function requires exactly one identifier for the lookup, raising an error if the identifier is missing, ambiguous, or if the API call fails.
710
650
 
@@ -736,7 +676,7 @@ class ResendApp(APIApplication):
736
676
  except Exception as e:
737
677
  raise ToolError(f"Failed to retrieve contact: {e}")
738
678
 
739
- def update_contact(
679
+ async def update_contact(
740
680
  self,
741
681
  audience_id: str,
742
682
  contact_id: str | None = None,
@@ -767,9 +707,7 @@ class ResendApp(APIApplication):
767
707
  """
768
708
  self.api_key
769
709
  if not (contact_id or email) or (contact_id and email):
770
- raise ToolError(
771
- "You must provide exactly one of 'contact_id' or 'email' to identify the contact."
772
- )
710
+ raise ToolError("You must provide exactly one of 'contact_id' or 'email' to identify the contact.")
773
711
  params: resend.Contacts.UpdateParams = {"audience_id": audience_id}
774
712
  if contact_id:
775
713
  params["id"] = contact_id
@@ -781,19 +719,15 @@ class ResendApp(APIApplication):
781
719
  params["last_name"] = last_name
782
720
  if unsubscribed is not None:
783
721
  params["unsubscribed"] = unsubscribed
784
- if len(params) <= 2: # Only audience_id and one identifier
785
- raise ToolError(
786
- "At least one field to update (e.g., first_name, unsubscribed) must be provided."
787
- )
722
+ if len(params) <= 2:
723
+ raise ToolError("At least one field to update (e.g., first_name, unsubscribed) must be provided.")
788
724
  try:
789
725
  response = resend.Contacts.update(params)
790
726
  return response
791
727
  except Exception as e:
792
728
  raise ToolError(f"Failed to update contact: {e}")
793
729
 
794
- def remove_contact(
795
- self, audience_id: str, contact_id: str | None = None, email: str | None = None
796
- ) -> dict[str, Any]:
730
+ async def remove_contact(self, audience_id: str, contact_id: str | None = None, email: str | None = None) -> dict[str, Any]:
797
731
  """
798
732
  Removes a contact from a specified audience. The contact must be identified by either its unique ID or email address, but not both. Raises an error if the identifier is missing, ambiguous, or if the API call to the Resend service fails.
799
733
 
@@ -825,7 +759,7 @@ class ResendApp(APIApplication):
825
759
  except Exception as e:
826
760
  raise ToolError(f"Failed to remove contact: {e}")
827
761
 
828
- def list_contacts(self, audience_id: str) -> list[dict[str, Any]]:
762
+ async def list_contacts(self, audience_id: str) -> list[dict[str, Any]]:
829
763
  """
830
764
  Retrieves a complete list of contacts belonging to a specific audience, identified by its unique ID. This function returns all contacts within the audience, unlike `get_contact` which retrieves only a single contact by its ID or email.
831
765
 
@@ -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
 
@@ -9,7 +8,7 @@ class RetellApp(APIApplication):
9
8
  super().__init__(name="retell", integration=integration, **kwargs)
10
9
  self.base_url = "https://api.retellai.com"
11
10
 
12
- def get_v2_get_call_by_call_id(self, call_id) -> dict[str, Any]:
11
+ async def get_v2_get_call_by_call_id(self, call_id) -> dict[str, Any]:
13
12
  """
14
13
  Retrieve detailed information about a specific call using its call ID.
15
14
 
@@ -34,13 +33,8 @@ class RetellApp(APIApplication):
34
33
  response.raise_for_status()
35
34
  return response.json()
36
35
 
37
- def post_v2_create_phone_call(
38
- self,
39
- from_number,
40
- to_number,
41
- override_agent_id=None,
42
- metadata=None,
43
- retell_llm_dynamic_variables=None,
36
+ async def post_v2_create_phone_call(
37
+ self, from_number, to_number, override_agent_id=None, metadata=None, retell_llm_dynamic_variables=None
44
38
  ) -> dict[str, Any]:
45
39
  """
46
40
  Initiates a phone call using a JSON payload with specified parameters.
@@ -79,9 +73,7 @@ class RetellApp(APIApplication):
79
73
  response.raise_for_status()
80
74
  return response.json()
81
75
 
82
- def post_v2_create_web_call(
83
- self, agent_id, metadata=None, retell_llm_dynamic_variables=None
84
- ) -> dict[str, Any]:
76
+ async def post_v2_create_web_call(self, agent_id, metadata=None, retell_llm_dynamic_variables=None) -> dict[str, Any]:
85
77
  """
86
78
  Creates a web call via a POST request to the v2 endpoint with specified agent ID and optional metadata or dynamic variables.
87
79
 
@@ -101,11 +93,7 @@ class RetellApp(APIApplication):
101
93
  """
102
94
  if agent_id is None:
103
95
  raise ValueError("Missing required parameter 'agent_id'")
104
- request_body = {
105
- "agent_id": agent_id,
106
- "metadata": metadata,
107
- "retell_llm_dynamic_variables": retell_llm_dynamic_variables,
108
- }
96
+ request_body = {"agent_id": agent_id, "metadata": metadata, "retell_llm_dynamic_variables": retell_llm_dynamic_variables}
109
97
  request_body = {k: v for k, v in request_body.items() if v is not None}
110
98
  url = f"{self.base_url}/v2/create-web-call"
111
99
  query_params = {}
@@ -113,7 +101,7 @@ class RetellApp(APIApplication):
113
101
  response.raise_for_status()
114
102
  return response.json()
115
103
 
116
- def get_get_voice_by_voice_id(self, voice_id) -> dict[str, Any]:
104
+ async def get_get_voice_by_voice_id(self, voice_id) -> dict[str, Any]:
117
105
  """
118
106
  Fetches voice details based on the provided voice ID.
119
107
 
@@ -137,9 +125,7 @@ class RetellApp(APIApplication):
137
125
  response.raise_for_status()
138
126
  return response.json()
139
127
 
140
- def post_v2_list_calls(
141
- self, filter_criteria=None, sort_order=None, limit=None, pagination_key=None
142
- ) -> list[Any]:
128
+ async def post_v2_list_calls(self, filter_criteria=None, sort_order=None, limit=None, pagination_key=None) -> list[Any]:
143
129
  """
144
130
  Sends a POST request to list call records with optional filtering, sorting, pagination, and limits.
145
131
 
@@ -158,12 +144,7 @@ class RetellApp(APIApplication):
158
144
  Tags:
159
145
  list, calls, api, batch, management, important
160
146
  """
161
- request_body = {
162
- "filter_criteria": filter_criteria,
163
- "sort_order": sort_order,
164
- "limit": limit,
165
- "pagination_key": pagination_key,
166
- }
147
+ request_body = {"filter_criteria": filter_criteria, "sort_order": sort_order, "limit": limit, "pagination_key": pagination_key}
167
148
  request_body = {k: v for k, v in request_body.items() if v is not None}
168
149
  url = f"{self.base_url}/v2/list-calls"
169
150
  query_params = {}
@@ -171,9 +152,7 @@ class RetellApp(APIApplication):
171
152
  response.raise_for_status()
172
153
  return response.json()
173
154
 
174
- def post_create_phone_number(
175
- self, area_code, inbound_agent_id=None, outbound_agent_id=None, nickname=None
176
- ) -> dict[str, Any]:
155
+ async def post_create_phone_number(self, area_code, inbound_agent_id=None, outbound_agent_id=None, nickname=None) -> dict[str, Any]:
177
156
  """
178
157
  Creates a phone number with the specified area code and optional parameters.
179
158
 
@@ -208,7 +187,7 @@ class RetellApp(APIApplication):
208
187
  response.raise_for_status()
209
188
  return response.json()
210
189
 
211
- def get_get_phone_number_by_phone_number(self, phone_number) -> dict[str, Any]:
190
+ async def get_get_phone_number_by_phone_number(self, phone_number) -> dict[str, Any]:
212
191
  """
213
192
  Retrieves phone number details by making a GET request to the API endpoint using the provided phone number.
214
193
 
@@ -233,9 +212,7 @@ class RetellApp(APIApplication):
233
212
  response.raise_for_status()
234
213
  return response.json()
235
214
 
236
- def get_list_phone_numbers(
237
- self,
238
- ) -> list[Any]:
215
+ async def get_list_phone_numbers(self) -> list[Any]:
239
216
  """
240
217
  Retrieves a list of phone numbers from the remote API.
241
218
 
@@ -257,7 +234,7 @@ class RetellApp(APIApplication):
257
234
  response.raise_for_status()
258
235
  return response.json()
259
236
 
260
- def patch_update_phone_number_by_phone_number(
237
+ async def patch_update_phone_number_by_phone_number(
261
238
  self, phone_number, inbound_agent_id=None, outbound_agent_id=None, nickname=None
262
239
  ) -> dict[str, Any]:
263
240
  """
@@ -281,11 +258,7 @@ class RetellApp(APIApplication):
281
258
  """
282
259
  if phone_number is None:
283
260
  raise ValueError("Missing required parameter 'phone_number'")
284
- request_body = {
285
- "inbound_agent_id": inbound_agent_id,
286
- "outbound_agent_id": outbound_agent_id,
287
- "nickname": nickname,
288
- }
261
+ request_body = {"inbound_agent_id": inbound_agent_id, "outbound_agent_id": outbound_agent_id, "nickname": nickname}
289
262
  request_body = {k: v for k, v in request_body.items() if v is not None}
290
263
  url = f"{self.base_url}/update-phone-number/{phone_number}"
291
264
  query_params = {}
@@ -293,7 +266,7 @@ class RetellApp(APIApplication):
293
266
  response.raise_for_status()
294
267
  return response.json()
295
268
 
296
- def delete_delete_phone_number_by_phone_number(self, phone_number) -> Any:
269
+ async def delete_delete_phone_number_by_phone_number(self, phone_number) -> Any:
297
270
  """
298
271
  Deletes a phone number resource by its phone number identifier via an HTTP DELETE request.
299
272