universal-mcp-applications 0.1.1__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.
Files changed (268) hide show
  1. universal_mcp/applications/ahrefs/README.md +51 -0
  2. universal_mcp/applications/ahrefs/__init__.py +1 -0
  3. universal_mcp/applications/ahrefs/app.py +2291 -0
  4. universal_mcp/applications/airtable/README.md +22 -0
  5. universal_mcp/applications/airtable/__init__.py +1 -0
  6. universal_mcp/applications/airtable/app.py +479 -0
  7. universal_mcp/applications/apollo/README.md +44 -0
  8. universal_mcp/applications/apollo/__init__.py +1 -0
  9. universal_mcp/applications/apollo/app.py +1847 -0
  10. universal_mcp/applications/asana/README.md +199 -0
  11. universal_mcp/applications/asana/__init__.py +1 -0
  12. universal_mcp/applications/asana/app.py +9509 -0
  13. universal_mcp/applications/aws-s3/README.md +0 -0
  14. universal_mcp/applications/aws-s3/__init__.py +1 -0
  15. universal_mcp/applications/aws-s3/app.py +552 -0
  16. universal_mcp/applications/bill/README.md +0 -0
  17. universal_mcp/applications/bill/__init__.py +1 -0
  18. universal_mcp/applications/bill/app.py +8705 -0
  19. universal_mcp/applications/box/README.md +307 -0
  20. universal_mcp/applications/box/__init__.py +1 -0
  21. universal_mcp/applications/box/app.py +15987 -0
  22. universal_mcp/applications/braze/README.md +106 -0
  23. universal_mcp/applications/braze/__init__.py +1 -0
  24. universal_mcp/applications/braze/app.py +4754 -0
  25. universal_mcp/applications/cal-com-v2/README.md +150 -0
  26. universal_mcp/applications/cal-com-v2/__init__.py +1 -0
  27. universal_mcp/applications/cal-com-v2/app.py +5541 -0
  28. universal_mcp/applications/calendly/README.md +53 -0
  29. universal_mcp/applications/calendly/__init__.py +1 -0
  30. universal_mcp/applications/calendly/app.py +1436 -0
  31. universal_mcp/applications/canva/README.md +43 -0
  32. universal_mcp/applications/canva/__init__.py +1 -0
  33. universal_mcp/applications/canva/app.py +941 -0
  34. universal_mcp/applications/clickup/README.md +135 -0
  35. universal_mcp/applications/clickup/__init__.py +1 -0
  36. universal_mcp/applications/clickup/app.py +5009 -0
  37. universal_mcp/applications/coda/README.md +108 -0
  38. universal_mcp/applications/coda/__init__.py +1 -0
  39. universal_mcp/applications/coda/app.py +3671 -0
  40. universal_mcp/applications/confluence/README.md +198 -0
  41. universal_mcp/applications/confluence/__init__.py +1 -0
  42. universal_mcp/applications/confluence/app.py +6273 -0
  43. universal_mcp/applications/contentful/README.md +17 -0
  44. universal_mcp/applications/contentful/__init__.py +1 -0
  45. universal_mcp/applications/contentful/app.py +364 -0
  46. universal_mcp/applications/crustdata/README.md +25 -0
  47. universal_mcp/applications/crustdata/__init__.py +1 -0
  48. universal_mcp/applications/crustdata/app.py +586 -0
  49. universal_mcp/applications/dialpad/README.md +202 -0
  50. universal_mcp/applications/dialpad/__init__.py +1 -0
  51. universal_mcp/applications/dialpad/app.py +5949 -0
  52. universal_mcp/applications/digitalocean/README.md +463 -0
  53. universal_mcp/applications/digitalocean/__init__.py +1 -0
  54. universal_mcp/applications/digitalocean/app.py +20835 -0
  55. universal_mcp/applications/domain-checker/README.md +13 -0
  56. universal_mcp/applications/domain-checker/__init__.py +1 -0
  57. universal_mcp/applications/domain-checker/app.py +265 -0
  58. universal_mcp/applications/e2b/README.md +12 -0
  59. universal_mcp/applications/e2b/__init__.py +1 -0
  60. universal_mcp/applications/e2b/app.py +187 -0
  61. universal_mcp/applications/elevenlabs/README.md +88 -0
  62. universal_mcp/applications/elevenlabs/__init__.py +1 -0
  63. universal_mcp/applications/elevenlabs/app.py +3235 -0
  64. universal_mcp/applications/exa/README.md +15 -0
  65. universal_mcp/applications/exa/__init__.py +1 -0
  66. universal_mcp/applications/exa/app.py +221 -0
  67. universal_mcp/applications/falai/README.md +17 -0
  68. universal_mcp/applications/falai/__init__.py +1 -0
  69. universal_mcp/applications/falai/app.py +331 -0
  70. universal_mcp/applications/figma/README.md +49 -0
  71. universal_mcp/applications/figma/__init__.py +1 -0
  72. universal_mcp/applications/figma/app.py +1090 -0
  73. universal_mcp/applications/firecrawl/README.md +20 -0
  74. universal_mcp/applications/firecrawl/__init__.py +1 -0
  75. universal_mcp/applications/firecrawl/app.py +514 -0
  76. universal_mcp/applications/fireflies/README.md +25 -0
  77. universal_mcp/applications/fireflies/__init__.py +1 -0
  78. universal_mcp/applications/fireflies/app.py +506 -0
  79. universal_mcp/applications/fpl/README.md +23 -0
  80. universal_mcp/applications/fpl/__init__.py +1 -0
  81. universal_mcp/applications/fpl/app.py +1327 -0
  82. universal_mcp/applications/fpl/utils/api.py +142 -0
  83. universal_mcp/applications/fpl/utils/fixtures.py +629 -0
  84. universal_mcp/applications/fpl/utils/helper.py +982 -0
  85. universal_mcp/applications/fpl/utils/league_utils.py +546 -0
  86. universal_mcp/applications/fpl/utils/position_utils.py +68 -0
  87. universal_mcp/applications/ghost-content/README.md +25 -0
  88. universal_mcp/applications/ghost-content/__init__.py +1 -0
  89. universal_mcp/applications/ghost-content/app.py +654 -0
  90. universal_mcp/applications/github/README.md +1049 -0
  91. universal_mcp/applications/github/__init__.py +1 -0
  92. universal_mcp/applications/github/app.py +50600 -0
  93. universal_mcp/applications/gong/README.md +63 -0
  94. universal_mcp/applications/gong/__init__.py +1 -0
  95. universal_mcp/applications/gong/app.py +2297 -0
  96. universal_mcp/applications/google-ads/README.md +0 -0
  97. universal_mcp/applications/google-ads/__init__.py +1 -0
  98. universal_mcp/applications/google-ads/app.py +23 -0
  99. universal_mcp/applications/google-calendar/README.md +21 -0
  100. universal_mcp/applications/google-calendar/__init__.py +1 -0
  101. universal_mcp/applications/google-calendar/app.py +574 -0
  102. universal_mcp/applications/google-docs/README.md +25 -0
  103. universal_mcp/applications/google-docs/__init__.py +1 -0
  104. universal_mcp/applications/google-docs/app.py +760 -0
  105. universal_mcp/applications/google-drive/README.md +68 -0
  106. universal_mcp/applications/google-drive/__init__.py +1 -0
  107. universal_mcp/applications/google-drive/app.py +4936 -0
  108. universal_mcp/applications/google-gemini/README.md +25 -0
  109. universal_mcp/applications/google-gemini/__init__.py +1 -0
  110. universal_mcp/applications/google-gemini/app.py +663 -0
  111. universal_mcp/applications/google-mail/README.md +31 -0
  112. universal_mcp/applications/google-mail/__init__.py +1 -0
  113. universal_mcp/applications/google-mail/app.py +1354 -0
  114. universal_mcp/applications/google-searchconsole/README.md +21 -0
  115. universal_mcp/applications/google-searchconsole/__init__.py +1 -0
  116. universal_mcp/applications/google-searchconsole/app.py +320 -0
  117. universal_mcp/applications/google-sheet/README.md +36 -0
  118. universal_mcp/applications/google-sheet/__init__.py +1 -0
  119. universal_mcp/applications/google-sheet/app.py +1941 -0
  120. universal_mcp/applications/hashnode/README.md +20 -0
  121. universal_mcp/applications/hashnode/__init__.py +1 -0
  122. universal_mcp/applications/hashnode/app.py +455 -0
  123. universal_mcp/applications/heygen/README.md +44 -0
  124. universal_mcp/applications/heygen/__init__.py +1 -0
  125. universal_mcp/applications/heygen/app.py +961 -0
  126. universal_mcp/applications/http-tools/README.md +16 -0
  127. universal_mcp/applications/http-tools/__init__.py +1 -0
  128. universal_mcp/applications/http-tools/app.py +153 -0
  129. universal_mcp/applications/hubspot/README.md +239 -0
  130. universal_mcp/applications/hubspot/__init__.py +1 -0
  131. universal_mcp/applications/hubspot/app.py +416 -0
  132. universal_mcp/applications/jira/README.md +600 -0
  133. universal_mcp/applications/jira/__init__.py +1 -0
  134. universal_mcp/applications/jira/app.py +28804 -0
  135. universal_mcp/applications/klaviyo/README.md +313 -0
  136. universal_mcp/applications/klaviyo/__init__.py +1 -0
  137. universal_mcp/applications/klaviyo/app.py +11236 -0
  138. universal_mcp/applications/linkedin/README.md +15 -0
  139. universal_mcp/applications/linkedin/__init__.py +1 -0
  140. universal_mcp/applications/linkedin/app.py +243 -0
  141. universal_mcp/applications/mailchimp/README.md +281 -0
  142. universal_mcp/applications/mailchimp/__init__.py +1 -0
  143. universal_mcp/applications/mailchimp/app.py +10937 -0
  144. universal_mcp/applications/markitdown/README.md +12 -0
  145. universal_mcp/applications/markitdown/__init__.py +1 -0
  146. universal_mcp/applications/markitdown/app.py +63 -0
  147. universal_mcp/applications/miro/README.md +151 -0
  148. universal_mcp/applications/miro/__init__.py +1 -0
  149. universal_mcp/applications/miro/app.py +5429 -0
  150. universal_mcp/applications/ms-teams/README.md +42 -0
  151. universal_mcp/applications/ms-teams/__init__.py +1 -0
  152. universal_mcp/applications/ms-teams/app.py +1823 -0
  153. universal_mcp/applications/neon/README.md +74 -0
  154. universal_mcp/applications/neon/__init__.py +1 -0
  155. universal_mcp/applications/neon/app.py +2018 -0
  156. universal_mcp/applications/notion/README.md +30 -0
  157. universal_mcp/applications/notion/__init__.py +1 -0
  158. universal_mcp/applications/notion/app.py +527 -0
  159. universal_mcp/applications/openai/README.md +22 -0
  160. universal_mcp/applications/openai/__init__.py +1 -0
  161. universal_mcp/applications/openai/app.py +759 -0
  162. universal_mcp/applications/outlook/README.md +20 -0
  163. universal_mcp/applications/outlook/__init__.py +1 -0
  164. universal_mcp/applications/outlook/app.py +444 -0
  165. universal_mcp/applications/perplexity/README.md +12 -0
  166. universal_mcp/applications/perplexity/__init__.py +1 -0
  167. universal_mcp/applications/perplexity/app.py +65 -0
  168. universal_mcp/applications/pipedrive/README.md +284 -0
  169. universal_mcp/applications/pipedrive/__init__.py +1 -0
  170. universal_mcp/applications/pipedrive/app.py +12924 -0
  171. universal_mcp/applications/posthog/README.md +132 -0
  172. universal_mcp/applications/posthog/__init__.py +1 -0
  173. universal_mcp/applications/posthog/app.py +7125 -0
  174. universal_mcp/applications/reddit/README.md +135 -0
  175. universal_mcp/applications/reddit/__init__.py +1 -0
  176. universal_mcp/applications/reddit/app.py +4652 -0
  177. universal_mcp/applications/replicate/README.md +18 -0
  178. universal_mcp/applications/replicate/__init__.py +1 -0
  179. universal_mcp/applications/replicate/app.py +495 -0
  180. universal_mcp/applications/resend/README.md +40 -0
  181. universal_mcp/applications/resend/__init__.py +1 -0
  182. universal_mcp/applications/resend/app.py +881 -0
  183. universal_mcp/applications/retell/README.md +21 -0
  184. universal_mcp/applications/retell/__init__.py +1 -0
  185. universal_mcp/applications/retell/app.py +333 -0
  186. universal_mcp/applications/rocketlane/README.md +70 -0
  187. universal_mcp/applications/rocketlane/__init__.py +1 -0
  188. universal_mcp/applications/rocketlane/app.py +4346 -0
  189. universal_mcp/applications/semanticscholar/README.md +25 -0
  190. universal_mcp/applications/semanticscholar/__init__.py +1 -0
  191. universal_mcp/applications/semanticscholar/app.py +482 -0
  192. universal_mcp/applications/semrush/README.md +44 -0
  193. universal_mcp/applications/semrush/__init__.py +1 -0
  194. universal_mcp/applications/semrush/app.py +2081 -0
  195. universal_mcp/applications/sendgrid/README.md +362 -0
  196. universal_mcp/applications/sendgrid/__init__.py +1 -0
  197. universal_mcp/applications/sendgrid/app.py +9752 -0
  198. universal_mcp/applications/sentry/README.md +186 -0
  199. universal_mcp/applications/sentry/__init__.py +1 -0
  200. universal_mcp/applications/sentry/app.py +7471 -0
  201. universal_mcp/applications/serpapi/README.md +14 -0
  202. universal_mcp/applications/serpapi/__init__.py +1 -0
  203. universal_mcp/applications/serpapi/app.py +293 -0
  204. universal_mcp/applications/sharepoint/README.md +0 -0
  205. universal_mcp/applications/sharepoint/__init__.py +1 -0
  206. universal_mcp/applications/sharepoint/app.py +215 -0
  207. universal_mcp/applications/shopify/README.md +321 -0
  208. universal_mcp/applications/shopify/__init__.py +1 -0
  209. universal_mcp/applications/shopify/app.py +15392 -0
  210. universal_mcp/applications/shortcut/README.md +128 -0
  211. universal_mcp/applications/shortcut/__init__.py +1 -0
  212. universal_mcp/applications/shortcut/app.py +4478 -0
  213. universal_mcp/applications/slack/README.md +0 -0
  214. universal_mcp/applications/slack/__init__.py +1 -0
  215. universal_mcp/applications/slack/app.py +570 -0
  216. universal_mcp/applications/spotify/README.md +91 -0
  217. universal_mcp/applications/spotify/__init__.py +1 -0
  218. universal_mcp/applications/spotify/app.py +2526 -0
  219. universal_mcp/applications/supabase/README.md +87 -0
  220. universal_mcp/applications/supabase/__init__.py +1 -0
  221. universal_mcp/applications/supabase/app.py +2970 -0
  222. universal_mcp/applications/tavily/README.md +12 -0
  223. universal_mcp/applications/tavily/__init__.py +1 -0
  224. universal_mcp/applications/tavily/app.py +51 -0
  225. universal_mcp/applications/trello/README.md +266 -0
  226. universal_mcp/applications/trello/__init__.py +1 -0
  227. universal_mcp/applications/trello/app.py +10875 -0
  228. universal_mcp/applications/twillo/README.md +0 -0
  229. universal_mcp/applications/twillo/__init__.py +1 -0
  230. universal_mcp/applications/twillo/app.py +269 -0
  231. universal_mcp/applications/twitter/README.md +100 -0
  232. universal_mcp/applications/twitter/__init__.py +1 -0
  233. universal_mcp/applications/twitter/api_segments/__init__.py +0 -0
  234. universal_mcp/applications/twitter/api_segments/api_segment_base.py +51 -0
  235. universal_mcp/applications/twitter/api_segments/compliance_api.py +122 -0
  236. universal_mcp/applications/twitter/api_segments/dm_conversations_api.py +255 -0
  237. universal_mcp/applications/twitter/api_segments/dm_events_api.py +140 -0
  238. universal_mcp/applications/twitter/api_segments/likes_api.py +159 -0
  239. universal_mcp/applications/twitter/api_segments/lists_api.py +395 -0
  240. universal_mcp/applications/twitter/api_segments/openapi_json_api.py +34 -0
  241. universal_mcp/applications/twitter/api_segments/spaces_api.py +309 -0
  242. universal_mcp/applications/twitter/api_segments/trends_api.py +40 -0
  243. universal_mcp/applications/twitter/api_segments/tweets_api.py +1403 -0
  244. universal_mcp/applications/twitter/api_segments/usage_api.py +40 -0
  245. universal_mcp/applications/twitter/api_segments/users_api.py +1498 -0
  246. universal_mcp/applications/twitter/app.py +46 -0
  247. universal_mcp/applications/unipile/README.md +28 -0
  248. universal_mcp/applications/unipile/__init__.py +1 -0
  249. universal_mcp/applications/unipile/app.py +829 -0
  250. universal_mcp/applications/whatsapp/README.md +23 -0
  251. universal_mcp/applications/whatsapp/__init__.py +1 -0
  252. universal_mcp/applications/whatsapp/app.py +595 -0
  253. universal_mcp/applications/whatsapp-business/README.md +34 -0
  254. universal_mcp/applications/whatsapp-business/__init__.py +1 -0
  255. universal_mcp/applications/whatsapp-business/app.py +1065 -0
  256. universal_mcp/applications/wrike/README.md +46 -0
  257. universal_mcp/applications/wrike/__init__.py +1 -0
  258. universal_mcp/applications/wrike/app.py +1583 -0
  259. universal_mcp/applications/youtube/README.md +57 -0
  260. universal_mcp/applications/youtube/__init__.py +1 -0
  261. universal_mcp/applications/youtube/app.py +1696 -0
  262. universal_mcp/applications/zenquotes/README.md +12 -0
  263. universal_mcp/applications/zenquotes/__init__.py +1 -0
  264. universal_mcp/applications/zenquotes/app.py +31 -0
  265. universal_mcp_applications-0.1.1.dist-info/METADATA +172 -0
  266. universal_mcp_applications-0.1.1.dist-info/RECORD +268 -0
  267. universal_mcp_applications-0.1.1.dist-info/WHEEL +4 -0
  268. universal_mcp_applications-0.1.1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,881 @@
1
+ from typing import Any
2
+
3
+ from universal_mcp.applications.application import APIApplication
4
+ from universal_mcp.exceptions import NotAuthorizedError, ToolError
5
+ from universal_mcp.integrations import Integration
6
+
7
+ import resend
8
+
9
+
10
+ class ResendApp(APIApplication):
11
+ def __init__(self, integration: Integration, **kwargs: Any) -> None:
12
+ super().__init__(name="resend", integration=integration, **kwargs)
13
+ self._api_key = None
14
+
15
+ @property
16
+ def api_key(self) -> str:
17
+ if self._api_key is None:
18
+ if not self.integration:
19
+ raise NotAuthorizedError("Resend integration not configured.")
20
+ credentials = self.integration.get_credentials()
21
+ api_key = (
22
+ credentials.get("api_key")
23
+ or credentials.get("API_KEY")
24
+ or credentials.get("apiKey")
25
+ )
26
+ if not api_key:
27
+ raise NotAuthorizedError("Resend API key not found in credentials.")
28
+ self._api_key = api_key
29
+ resend.api_key = self._api_key
30
+ return self._api_key
31
+
32
+ def send_email(
33
+ self,
34
+ from_email: str,
35
+ to_emails: list[str],
36
+ subject: str,
37
+ text: str,
38
+ ) -> dict[str, Any]:
39
+ """
40
+ Sends an email to specified recipients using the Resend API.
41
+
42
+ Args:
43
+ from_email: The email address to send the email from in this format:- Ankit <ankit@agentr.dev>
44
+ to_emails: A list of email addresses to send the email to.
45
+ subject: The subject of the email.
46
+ text: The text content of the email.
47
+
48
+ Returns:
49
+ A dictionary containing the response from the Resend API.
50
+
51
+ Raises:
52
+ ToolError: If the email fails to send due to an API error.
53
+
54
+ Tags:
55
+ send, email, api, communication, important
56
+ """
57
+ self.api_key
58
+ params: resend.Emails.SendParams = {
59
+ "from": from_email,
60
+ "to": to_emails,
61
+ "subject": subject,
62
+ "text": text,
63
+ }
64
+ try:
65
+ email = resend.Emails.send(params)
66
+ return email
67
+ except Exception as e:
68
+ raise ToolError(f"Failed to send email: {e}")
69
+
70
+ def send_batch_emails(
71
+ self,
72
+ emails: list[dict[str, Any]],
73
+ ) -> dict[str, Any]:
74
+ """
75
+ Sends a batch of emails using the Resend API.
76
+
77
+ Args:
78
+ emails: A list of dictionaries containing parameters for individual emails, such as `from`, `to`, `subject`, `html`, and `text`.
79
+
80
+ Returns:
81
+ A dictionary containing the response from the Resend API.
82
+
83
+ Raises:
84
+ ToolError: If the batch email sending fails or if the number of emails is not between 1 and 100.
85
+
86
+ Tags:
87
+ batch, send, emails, resend-api
88
+ """
89
+ self.api_key
90
+ if not 1 <= len(emails) <= 100:
91
+ raise ToolError(
92
+ "The number of emails in a batch must be between 1 and 100."
93
+ )
94
+ params: list[resend.Emails.SendParams] = emails
95
+ try:
96
+ sent_emails_response = resend.Batch.send(params)
97
+ return sent_emails_response
98
+ except Exception as e:
99
+ raise ToolError(f"Failed to send batch emails: {e}")
100
+
101
+ def get_email(self, email_id: str) -> dict[str, Any]:
102
+ """
103
+ Retrieves a single email by its specified ID.
104
+
105
+ Args:
106
+ email_id: The unique identifier of the email to retrieve.
107
+
108
+ Returns:
109
+ A dictionary containing the details of the retrieved email.
110
+
111
+ Raises:
112
+ ToolError: Raised if the retrieval of the email fails due to an internal error.
113
+
114
+ Tags:
115
+ retrieve, email, management
116
+ """
117
+ self.api_key
118
+ try:
119
+ email = resend.Emails.get(email_id=email_id)
120
+ return email
121
+ except Exception as e:
122
+ raise ToolError(f"Failed to retrieve email: {e}")
123
+
124
+ def update_scheduled_email(
125
+ self, email_id: str, scheduled_at: str
126
+ ) -> dict[str, Any]:
127
+ """
128
+ Updates the scheduling of an email to a new time.
129
+
130
+ Args:
131
+ email_id: The ID of the email to update.
132
+ scheduled_at: The new scheduled time in ISO 8601 format.
133
+
134
+ Returns:
135
+ A dictionary containing the response from the Resend API.
136
+
137
+ Raises:
138
+ ToolError: If updating the scheduled email fails.
139
+
140
+ Tags:
141
+ update, email, async_job, management
142
+ """
143
+ self.api_key
144
+ params: resend.Emails.UpdateParams = {
145
+ "id": email_id,
146
+ "scheduled_at": scheduled_at,
147
+ }
148
+ try:
149
+ response = resend.Emails.update(params=params)
150
+ return response
151
+ except Exception as e:
152
+ raise ToolError(f"Failed to update scheduled email: {e}")
153
+
154
+ def cancel_scheduled_email(self, email_id: str) -> dict[str, Any]:
155
+ """
156
+ Cancels a scheduled email using the provided email ID.
157
+
158
+ Args:
159
+ email_id: The ID of the scheduled email to cancel.
160
+
161
+ Returns:
162
+ A dictionary containing the response from the Resend API.
163
+
164
+ Raises:
165
+ ToolError: If canceling the scheduled email fails.
166
+
167
+ Tags:
168
+ cancel, email, management
169
+ """
170
+ self.api_key
171
+ try:
172
+ response = resend.Emails.cancel(email_id=email_id)
173
+ return response
174
+ except Exception as e:
175
+ raise ToolError(f"Failed to cancel scheduled email: {e}")
176
+
177
+ def create_domain(self, name: str) -> dict[str, Any]:
178
+ """
179
+ Creates a new domain with the specified name.
180
+
181
+ Args:
182
+ name: The name of the domain to create (e.g., 'example.com')
183
+
184
+ Returns:
185
+ A dictionary containing the created domain object and its details.
186
+
187
+ Raises:
188
+ ToolError: If the domain creation fails due to API errors or invalid input.
189
+
190
+ Tags:
191
+ create, domain, management, api, batch, important
192
+ """
193
+ self.api_key
194
+ params: resend.Domains.CreateParams = {"name": name}
195
+ try:
196
+ domain = resend.Domains.create(params)
197
+ return domain
198
+ except Exception as e:
199
+ raise ToolError(f"Failed to create domain: {e}")
200
+
201
+ def get_domain(self, domain_id: str) -> dict[str, Any]:
202
+ """
203
+ Retrieves a single domain by its ID.
204
+
205
+ Args:
206
+ domain_id: The ID of the domain to retrieve.
207
+
208
+ Returns:
209
+ A dictionary containing the domain object.
210
+
211
+ Raises:
212
+ ToolError: Raised if the domain retrieval fails.
213
+
214
+ Tags:
215
+ retrieve, domain, management
216
+ """
217
+ self.api_key
218
+ try:
219
+ domain = resend.Domains.get(domain_id=domain_id)
220
+ return domain
221
+ except Exception as e:
222
+ raise ToolError(f"Failed to retrieve domain: {e}")
223
+
224
+ def verify_domain(self, domain_id: str) -> dict[str, Any]:
225
+ """
226
+ Verifies an existing domain using the provided domain ID.
227
+
228
+ Args:
229
+ domain_id: The ID of the domain to verify.
230
+
231
+ Returns:
232
+ A dictionary containing the response from the domain verification API.
233
+
234
+ Raises:
235
+ ToolError: If the domain verification process fails.
236
+
237
+ Tags:
238
+ verify, domain
239
+ """
240
+ self.api_key
241
+ try:
242
+ response = resend.Domains.verify(domain_id=domain_id)
243
+ return response
244
+ except Exception as e:
245
+ raise ToolError(f"Failed to verify domain: {e}")
246
+
247
+ def update_domain(
248
+ self,
249
+ domain_id: str,
250
+ open_tracking: bool | None = None,
251
+ click_tracking: bool | None = None,
252
+ tls: str | None = None,
253
+ ) -> dict[str, Any]:
254
+ """
255
+ Updates an existing domain's settings regarding open tracking, click tracking, and TLS enforcement.
256
+
257
+ Args:
258
+ domain_id: The ID of the domain to update.
259
+ open_tracking: Enable or disable open tracking.
260
+ click_tracking: Enable or disable click tracking.
261
+ tls: The TLS enforcement policy (enforced or opportunistic).
262
+
263
+ Returns:
264
+ A dictionary containing the updated domain object.
265
+
266
+ Raises:
267
+ ToolError: Raised if updating the domain fails.
268
+
269
+ Tags:
270
+ update, domain, management
271
+ """
272
+ self.api_key
273
+ params: resend.Domains.UpdateParams = {"id": domain_id}
274
+ if open_tracking is not None:
275
+ params["open_tracking"] = open_tracking
276
+ if click_tracking is not None:
277
+ params["click_tracking"] = click_tracking
278
+ if tls is not None:
279
+ params["tls"] = tls
280
+ try:
281
+ updated_domain = resend.Domains.update(params)
282
+ return updated_domain
283
+ except Exception as e:
284
+ raise ToolError(f"Failed to update domain: {e}")
285
+
286
+ def list_domains(self) -> list[dict[str, Any]]:
287
+ """
288
+ Retrieves a list of all domains for the authenticated user.
289
+
290
+ Returns:
291
+ A list of dictionaries, each representing a domain.
292
+
293
+ Raises:
294
+ ToolError: If listing the domains fails.
295
+
296
+ Tags:
297
+ list, domains, important, management
298
+ """
299
+ self.api_key
300
+ try:
301
+ domains = resend.Domains.list()
302
+ return domains
303
+ except Exception as e:
304
+ raise ToolError(f"Failed to list domains: {e}")
305
+
306
+ def remove_domain(self, domain_id: str) -> dict[str, Any]:
307
+ """
308
+ Removes an existing domain by its ID using the Resend API.
309
+
310
+ Args:
311
+ domain_id: The unique identifier of the domain to be removed.
312
+
313
+ Returns:
314
+ A dictionary containing the response from the Resend API after attempting to remove the domain.
315
+
316
+ Raises:
317
+ ToolError: Raised if the operation to remove the domain fails, including if the API call encounters an error.
318
+
319
+ Tags:
320
+ remove, management, api, domain
321
+ """
322
+ self.api_key
323
+ try:
324
+ response = resend.Domains.remove(domain_id=domain_id)
325
+ return response
326
+ except Exception as e:
327
+ raise ToolError(f"Failed to remove domain: {e}")
328
+
329
+ def create_api_key(self, name: str) -> dict[str, Any]:
330
+ """
331
+ Creates a new API key for authenticating with Resend.
332
+
333
+ Args:
334
+ name: The name of the API key (e.g., 'Production').
335
+
336
+ Returns:
337
+ A dictionary containing the new API key object.
338
+
339
+ Raises:
340
+ ToolError: Raised if API key creation fails.
341
+
342
+ Tags:
343
+ create, api-key, authentication
344
+ """
345
+ self.api_key
346
+ params: resend.ApiKeys.CreateParams = {"name": name}
347
+ try:
348
+ api_key_obj = resend.ApiKeys.create(params)
349
+ return api_key_obj
350
+ except Exception as e:
351
+ raise ToolError(f"Failed to create API key: {e}")
352
+
353
+ def list_api_keys(self) -> list[dict[str, Any]]:
354
+ """
355
+ Retrieves a list of all API keys available through the resend service.
356
+
357
+ Args:
358
+ None: This function takes no arguments.
359
+
360
+ Returns:
361
+ List of dictionaries, each representing an API key with associated details.
362
+
363
+ Raises:
364
+ ToolError: If there is a failure when attempting to list the API keys, typically due to an underlying exception from the resend API.
365
+
366
+ Tags:
367
+ list, api, important
368
+ """
369
+ self.api_key
370
+ try:
371
+ keys = resend.ApiKeys.list()
372
+ return keys
373
+ except Exception as e:
374
+ raise ToolError(f"Failed to list API keys: {e}")
375
+
376
+ def remove_api_key(self, api_key_id: str) -> dict[str, Any]:
377
+ """
378
+ Removes an existing API key using the specified key ID.
379
+
380
+ Args:
381
+ api_key_id: The ID of the API key to remove.
382
+
383
+ Returns:
384
+ A dictionary containing the response from the Resend API after removing the API key.
385
+
386
+ Raises:
387
+ ToolError: Raised if removing the API key fails, including any underlying errors.
388
+
389
+ Tags:
390
+ remove, api-key, management
391
+ """
392
+ self.api_key
393
+ try:
394
+ response = resend.ApiKeys.remove(api_key_id=api_key_id)
395
+ return response
396
+ except Exception as e:
397
+ raise ToolError(f"Failed to remove API key: {e}")
398
+
399
+ def create_broadcast(
400
+ self,
401
+ audience_id: str,
402
+ from_email: str,
403
+ subject: str,
404
+ html: str,
405
+ ) -> dict[str, Any]:
406
+ """
407
+ Creates a new broadcast to send to a specified audience.
408
+
409
+ Args:
410
+ audience_id: The ID of the audience to send the broadcast to.
411
+ from_email: The sender's email address.
412
+ subject: The subject line of the broadcast.
413
+ html: The HTML content of the broadcast. Use {{{...}}} for merge tags.
414
+
415
+ Returns:
416
+ A dictionary containing the created broadcast object.
417
+
418
+ Raises:
419
+ ToolError: Raised if creating the broadcast fails due to an underlying exception.
420
+
421
+ Tags:
422
+ broadcast, email, important
423
+ """
424
+ self.api_key
425
+ params: resend.Broadcasts.CreateParams = {
426
+ "audience_id": audience_id,
427
+ "from": from_email,
428
+ "subject": subject,
429
+ "html": html,
430
+ }
431
+ try:
432
+ broadcast = resend.Broadcasts.create(params)
433
+ return broadcast
434
+ except Exception as e:
435
+ raise ToolError(f"Failed to create broadcast: {e}")
436
+
437
+ def get_broadcast(self, broadcast_id: str) -> dict[str, Any]:
438
+ """
439
+ Retrieves a single broadcast by its ID.
440
+
441
+ Args:
442
+ broadcast_id: The ID of the broadcast to retrieve.
443
+
444
+ Returns:
445
+ A dictionary containing the broadcast object.
446
+
447
+ Raises:
448
+ ToolError: Raised if retrieving the broadcast fails.
449
+
450
+ Tags:
451
+ retrieve, broadcast
452
+ """
453
+ self.api_key
454
+ try:
455
+ broadcast = resend.Broadcasts.get(id=broadcast_id)
456
+ return broadcast
457
+ except Exception as e:
458
+ raise ToolError(f"Failed to retrieve broadcast: {e}")
459
+
460
+ def update_broadcast(
461
+ self,
462
+ broadcast_id: str,
463
+ html: str | None = None,
464
+ subject: str | None = None,
465
+ ) -> dict[str, Any]:
466
+ """
467
+ Updates a broadcast by modifying its HTML content and/or subject line.
468
+
469
+ Args:
470
+ broadcast_id: The ID of the broadcast to update.
471
+ html: The new HTML content for the broadcast.
472
+ subject: The new subject line for the broadcast.
473
+
474
+ Returns:
475
+ A dictionary containing the updated broadcast object.
476
+
477
+ Raises:
478
+ ToolError: Raised if updating the broadcast fails or no update fields are provided.
479
+
480
+ Tags:
481
+ update, management, broadcast, api
482
+ """
483
+ self.api_key
484
+ params: resend.Broadcasts.UpdateParams = {"id": broadcast_id}
485
+ if html is not None:
486
+ params["html"] = html
487
+ if subject is not None:
488
+ params["subject"] = subject
489
+ if len(params) == 1:
490
+ raise ToolError(
491
+ "At least one field (e.g., html, subject) must be provided for the update."
492
+ )
493
+ try:
494
+ updated_broadcast = resend.Broadcasts.update(params)
495
+ return updated_broadcast
496
+ except Exception as e:
497
+ raise ToolError(f"Failed to update broadcast: {e}")
498
+
499
+ def send_broadcast(
500
+ self, broadcast_id: str, scheduled_at: str | None = None
501
+ ) -> dict[str, Any]:
502
+ """
503
+ Starts sending a broadcast via the API.
504
+
505
+ Args:
506
+ broadcast_id: The ID of the broadcast to send.
507
+ scheduled_at: The time to send the broadcast, e.g., 'in 1 min' or an ISO 8601 datetime.
508
+
509
+ Returns:
510
+ A dictionary containing the response from the Resend API.
511
+
512
+ Raises:
513
+ ToolError: If sending the broadcast fails.
514
+
515
+ Tags:
516
+ broadcast, send, api, management
517
+ """
518
+ self.api_key
519
+ params: resend.Broadcasts.SendParams = {"broadcast_id": broadcast_id}
520
+ if scheduled_at:
521
+ params["scheduled_at"] = scheduled_at
522
+ try:
523
+ response = resend.Broadcasts.send(params)
524
+ return response
525
+ except Exception as e:
526
+ raise ToolError(f"Failed to send broadcast: {e}")
527
+
528
+ def remove_broadcast(self, broadcast_id: str) -> dict[str, Any]:
529
+ """
530
+ Removes an existing broadcast with 'draft' status.
531
+
532
+ Args:
533
+ broadcast_id: The ID of the broadcast to remove.
534
+
535
+ Returns:
536
+ A dictionary containing the response from the Resend API.
537
+
538
+ Raises:
539
+ ToolError: If removing the broadcast fails.
540
+
541
+ Tags:
542
+ remove, broadcast, api-management, draft-status
543
+ """
544
+ self.api_key
545
+ try:
546
+ response = resend.Broadcasts.remove(id=broadcast_id)
547
+ return response
548
+ except Exception as e:
549
+ raise ToolError(f"Failed to remove broadcast: {e}")
550
+
551
+ def list_broadcasts(self) -> list[dict[str, Any]]:
552
+ """
553
+ Retrieves a list of all available broadcasts using the configured API key.
554
+
555
+ Returns:
556
+ A list of dictionaries, each representing a broadcast with its attributes.
557
+
558
+ Raises:
559
+ ToolError: If listing broadcasts fails due to a connection, API, or other retrieval error.
560
+
561
+ Tags:
562
+ list, broadcast, api, management, important
563
+ """
564
+ self.api_key
565
+ try:
566
+ broadcasts = resend.Broadcasts.list()
567
+ return broadcasts
568
+ except Exception as e:
569
+ raise ToolError(f"Failed to list broadcasts: {e}")
570
+
571
+ def create_audience(self, name: str) -> dict[str, Any]:
572
+ """
573
+ Creates a new audience (a list of contacts) with the specified name.
574
+
575
+ Args:
576
+ name: The name of the audience (e.g., "Registered Users").
577
+
578
+ Returns:
579
+ A dictionary containing the created audience object.
580
+
581
+ Raises:
582
+ ToolError: If creating the audience fails due to an underlying error.
583
+
584
+ Tags:
585
+ create, audience, management, important
586
+ """
587
+ self.api_key
588
+ params: resend.Audiences.CreateParams = {"name": name}
589
+ try:
590
+ audience = resend.Audiences.create(params)
591
+ return audience
592
+ except Exception as e:
593
+ raise ToolError(f"Failed to create audience: {e}")
594
+
595
+ def get_audience(self, audience_id: str) -> dict[str, Any]:
596
+ """
597
+ Retrieves a single audience object from the API using the specified audience ID.
598
+
599
+ Args:
600
+ audience_id: The unique identifier of the audience to retrieve.
601
+
602
+ Returns:
603
+ A dictionary containing all data for the requested audience object.
604
+
605
+ Raises:
606
+ ToolError: If retrieving the audience from the API fails, with a message describing the error.
607
+
608
+ Tags:
609
+ fetch, audience, management, api
610
+ """
611
+ self.api_key
612
+ try:
613
+ audience = resend.Audiences.get(id=audience_id)
614
+ return audience
615
+ except Exception as e:
616
+ raise ToolError(f"Failed to retrieve audience: {e}")
617
+
618
+ def remove_audience(self, audience_id: str) -> dict[str, Any]:
619
+ """
620
+ Removes an existing audience using the provided audience ID and returns the API response.
621
+
622
+ Args:
623
+ audience_id: The unique identifier of the audience to remove.
624
+
625
+ Returns:
626
+ A dictionary containing the response from the Resend API.
627
+
628
+ Raises:
629
+ ToolError: Raised if removing the audience fails due to API error or other issues.
630
+
631
+ Tags:
632
+ remove, audience, management, api
633
+ """
634
+ self.api_key
635
+ try:
636
+ response = resend.Audiences.remove(id=audience_id)
637
+ return response
638
+ except Exception as e:
639
+ raise ToolError(f"Failed to remove audience: {e}")
640
+
641
+ def list_audiences(self) -> list[dict[str, Any]]:
642
+ """
643
+ Retrieves a list of all audiences.
644
+
645
+ Returns:
646
+ A list of dictionaries, each representing an audience.
647
+
648
+ Raises:
649
+ ToolError: Raised if listing the audiences fails due to an internal error.
650
+
651
+ Tags:
652
+ list, audiences, management, important
653
+ """
654
+ self.api_key
655
+ try:
656
+ audiences = resend.Audiences.list()
657
+ return audiences
658
+ except Exception as e:
659
+ raise ToolError(f"Failed to list audiences: {e}")
660
+
661
+ def create_contact(
662
+ self,
663
+ audience_id: str,
664
+ email: str,
665
+ first_name: str | None = None,
666
+ last_name: str | None = None,
667
+ unsubscribed: bool = False,
668
+ ) -> dict[str, Any]:
669
+ """
670
+ Creates a contact within a specific audience.
671
+
672
+ Args:
673
+ audience_id: The ID of the audience to add the contact to.
674
+ email: The email address of the contact.
675
+ first_name: The contact's first name.
676
+ last_name: The contact's last name.
677
+ unsubscribed: The contact's subscription status.
678
+
679
+ Returns:
680
+ A dictionary containing the created contact's ID.
681
+
682
+ Raises:
683
+ ToolError: Raised if creating the contact fails.
684
+
685
+ Tags:
686
+ create, contact, management, important
687
+ """
688
+ self.api_key
689
+ params: resend.Contacts.CreateParams = {
690
+ "audience_id": audience_id,
691
+ "email": email,
692
+ "unsubscribed": unsubscribed,
693
+ }
694
+ if first_name:
695
+ params["first_name"] = first_name
696
+ if last_name:
697
+ params["last_name"] = last_name
698
+ try:
699
+ contact = resend.Contacts.create(params)
700
+ return contact
701
+ except Exception as e:
702
+ raise ToolError(f"Failed to create contact: {e}")
703
+
704
+ def get_contact(
705
+ self, audience_id: str, contact_id: str | None = None, email: str | None = None
706
+ ) -> dict[str, Any]:
707
+ """
708
+ Retrieves a single contact from an audience by providing either a unique contact ID or an email address, ensuring exactly one identifier is given.
709
+
710
+ Args:
711
+ audience_id: The ID of the audience in which to search for the contact.
712
+ contact_id: The unique ID of the contact, if available. Exactly one of 'contact_id' or 'email' must be provided.
713
+ email: The email address of the contact, if available. Exactly one of 'contact_id' or 'email' must be provided.
714
+
715
+ Returns:
716
+ A dictionary containing the retrieved contact object, with details such as ID, email, and other contact attributes.
717
+
718
+ Raises:
719
+ ToolError: Raised if neither 'contact_id' nor 'email' is provided, if both are provided (ambiguous identifier), or if retrieval from the API fails.
720
+
721
+ Tags:
722
+ retrieve, contact, audience, management, api
723
+ """
724
+ self.api_key
725
+ if not (contact_id or email) or (contact_id and email):
726
+ raise ToolError("You must provide exactly one of 'contact_id' or 'email'.")
727
+ params = {"audience_id": audience_id}
728
+ if contact_id:
729
+ params["id"] = contact_id
730
+ if email:
731
+ params["email"] = email
732
+ try:
733
+ contact = resend.Contacts.get(**params)
734
+ return contact
735
+ except Exception as e:
736
+ raise ToolError(f"Failed to retrieve contact: {e}")
737
+
738
+ def update_contact(
739
+ self,
740
+ audience_id: str,
741
+ contact_id: str | None = None,
742
+ email: str | None = None,
743
+ first_name: str | None = None,
744
+ last_name: str | None = None,
745
+ unsubscribed: bool | None = None,
746
+ ) -> dict[str, Any]:
747
+ """
748
+ Updates an existing contact, identified by ID or email, within a specified audience.
749
+
750
+ Args:
751
+ audience_id: The ID of the audience containing the contact.
752
+ contact_id: The ID of the contact to update.
753
+ email: The email of the contact to update.
754
+ first_name: The new first name for the contact.
755
+ last_name: The new last name for the contact.
756
+ unsubscribed: The new subscription status for the contact.
757
+
758
+ Returns:
759
+ A dictionary containing the response from the Resend API.
760
+
761
+ Raises:
762
+ ToolError: Raised if the update fails, if an identifier is missing, or if no update fields are provided.
763
+
764
+ Tags:
765
+ update, contact, management
766
+ """
767
+ self.api_key
768
+ if not (contact_id or email) or (contact_id and email):
769
+ raise ToolError(
770
+ "You must provide exactly one of 'contact_id' or 'email' to identify the contact."
771
+ )
772
+ params: resend.Contacts.UpdateParams = {"audience_id": audience_id}
773
+ if contact_id:
774
+ params["id"] = contact_id
775
+ if email:
776
+ params["email"] = email
777
+ if first_name is not None:
778
+ params["first_name"] = first_name
779
+ if last_name is not None:
780
+ params["last_name"] = last_name
781
+ if unsubscribed is not None:
782
+ params["unsubscribed"] = unsubscribed
783
+ if len(params) <= 2: # Only audience_id and one identifier
784
+ raise ToolError(
785
+ "At least one field to update (e.g., first_name, unsubscribed) must be provided."
786
+ )
787
+ try:
788
+ response = resend.Contacts.update(params)
789
+ return response
790
+ except Exception as e:
791
+ raise ToolError(f"Failed to update contact: {e}")
792
+
793
+ def remove_contact(
794
+ self, audience_id: str, contact_id: str | None = None, email: str | None = None
795
+ ) -> dict[str, Any]:
796
+ """
797
+ Removes a contact from an audience, identified by ID or email.
798
+
799
+ Args:
800
+ audience_id: The ID of the audience.
801
+ contact_id: The ID of the contact to remove.
802
+ email: The email of the contact to remove.
803
+
804
+ Returns:
805
+ A dictionary containing the response from the Resend API.
806
+
807
+ Raises:
808
+ ToolError: If contact removal fails, or if the contact identifier is missing or ambiguous.
809
+
810
+ Tags:
811
+ remove, contact-management, api-call
812
+ """
813
+ self.api_key
814
+ if not (contact_id or email) or (contact_id and email):
815
+ raise ToolError("You must provide exactly one of 'contact_id' or 'email'.")
816
+ params = {"audience_id": audience_id}
817
+ if contact_id:
818
+ params["id"] = contact_id
819
+ if email:
820
+ params["email"] = email
821
+ try:
822
+ response = resend.Contacts.remove(**params)
823
+ return response
824
+ except Exception as e:
825
+ raise ToolError(f"Failed to remove contact: {e}")
826
+
827
+ def list_contacts(self, audience_id: str) -> list[dict[str, Any]]:
828
+ """
829
+ Lists all contacts from a specified audience.
830
+
831
+ Args:
832
+ audience_id: The ID of the audience whose contacts you want to list.
833
+
834
+ Returns:
835
+ A list of dictionaries, each representing a contact in the audience.
836
+
837
+ Raises:
838
+ ToolError: Raised if listing the contacts fails.
839
+
840
+ Tags:
841
+ list, contacts, management, important
842
+ """
843
+ self.api_key
844
+ try:
845
+ contacts = resend.Contacts.list(audience_id=audience_id)
846
+ return contacts
847
+ except Exception as e:
848
+ raise ToolError(f"Failed to list contacts: {e}")
849
+
850
+ def list_tools(self) -> list[callable]:
851
+ return [
852
+ self.send_email,
853
+ self.send_batch_emails,
854
+ self.get_email,
855
+ self.update_scheduled_email,
856
+ self.cancel_scheduled_email,
857
+ self.create_domain,
858
+ self.get_domain,
859
+ self.verify_domain,
860
+ self.update_domain,
861
+ self.list_domains,
862
+ self.remove_domain,
863
+ self.create_api_key,
864
+ self.list_api_keys,
865
+ self.remove_api_key,
866
+ self.create_broadcast,
867
+ self.get_broadcast,
868
+ self.update_broadcast,
869
+ self.send_broadcast,
870
+ self.remove_broadcast,
871
+ self.list_broadcasts,
872
+ self.create_audience,
873
+ self.get_audience,
874
+ self.remove_audience,
875
+ self.list_audiences,
876
+ self.create_contact,
877
+ self.get_contact,
878
+ self.update_contact,
879
+ self.remove_contact,
880
+ self.list_contacts,
881
+ ]