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
File without changes
@@ -0,0 +1 @@
1
+ from .app import AwsS3App
@@ -0,0 +1,552 @@
1
+ import base64
2
+ import json
3
+ from typing import Any
4
+
5
+ import boto3
6
+ from botocore.exceptions import ClientError
7
+ from universal_mcp.applications import BaseApplication
8
+ from universal_mcp.integrations import Integration
9
+
10
+
11
+ class AwsS3App(BaseApplication):
12
+ """
13
+ A class to interact with Amazon S3.
14
+ """
15
+
16
+ def __init__(self, integration: Integration | None = None, client=None, **kwargs):
17
+ """
18
+ Initializes the AmazonS3App.
19
+
20
+ Args:
21
+ aws_access_key_id (str, optional): AWS access key ID.
22
+ aws_secret_access_key (str, optional): AWS secret access key.
23
+ region_name (str, optional): AWS region name.
24
+ """
25
+ super().__init__(name="aws-s3", integration=integration, **kwargs)
26
+ self._client = client
27
+ self.integration = integration
28
+
29
+ @property
30
+ def client(self):
31
+ """
32
+ Gets the S3 client.
33
+ """
34
+ if not self.integration:
35
+ raise ValueError("Integration not initialized")
36
+ if not self._client:
37
+ credentials = self.integration.get_credentials()
38
+ credentials = {
39
+ "aws_access_key_id": credentials.get("access_key_id")
40
+ or credentials.get("username"),
41
+ "aws_secret_access_key": credentials.get("secret_access_key")
42
+ or credentials.get("password"),
43
+ "region_name": credentials.get("region"),
44
+ }
45
+ self._client = boto3.client("s3", **credentials)
46
+ return self._client
47
+
48
+ def list_buckets(self) -> list[str]:
49
+ """
50
+ Lists all S3 buckets.
51
+
52
+ Returns:
53
+ List[str]: A list of bucket names.
54
+ """
55
+ response = self.client.list_buckets()
56
+ return [bucket["Name"] for bucket in response["Buckets"]]
57
+
58
+ def create_bucket(self, bucket_name: str, region: str | None = None) -> bool:
59
+ """
60
+ Creates a new S3 bucket.
61
+
62
+ Args:
63
+ bucket_name (str): The name of the bucket to create.
64
+ region (str, optional): The region to create the bucket in.
65
+
66
+ Returns:
67
+ bool: True if the bucket was created successfully.
68
+ Tags:
69
+ important
70
+ """
71
+ try:
72
+ if region:
73
+ self.client.create_bucket(
74
+ Bucket=bucket_name,
75
+ CreateBucketConfiguration={"LocationConstraint": region},
76
+ )
77
+ else:
78
+ self.client.create_bucket(Bucket=bucket_name)
79
+ return True
80
+ except ClientError:
81
+ return False
82
+
83
+ def delete_bucket(self, bucket_name: str) -> bool:
84
+ """
85
+ Deletes an S3 bucket (must be empty).
86
+
87
+ Args:
88
+ bucket_name (str): The name of the bucket to delete.
89
+
90
+ Returns:
91
+ bool: True if the bucket was deleted successfully.
92
+ Tags:
93
+ important
94
+ """
95
+ try:
96
+ self.client.delete_bucket(Bucket=bucket_name)
97
+ return True
98
+ except ClientError:
99
+ return False
100
+
101
+ def get_bucket_policy(self, bucket_name: str) -> dict[str, Any]:
102
+ """
103
+ Gets the bucket policy for the specified bucket.
104
+
105
+ Args:
106
+ bucket_name (str): The name of the S3 bucket.
107
+
108
+ Returns:
109
+ Dict[str, Any]: The bucket policy as a dictionary.
110
+ Tags:
111
+ important
112
+ """
113
+ try:
114
+ response = self.client.get_bucket_policy(Bucket=bucket_name)
115
+ return json.loads(response["Policy"])
116
+ except ClientError as e:
117
+ return {"error": str(e)}
118
+
119
+ def put_bucket_policy(self, bucket_name: str, policy: dict[str, Any]) -> bool:
120
+ """
121
+ Sets the bucket policy for the specified bucket.
122
+
123
+ Args:
124
+ bucket_name (str): The name of the S3 bucket.
125
+ policy (Dict[str, Any]): The bucket policy as a dictionary.
126
+
127
+ Returns:
128
+ bool: True if the policy was set successfully.
129
+ Tags:
130
+ important
131
+ """
132
+ try:
133
+ self.client.put_bucket_policy(Bucket=bucket_name, Policy=json.dumps(policy))
134
+ return True
135
+ except ClientError:
136
+ return False
137
+
138
+ def list_prefixes(
139
+ self, bucket_name: str, prefix: str | None = None
140
+ ) -> list[str]:
141
+ """
142
+ Lists common prefixes ("folders") in the specified S3 bucket and prefix.
143
+
144
+ Args:
145
+ bucket_name (str): The name of the S3 bucket.
146
+ prefix (str, optional): The prefix to list folders under.
147
+
148
+ Returns:
149
+ List[str]: A list of folder prefixes.
150
+ Tags:
151
+ important
152
+ """
153
+ paginator = self.client.get_paginator("list_objects_v2")
154
+ operation_parameters = {"Bucket": bucket_name}
155
+ if prefix:
156
+ operation_parameters["Prefix"] = prefix
157
+ operation_parameters["Delimiter"] = "/"
158
+ else:
159
+ operation_parameters["Delimiter"] = "/"
160
+
161
+ prefixes = []
162
+ for page in paginator.paginate(**operation_parameters):
163
+ for cp in page.get("CommonPrefixes", []):
164
+ prefixes.append(cp.get("Prefix"))
165
+ return prefixes
166
+
167
+ def put_prefix(
168
+ self, bucket_name: str, prefix_name: str, parent_prefix: str | None = None
169
+ ) -> bool:
170
+ """
171
+ Creates a prefix ("folder") in the specified S3 bucket.
172
+
173
+ Args:
174
+ bucket_name (str): The name of the S3 bucket.
175
+ prefix_name (str): The name of the prefix to create.
176
+ parent_prefix (str, optional): The parent prefix (folder path).
177
+
178
+ Returns:
179
+ bool: True if the prefix was created successfully.
180
+ Tags:
181
+ important
182
+ """
183
+ if parent_prefix:
184
+ key = f"{parent_prefix.rstrip('/')}/{prefix_name}/"
185
+ else:
186
+ key = f"{prefix_name}/"
187
+ self.client.put_object(Bucket=bucket_name, Key=key)
188
+ return True
189
+
190
+ def list_objects(self, bucket_name: str, prefix: str) -> list[dict[str, Any]]:
191
+ """
192
+ Lists all objects in a specified S3 prefix.
193
+
194
+ Args:
195
+ bucket_name (str): The name of the S3 bucket.
196
+ prefix (str): The prefix (folder path) to list objects under.
197
+
198
+ Returns:
199
+ List[Dict[str, Any]]: A list of dictionaries containing object metadata.
200
+ Tags:
201
+ important
202
+ """
203
+ paginator = self.client.get_paginator("list_objects_v2")
204
+ operation_parameters = {"Bucket": bucket_name, "Prefix": prefix}
205
+ objects = []
206
+ for page in paginator.paginate(**operation_parameters):
207
+ for obj in page.get("Contents", []):
208
+ if not obj["Key"].endswith("/"):
209
+ objects.append(
210
+ {
211
+ "key": obj["Key"],
212
+ "name": obj["Key"].split("/")[-1],
213
+ "size": obj["Size"],
214
+ "last_modified": obj["LastModified"].isoformat()
215
+ if hasattr(obj["LastModified"], "isoformat")
216
+ else str(obj["LastModified"]),
217
+ }
218
+ )
219
+ return objects
220
+
221
+ def put_object(
222
+ self, bucket_name: str, prefix: str, object_name: str, content: str
223
+ ) -> bool:
224
+ """
225
+ Uploads an object to the specified S3 prefix.
226
+
227
+ Args:
228
+ bucket_name (str): The name of the S3 bucket.
229
+ prefix (str): The prefix (folder path) where the object will be created.
230
+ object_name (str): The name of the object to create.
231
+ content (str): The content to write into the object.
232
+
233
+ Returns:
234
+ bool: True if the object was created successfully.
235
+ Tags:
236
+ important
237
+ """
238
+ key = f"{prefix.rstrip('/')}/{object_name}" if prefix else object_name
239
+ self.client.put_object(
240
+ Bucket=bucket_name, Key=key, Body=content.encode("utf-8")
241
+ )
242
+ return True
243
+
244
+ def put_object_from_base64(
245
+ self, bucket_name: str, prefix: str, object_name: str, base64_content: str
246
+ ) -> bool:
247
+ """
248
+ Uploads a binary object from base64 content to the specified S3 prefix.
249
+
250
+ Args:
251
+ bucket_name (str): The name of the S3 bucket.
252
+ prefix (str): The prefix (folder path) where the object will be created.
253
+ object_name (str): The name of the object to create.
254
+ base64_content (str): The base64-encoded content to upload.
255
+
256
+ Returns:
257
+ bool: True if the object was created successfully.
258
+ Tags:
259
+ important
260
+ """
261
+ try:
262
+ key = f"{prefix.rstrip('/')}/{object_name}" if prefix else object_name
263
+ content = base64.b64decode(base64_content)
264
+ self.client.put_object(Bucket=bucket_name, Key=key, Body=content)
265
+ return True
266
+ except Exception:
267
+ return False
268
+
269
+ def get_object_content(self, bucket_name: str, key: str) -> dict[str, Any]:
270
+ """
271
+ Gets the content of a specified object.
272
+
273
+ Args:
274
+ bucket_name (str): The name of the S3 bucket.
275
+ key (str): The key (path) to the object.
276
+
277
+ Returns:
278
+ Dict[str, Any]: A dictionary containing the object's name, content type, content (as text or base64), and size.
279
+ Tags:
280
+ important
281
+ """
282
+ try:
283
+ obj = self.client.get_object(Bucket=bucket_name, Key=key)
284
+ content = obj["Body"].read()
285
+ is_text_file = key.lower().endswith(
286
+ (".txt", ".csv", ".json", ".xml", ".html", ".md", ".js", ".css", ".py")
287
+ )
288
+ content_dict = (
289
+ {"content": content.decode("utf-8")}
290
+ if is_text_file
291
+ else {"content_base64": base64.b64encode(content).decode("ascii")}
292
+ )
293
+ return {
294
+ "name": key.split("/")[-1],
295
+ "content_type": "text" if is_text_file else "binary",
296
+ **content_dict,
297
+ "size": len(content),
298
+ }
299
+ except ClientError as e:
300
+ return {"error": str(e)}
301
+
302
+ def get_object_metadata(self, bucket_name: str, key: str) -> dict[str, Any]:
303
+ """
304
+ Gets metadata for a specified object without downloading the content.
305
+
306
+ Args:
307
+ bucket_name (str): The name of the S3 bucket.
308
+ key (str): The key (path) to the object.
309
+
310
+ Returns:
311
+ Dict[str, Any]: A dictionary containing the object's metadata.
312
+ Tags:
313
+ important
314
+ """
315
+ try:
316
+ response = self.client.head_object(Bucket=bucket_name, Key=key)
317
+ return {
318
+ "key": key,
319
+ "name": key.split("/")[-1],
320
+ "size": response.get("ContentLength", 0),
321
+ "last_modified": response.get("LastModified", "").isoformat()
322
+ if response.get("LastModified")
323
+ else "",
324
+ "content_type": response.get("ContentType", ""),
325
+ "etag": response.get("ETag", ""),
326
+ "metadata": response.get("Metadata", {}),
327
+ }
328
+ except ClientError as e:
329
+ return {"error": str(e)}
330
+
331
+ def copy_object(
332
+ self, source_bucket: str, source_key: str, dest_bucket: str, dest_key: str
333
+ ) -> bool:
334
+ """
335
+ Copies an object from one location to another.
336
+
337
+ Args:
338
+ source_bucket (str): The source bucket name.
339
+ source_key (str): The source object key.
340
+ dest_bucket (str): The destination bucket name.
341
+ dest_key (str): The destination object key.
342
+
343
+ Returns:
344
+ bool: True if the object was copied successfully.
345
+ Tags:
346
+ important
347
+ """
348
+ try:
349
+ copy_source = {"Bucket": source_bucket, "Key": source_key}
350
+ self.client.copy_object(
351
+ CopySource=copy_source, Bucket=dest_bucket, Key=dest_key
352
+ )
353
+ return True
354
+ except ClientError:
355
+ return False
356
+
357
+ def move_object(
358
+ self, source_bucket: str, source_key: str, dest_bucket: str, dest_key: str
359
+ ) -> bool:
360
+ """
361
+ Moves an object from one location to another (copy then delete).
362
+
363
+ Args:
364
+ source_bucket (str): The source bucket name.
365
+ source_key (str): The source object key.
366
+ dest_bucket (str): The destination bucket name.
367
+ dest_key (str): The destination object key.
368
+
369
+ Returns:
370
+ bool: True if the object was moved successfully.
371
+ Tags:
372
+ important
373
+ """
374
+ if self.copy_object(source_bucket, source_key, dest_bucket, dest_key):
375
+ return self.delete_object(source_bucket, source_key)
376
+ return False
377
+
378
+ def delete_object(self, bucket_name: str, key: str) -> bool:
379
+ """
380
+ Deletes an object from S3.
381
+
382
+ Args:
383
+ bucket_name (str): The name of the S3 bucket.
384
+ key (str): The key (path) to the object to delete.
385
+
386
+ Returns:
387
+ bool: True if the object was deleted successfully.
388
+ Tags:
389
+ important
390
+ """
391
+ try:
392
+ self.client.delete_object(Bucket=bucket_name, Key=key)
393
+ return True
394
+ except ClientError:
395
+ return False
396
+
397
+ def delete_objects(self, bucket_name: str, keys: list[str]) -> dict[str, Any]:
398
+ """
399
+ Deletes multiple objects from S3.
400
+
401
+ Args:
402
+ bucket_name (str): The name of the S3 bucket.
403
+ keys (List[str]): List of object keys to delete.
404
+
405
+ Returns:
406
+ Dict[str, Any]: Results of the deletion operation.
407
+ Tags:
408
+ important
409
+ """
410
+ try:
411
+ delete_dict = {"Objects": [{"Key": key} for key in keys]}
412
+ response = self.client.delete_objects(
413
+ Bucket=bucket_name, Delete=delete_dict
414
+ )
415
+ return {
416
+ "deleted": [obj.get("Key") for obj in response.get("Deleted", [])],
417
+ "errors": [obj for obj in response.get("Errors", [])],
418
+ }
419
+ except ClientError as e:
420
+ return {"error": str(e)}
421
+
422
+ def generate_presigned_url(
423
+ self,
424
+ bucket_name: str,
425
+ key: str,
426
+ expiration: int = 3600,
427
+ http_method: str = "GET",
428
+ ) -> str:
429
+ """
430
+ Generates a presigned URL for accessing an S3 object.
431
+
432
+ Args:
433
+ bucket_name (str): The name of the S3 bucket.
434
+ key (str): The key (path) to the object.
435
+ expiration (int): Time in seconds for the presigned URL to remain valid (default: 3600).
436
+ http_method (str): HTTP method for the presigned URL (default: 'GET').
437
+
438
+ Returns:
439
+ str: The presigned URL or error message.
440
+ Tags:
441
+ important
442
+ """
443
+ try:
444
+ method_map = {
445
+ "GET": "get_object",
446
+ "PUT": "put_object",
447
+ "DELETE": "delete_object",
448
+ }
449
+
450
+ response = self.client.generate_presigned_url(
451
+ method_map.get(http_method.upper(), "get_object"),
452
+ Params={"Bucket": bucket_name, "Key": key},
453
+ ExpiresIn=expiration,
454
+ )
455
+ return response
456
+ except ClientError as e:
457
+ return f"Error: {str(e)}"
458
+
459
+ def search_objects(
460
+ self,
461
+ bucket_name: str,
462
+ prefix: str = "",
463
+ name_pattern: str = "",
464
+ min_size: int | None = None,
465
+ max_size: int | None = None,
466
+ ) -> list[dict[str, Any]]:
467
+ """
468
+ Searches for objects in S3 based on various criteria.
469
+
470
+ Args:
471
+ bucket_name (str): The name of the S3 bucket.
472
+ prefix (str): The prefix to search within.
473
+ name_pattern (str): Pattern to match in object names (case-insensitive).
474
+ min_size (int, optional): Minimum object size in bytes.
475
+ max_size (int, optional): Maximum object size in bytes.
476
+
477
+ Returns:
478
+ List[Dict[str, Any]]: List of matching objects with metadata.
479
+ Tags:
480
+ important
481
+ """
482
+ all_objects = self.list_objects(bucket_name, prefix)
483
+ filtered_objects = []
484
+
485
+ for obj in all_objects:
486
+ # Filter by name pattern
487
+ if name_pattern and name_pattern.lower() not in obj["name"].lower():
488
+ continue
489
+
490
+ # Filter by size
491
+ if min_size and obj["size"] < min_size:
492
+ continue
493
+ if max_size and obj["size"] > max_size:
494
+ continue
495
+
496
+ filtered_objects.append(obj)
497
+
498
+ return filtered_objects
499
+
500
+ def get_bucket_size(self, bucket_name: str, prefix: str = "") -> dict[str, Any]:
501
+ """
502
+ Calculates the total size and object count for a bucket or prefix.
503
+
504
+ Args:
505
+ bucket_name (str): The name of the S3 bucket.
506
+ prefix (str): The prefix to calculate size for (default: entire bucket).
507
+
508
+ Returns:
509
+ Dict[str, Any]: Dictionary containing total size, object count, and human-readable size.
510
+ Tags:
511
+ important
512
+ """
513
+ objects = self.list_objects(bucket_name, prefix)
514
+ total_size = sum(obj["size"] for obj in objects)
515
+ object_count = len(objects)
516
+
517
+ # Convert to human-readable format
518
+ for unit in ["B", "KB", "MB", "GB", "TB"]:
519
+ if total_size < 1024.0:
520
+ human_size = f"{total_size:.2f} {unit}"
521
+ break
522
+ total_size /= 1024.0
523
+ else:
524
+ human_size = f"{total_size:.2f} PB"
525
+
526
+ return {
527
+ "total_size_bytes": sum(obj["size"] for obj in objects),
528
+ "human_readable_size": human_size,
529
+ "object_count": object_count,
530
+ }
531
+
532
+ def list_tools(self):
533
+ return [
534
+ self.create_bucket,
535
+ self.delete_bucket,
536
+ self.get_bucket_policy,
537
+ self.put_bucket_policy,
538
+ self.list_prefixes,
539
+ self.put_prefix,
540
+ self.list_objects,
541
+ self.put_object,
542
+ self.put_object_from_base64,
543
+ self.get_object_content,
544
+ self.get_object_metadata,
545
+ self.copy_object,
546
+ self.move_object,
547
+ self.delete_object,
548
+ self.delete_objects,
549
+ self.generate_presigned_url,
550
+ self.search_objects,
551
+ self.get_bucket_size,
552
+ ]
File without changes
@@ -0,0 +1 @@
1
+ from .app import BillApp