universal-mcp-applications 0.1.30rc1__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 (106) 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 +23 -4
  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 +280 -93
  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.30rc1.dist-info → universal_mcp_applications-0.1.36rc1.dist-info}/METADATA +2 -2
  103. {universal_mcp_applications-0.1.30rc1.dist-info → universal_mcp_applications-0.1.36rc1.dist-info}/RECORD +105 -106
  104. universal_mcp/applications/scraper/scraper_testers.py +0 -17
  105. {universal_mcp_applications-0.1.30rc1.dist-info → universal_mcp_applications-0.1.36rc1.dist-info}/WHEEL +0 -0
  106. {universal_mcp_applications-0.1.30rc1.dist-info → universal_mcp_applications-0.1.36rc1.dist-info}/licenses/LICENSE +0 -0
@@ -1,7 +1,6 @@
1
1
  import base64
2
2
  import json
3
3
  from typing import Any
4
-
5
4
  import boto3
6
5
  from botocore.exceptions import ClientError
7
6
  from universal_mcp.applications.application import BaseApplication
@@ -36,10 +35,8 @@ class AwsS3App(BaseApplication):
36
35
  if not self._client:
37
36
  credentials = self.integration.get_credentials()
38
37
  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"),
38
+ "aws_access_key_id": credentials.get("access_key_id") or credentials.get("username"),
39
+ "aws_secret_access_key": credentials.get("secret_access_key") or credentials.get("password"),
43
40
  "region_name": credentials.get("region"),
44
41
  }
45
42
  self._client = boto3.client("s3", **credentials)
@@ -55,7 +52,7 @@ class AwsS3App(BaseApplication):
55
52
  response = self.client.list_buckets()
56
53
  return [bucket["Name"] for bucket in response["Buckets"]]
57
54
 
58
- def create_bucket(self, bucket_name: str, region: str | None = None) -> bool:
55
+ async def create_bucket(self, bucket_name: str, region: str | None = None) -> bool:
59
56
  """
60
57
  Creates a new Amazon S3 bucket with a specified name and optional region. Returns `True` upon successful creation, or `False` if an AWS client error, such as a naming conflict or permission issue, occurs.
61
58
 
@@ -70,17 +67,14 @@ class AwsS3App(BaseApplication):
70
67
  """
71
68
  try:
72
69
  if region:
73
- self.client.create_bucket(
74
- Bucket=bucket_name,
75
- CreateBucketConfiguration={"LocationConstraint": region},
76
- )
70
+ self.client.create_bucket(Bucket=bucket_name, CreateBucketConfiguration={"LocationConstraint": region})
77
71
  else:
78
72
  self.client.create_bucket(Bucket=bucket_name)
79
73
  return True
80
74
  except ClientError:
81
75
  return False
82
76
 
83
- def delete_bucket(self, bucket_name: str) -> bool:
77
+ async def delete_bucket(self, bucket_name: str) -> bool:
84
78
  """
85
79
  Deletes a specified S3 bucket. The operation requires the bucket to be empty to succeed. It returns `True` if the bucket is successfully deleted and `False` if an error occurs, such as the bucket not being found or containing objects.
86
80
 
@@ -98,7 +92,7 @@ class AwsS3App(BaseApplication):
98
92
  except ClientError:
99
93
  return False
100
94
 
101
- def get_bucket_policy(self, bucket_name: str) -> dict[str, Any]:
95
+ async def get_bucket_policy(self, bucket_name: str) -> dict[str, Any]:
102
96
  """
103
97
  Retrieves the IAM resource policy for a specified S3 bucket, parsing the JSON string into a dictionary. If the operation fails due to permissions or a non-existent policy, it returns an error dictionary. This complements `put_bucket_policy`, which applies a new policy.
104
98
 
@@ -116,7 +110,7 @@ class AwsS3App(BaseApplication):
116
110
  except ClientError as e:
117
111
  return {"error": str(e)}
118
112
 
119
- def set_bucket_policy(self, bucket_name: str, policy: dict[str, Any]) -> bool:
113
+ async def set_bucket_policy(self, bucket_name: str, policy: dict[str, Any]) -> bool:
120
114
  """
121
115
  Applies or replaces the access policy for a specified S3 bucket. The function accepts the policy as a dictionary, converts it to JSON, and assigns it to the bucket. This write operation is the counterpart to `get_bucket_policy` and returns `True` on success.
122
116
 
@@ -135,9 +129,7 @@ class AwsS3App(BaseApplication):
135
129
  except ClientError:
136
130
  return False
137
131
 
138
- def list_subdirectories(
139
- self, bucket_name: str, prefix: str | None = None
140
- ) -> list[str]:
132
+ async def list_subdirectories(self, bucket_name: str, prefix: str | None = None) -> list[str]:
141
133
  """
142
134
  Lists immediate subdirectories (common prefixes) within an S3 bucket. If a prefix is provided, it returns subdirectories under that path; otherwise, it lists top-level directories. This function specifically lists folders, distinguishing it from `list_objects`, which lists files.
143
135
 
@@ -157,16 +149,13 @@ class AwsS3App(BaseApplication):
157
149
  operation_parameters["Delimiter"] = "/"
158
150
  else:
159
151
  operation_parameters["Delimiter"] = "/"
160
-
161
152
  prefixes = []
162
153
  for page in paginator.paginate(**operation_parameters):
163
154
  for cp in page.get("CommonPrefixes", []):
164
155
  prefixes.append(cp.get("Prefix"))
165
156
  return prefixes
166
157
 
167
- def create_prefix(
168
- self, bucket_name: str, prefix_name: str, parent_prefix: str | None = None
169
- ) -> bool:
158
+ async def create_prefix(self, bucket_name: str, prefix_name: str, parent_prefix: str | None = None) -> bool:
170
159
  """
171
160
  Creates a prefix (folder) in an S3 bucket, optionally nested under a parent prefix. It simulates a directory by creating a zero-byte object with a key ending in a slash ('/'), distinguishing it from put_object, which uploads content.
172
161
 
@@ -187,7 +176,7 @@ class AwsS3App(BaseApplication):
187
176
  self.client.put_object(Bucket=bucket_name, Key=key)
188
177
  return True
189
178
 
190
- def list_objects(self, bucket_name: str, prefix: str) -> list[dict[str, Any]]:
179
+ async def list_objects(self, bucket_name: str, prefix: str) -> list[dict[str, Any]]:
191
180
  """
192
181
  Paginates through and lists all objects within a specified S3 bucket prefix. It returns a curated list of metadata for each object (key, name, size, last modified), excluding folder placeholders. This function specifically lists files, distinguishing it from `list_prefixes` which lists folders.
193
182
 
@@ -218,9 +207,7 @@ class AwsS3App(BaseApplication):
218
207
  )
219
208
  return objects
220
209
 
221
- def put_text_object(
222
- self, bucket_name: str, prefix: str, object_name: str, content: str
223
- ) -> bool:
210
+ async def put_text_object(self, bucket_name: str, prefix: str, object_name: str, content: str) -> bool:
224
211
  """
225
212
  Uploads string content to create an object in a specified S3 bucket and prefix. The content is UTF-8 encoded before being written. This method is for text, distinguishing it from `put_object_from_base64` which handles binary data.
226
213
 
@@ -236,14 +223,10 @@ class AwsS3App(BaseApplication):
236
223
  important
237
224
  """
238
225
  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
- )
226
+ self.client.put_object(Bucket=bucket_name, Key=key, Body=content.encode("utf-8"))
242
227
  return True
243
228
 
244
- def put_object_from_base64(
245
- self, bucket_name: str, prefix: str, object_name: str, base64_content: str
246
- ) -> bool:
229
+ async def put_object_from_base64(self, bucket_name: str, prefix: str, object_name: str, base64_content: str) -> bool:
247
230
  """
248
231
  Decodes a base64 string into binary data and uploads it as an object to a specified S3 location. This method is designed for binary files, differentiating it from `put_object` which handles plain text content. Returns true on success.
249
232
 
@@ -266,7 +249,7 @@ class AwsS3App(BaseApplication):
266
249
  except Exception:
267
250
  return False
268
251
 
269
- def get_object_with_content(self, bucket_name: str, key: str) -> dict[str, Any]:
252
+ async def get_object_with_content(self, bucket_name: str, key: str) -> dict[str, Any]:
270
253
  """
271
254
  Retrieves an S3 object's content. It decodes text files as UTF-8 or encodes binary files as base64 based on the object's key. This function downloads the full object body, unlike `get_object_metadata` which only fetches metadata without content, returning the content, name, size, and type.
272
255
 
@@ -282,24 +265,15 @@ class AwsS3App(BaseApplication):
282
265
  try:
283
266
  obj = self.client.get_object(Bucket=bucket_name, Key=key)
284
267
  content = obj["Body"].read()
285
- is_text_file = key.lower().endswith(
286
- (".txt", ".csv", ".json", ".xml", ".html", ".md", ".js", ".css", ".py")
287
- )
268
+ is_text_file = key.lower().endswith((".txt", ".csv", ".json", ".xml", ".html", ".md", ".js", ".css", ".py"))
288
269
  content_dict = (
289
- {"content": content.decode("utf-8")}
290
- if is_text_file
291
- else {"content_base64": base64.b64encode(content).decode("ascii")}
270
+ {"content": content.decode("utf-8")} if is_text_file else {"content_base64": base64.b64encode(content).decode("ascii")}
292
271
  )
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
- }
272
+ return {"name": key.split("/")[-1], "content_type": "text" if is_text_file else "binary", **content_dict, "size": len(content)}
299
273
  except ClientError as e:
300
274
  return {"error": str(e)}
301
275
 
302
- def get_object_metadata(self, bucket_name: str, key: str) -> dict[str, Any]:
276
+ async def get_object_metadata(self, bucket_name: str, key: str) -> dict[str, Any]:
303
277
  """
304
278
  Efficiently retrieves metadata for a specified S3 object, such as size and last modified date, without downloading its content. This function uses a HEAD request, making it faster than `get_object_content` for accessing object properties. Returns a dictionary of metadata or an error message on failure.
305
279
 
@@ -318,9 +292,7 @@ class AwsS3App(BaseApplication):
318
292
  "key": key,
319
293
  "name": key.split("/")[-1],
320
294
  "size": response.get("ContentLength", 0),
321
- "last_modified": response.get("LastModified", "").isoformat()
322
- if response.get("LastModified")
323
- else "",
295
+ "last_modified": response.get("LastModified", "").isoformat() if response.get("LastModified") else "",
324
296
  "content_type": response.get("ContentType", ""),
325
297
  "etag": response.get("ETag", ""),
326
298
  "metadata": response.get("Metadata", {}),
@@ -328,9 +300,7 @@ class AwsS3App(BaseApplication):
328
300
  except ClientError as e:
329
301
  return {"error": str(e)}
330
302
 
331
- def copy_object(
332
- self, source_bucket: str, source_key: str, dest_bucket: str, dest_key: str
333
- ) -> bool:
303
+ async def copy_object(self, source_bucket: str, source_key: str, dest_bucket: str, dest_key: str) -> bool:
334
304
  """
335
305
  Copies an S3 object from a specified source location to a destination, which can be in the same or a different bucket. Unlike `move_object`, the original object remains at the source after the copy operation. It returns `True` for a successful operation.
336
306
 
@@ -347,16 +317,12 @@ class AwsS3App(BaseApplication):
347
317
  """
348
318
  try:
349
319
  copy_source = {"Bucket": source_bucket, "Key": source_key}
350
- self.client.copy_object(
351
- CopySource=copy_source, Bucket=dest_bucket, Key=dest_key
352
- )
320
+ self.client.copy_object(CopySource=copy_source, Bucket=dest_bucket, Key=dest_key)
353
321
  return True
354
322
  except ClientError:
355
323
  return False
356
324
 
357
- def move_object(
358
- self, source_bucket: str, source_key: str, dest_bucket: str, dest_key: str
359
- ) -> bool:
325
+ async def move_object(self, source_bucket: str, source_key: str, dest_bucket: str, dest_key: str) -> bool:
360
326
  """
361
327
  Moves an S3 object from a source to a destination. This is achieved by first copying the object to the new location and subsequently deleting the original. The move can occur within the same bucket or between different ones, returning `True` on success.
362
328
 
@@ -375,7 +341,7 @@ class AwsS3App(BaseApplication):
375
341
  return self.delete_object(source_bucket, source_key)
376
342
  return False
377
343
 
378
- def delete_single_object(self, bucket_name: str, key: str) -> bool:
344
+ async def delete_single_object(self, bucket_name: str, key: str) -> bool:
379
345
  """
380
346
  Deletes a single, specified object from an S3 bucket using its key. Returns `True` if successful, otherwise `False`. For bulk deletions in a single request, the `delete_objects` function should be used instead.
381
347
 
@@ -394,7 +360,7 @@ class AwsS3App(BaseApplication):
394
360
  except ClientError:
395
361
  return False
396
362
 
397
- def delete_objects(self, bucket_name: str, keys: list[str]) -> dict[str, Any]:
363
+ async def delete_objects(self, bucket_name: str, keys: list[str]) -> dict[str, Any]:
398
364
  """
399
365
  Performs a bulk deletion of objects from a specified S3 bucket in a single request. Given a list of keys, it returns a dictionary detailing successful deletions and any errors. This method is the batch-processing counterpart to the singular `delete_object` function, designed for efficiency.
400
366
 
@@ -409,9 +375,7 @@ class AwsS3App(BaseApplication):
409
375
  """
410
376
  try:
411
377
  delete_dict = {"Objects": [{"Key": key} for key in keys]}
412
- response = self.client.delete_objects(
413
- Bucket=bucket_name, Delete=delete_dict
414
- )
378
+ response = self.client.delete_objects(Bucket=bucket_name, Delete=delete_dict)
415
379
  return {
416
380
  "deleted": [obj.get("Key") for obj in response.get("Deleted", [])],
417
381
  "errors": [obj for obj in response.get("Errors", [])],
@@ -419,13 +383,7 @@ class AwsS3App(BaseApplication):
419
383
  except ClientError as e:
420
384
  return {"error": str(e)}
421
385
 
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:
386
+ async def generate_presigned_url(self, bucket_name: str, key: str, expiration: int = 3600, http_method: str = "GET") -> str:
429
387
  """
430
388
  Generates a temporary, secure URL for a specific S3 object. This URL grants time-limited permissions for actions like GET, PUT, or DELETE, expiring after a defined period. It allows object access without sharing permanent AWS credentials.
431
389
 
@@ -441,28 +399,16 @@ class AwsS3App(BaseApplication):
441
399
  important
442
400
  """
443
401
  try:
444
- method_map = {
445
- "GET": "get_object",
446
- "PUT": "put_object",
447
- "DELETE": "delete_object",
448
- }
449
-
402
+ method_map = {"GET": "get_object", "PUT": "put_object", "DELETE": "delete_object"}
450
403
  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,
404
+ method_map.get(http_method.upper(), "get_object"), Params={"Bucket": bucket_name, "Key": key}, ExpiresIn=expiration
454
405
  )
455
406
  return response
456
407
  except ClientError as e:
457
408
  return f"Error: {str(e)}"
458
409
 
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,
410
+ async def search_objects(
411
+ self, bucket_name: str, prefix: str = "", name_pattern: str = "", min_size: int | None = None, max_size: int | None = None
466
412
  ) -> list[dict[str, Any]]:
467
413
  """
468
414
  Filters objects within an S3 bucket and prefix based on a name pattern and size range. It retrieves all objects via `list_objects` and then applies the specified criteria client-side, returning a refined list of matching objects and their metadata.
@@ -481,23 +427,17 @@ class AwsS3App(BaseApplication):
481
427
  """
482
428
  all_objects = self.list_objects(bucket_name, prefix)
483
429
  filtered_objects = []
484
-
485
430
  for obj in all_objects:
486
- # Filter by name pattern
487
431
  if name_pattern and name_pattern.lower() not in obj["name"].lower():
488
432
  continue
489
-
490
- # Filter by size
491
433
  if min_size and obj["size"] < min_size:
492
434
  continue
493
435
  if max_size and obj["size"] > max_size:
494
436
  continue
495
-
496
437
  filtered_objects.append(obj)
497
-
498
438
  return filtered_objects
499
439
 
500
- def get_storage_summary(self, bucket_name: str, prefix: str = "") -> dict[str, Any]:
440
+ async def get_storage_summary(self, bucket_name: str, prefix: str = "") -> dict[str, Any]:
501
441
  """
502
442
  Calculates and returns statistics for an S3 bucket or prefix. The result includes the total number of objects, their combined size in bytes, and a human-readable string representation of the size (e.g., '15.2 MB').
503
443
 
@@ -511,10 +451,8 @@ class AwsS3App(BaseApplication):
511
451
  important
512
452
  """
513
453
  objects = self.list_objects(bucket_name, prefix)
514
- total_size = sum(obj["size"] for obj in objects)
454
+ total_size = sum((obj["size"] for obj in objects))
515
455
  object_count = len(objects)
516
-
517
- # Convert to human-readable format
518
456
  for unit in ["B", "KB", "MB", "GB", "TB"]:
519
457
  if total_size < 1024.0:
520
458
  human_size = f"{total_size:.2f} {unit}"
@@ -522,12 +460,7 @@ class AwsS3App(BaseApplication):
522
460
  total_size /= 1024.0
523
461
  else:
524
462
  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
- }
463
+ return {"total_size_bytes": sum((obj["size"] for obj in objects)), "human_readable_size": human_size, "object_count": object_count}
531
464
 
532
465
  def list_tools(self):
533
466
  return [