universal-mcp-applications 0.1.21__py3-none-any.whl → 0.1.22__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 (78) hide show
  1. universal_mcp/applications/BEST_PRACTICES.md +166 -0
  2. universal_mcp/applications/airtable/app.py +0 -1
  3. universal_mcp/applications/apollo/app.py +0 -1
  4. universal_mcp/applications/aws_s3/app.py +40 -39
  5. universal_mcp/applications/browser_use/README.md +1 -0
  6. universal_mcp/applications/browser_use/__init__.py +0 -0
  7. universal_mcp/applications/browser_use/app.py +76 -0
  8. universal_mcp/applications/calendly/app.py +125 -125
  9. universal_mcp/applications/canva/app.py +95 -99
  10. universal_mcp/applications/confluence/app.py +0 -1
  11. universal_mcp/applications/contentful/app.py +4 -5
  12. universal_mcp/applications/domain_checker/app.py +11 -15
  13. universal_mcp/applications/e2b/app.py +4 -4
  14. universal_mcp/applications/elevenlabs/app.py +18 -15
  15. universal_mcp/applications/exa/app.py +17 -17
  16. universal_mcp/applications/falai/app.py +28 -29
  17. universal_mcp/applications/file_system/app.py +9 -9
  18. universal_mcp/applications/firecrawl/app.py +36 -36
  19. universal_mcp/applications/fireflies/app.py +55 -56
  20. universal_mcp/applications/fpl/app.py +49 -50
  21. universal_mcp/applications/ghost_content/app.py +0 -1
  22. universal_mcp/applications/github/app.py +41 -43
  23. universal_mcp/applications/google_calendar/app.py +40 -39
  24. universal_mcp/applications/google_docs/app.py +56 -56
  25. universal_mcp/applications/google_drive/app.py +212 -215
  26. universal_mcp/applications/google_gemini/app.py +1 -5
  27. universal_mcp/applications/google_mail/app.py +91 -90
  28. universal_mcp/applications/google_searchconsole/app.py +29 -29
  29. universal_mcp/applications/google_sheet/app.py +115 -115
  30. universal_mcp/applications/hashnode/README.md +6 -3
  31. universal_mcp/applications/hashnode/app.py +174 -25
  32. universal_mcp/applications/http_tools/app.py +10 -11
  33. universal_mcp/applications/hubspot/__init__.py +1 -1
  34. universal_mcp/applications/hubspot/api_segments/api_segment_base.py +36 -7
  35. universal_mcp/applications/hubspot/api_segments/crm_api.py +368 -368
  36. universal_mcp/applications/hubspot/api_segments/marketing_api.py +115 -115
  37. universal_mcp/applications/hubspot/app.py +131 -72
  38. universal_mcp/applications/jira/app.py +0 -1
  39. universal_mcp/applications/linkedin/app.py +20 -20
  40. universal_mcp/applications/markitdown/app.py +10 -5
  41. universal_mcp/applications/ms_teams/app.py +123 -123
  42. universal_mcp/applications/openai/app.py +40 -39
  43. universal_mcp/applications/outlook/app.py +32 -32
  44. universal_mcp/applications/perplexity/app.py +4 -4
  45. universal_mcp/applications/reddit/app.py +69 -70
  46. universal_mcp/applications/resend/app.py +116 -117
  47. universal_mcp/applications/rocketlane/app.py +0 -1
  48. universal_mcp/applications/scraper/__init__.py +1 -1
  49. universal_mcp/applications/scraper/app.py +80 -81
  50. universal_mcp/applications/serpapi/app.py +14 -14
  51. universal_mcp/applications/sharepoint/app.py +19 -20
  52. universal_mcp/applications/shopify/app.py +0 -1
  53. universal_mcp/applications/slack/app.py +48 -48
  54. universal_mcp/applications/tavily/app.py +4 -4
  55. universal_mcp/applications/twitter/api_segments/compliance_api.py +13 -15
  56. universal_mcp/applications/twitter/api_segments/dm_conversations_api.py +20 -20
  57. universal_mcp/applications/twitter/api_segments/dm_events_api.py +12 -12
  58. universal_mcp/applications/twitter/api_segments/likes_api.py +12 -12
  59. universal_mcp/applications/twitter/api_segments/lists_api.py +37 -39
  60. universal_mcp/applications/twitter/api_segments/spaces_api.py +24 -24
  61. universal_mcp/applications/twitter/api_segments/trends_api.py +4 -4
  62. universal_mcp/applications/twitter/api_segments/tweets_api.py +105 -105
  63. universal_mcp/applications/twitter/api_segments/usage_api.py +4 -4
  64. universal_mcp/applications/twitter/api_segments/users_api.py +136 -136
  65. universal_mcp/applications/twitter/app.py +6 -2
  66. universal_mcp/applications/unipile/app.py +90 -97
  67. universal_mcp/applications/whatsapp/app.py +53 -54
  68. universal_mcp/applications/whatsapp/audio.py +39 -35
  69. universal_mcp/applications/whatsapp/whatsapp.py +176 -154
  70. universal_mcp/applications/whatsapp_business/app.py +92 -92
  71. universal_mcp/applications/yahoo_finance/app.py +105 -63
  72. universal_mcp/applications/youtube/app.py +193 -196
  73. universal_mcp/applications/zenquotes/__init__.py +2 -0
  74. universal_mcp/applications/zenquotes/app.py +3 -3
  75. {universal_mcp_applications-0.1.21.dist-info → universal_mcp_applications-0.1.22.dist-info}/METADATA +2 -1
  76. {universal_mcp_applications-0.1.21.dist-info → universal_mcp_applications-0.1.22.dist-info}/RECORD +78 -74
  77. {universal_mcp_applications-0.1.21.dist-info → universal_mcp_applications-0.1.22.dist-info}/WHEEL +0 -0
  78. {universal_mcp_applications-0.1.21.dist-info → universal_mcp_applications-0.1.22.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,10 @@
1
1
  from typing import Any
2
2
 
3
3
  import requests
4
+ from universal_mcp.agentr.integration import AgentrIntegration
5
+ from universal_mcp.applications.application import BaseApplication
6
+ from universal_mcp.exceptions import NotAuthorizedError
7
+
4
8
  from universal_mcp.applications.whatsapp.whatsapp import (
5
9
  WHATSAPP_API_BASE_URL,
6
10
  )
@@ -41,10 +45,6 @@ from universal_mcp.applications.whatsapp.whatsapp import (
41
45
  send_message as whatsapp_send_message,
42
46
  )
43
47
 
44
- from universal_mcp.applications.application import BaseApplication
45
- from universal_mcp.exceptions import NotAuthorizedError
46
- from universal_mcp.agentr.integration import AgentrIntegration
47
-
48
48
 
49
49
  class WhatsappApp(BaseApplication):
50
50
  """
@@ -63,11 +63,10 @@ class WhatsappApp(BaseApplication):
63
63
  """
64
64
  if not self.integration:
65
65
  raise ValueError("No integration available to get API key from")
66
-
67
66
 
68
67
  try:
69
68
  headers = self.integration.client.client.headers
70
- api_key = headers.get('X-API-KEY')
69
+ api_key = headers.get("X-API-KEY")
71
70
  if api_key:
72
71
  return api_key
73
72
  else:
@@ -160,16 +159,16 @@ class WhatsappApp(BaseApplication):
160
159
  ) -> list[dict[str, Any]]:
161
160
  """
162
161
  Searches for WhatsApp contacts by name or phone number. This function takes a query string, handles user authentication, and calls the underlying API to find and return a list of matching contacts. It serves as the primary method to look up contact information within the application.
163
-
162
+
164
163
  Args:
165
164
  query (string): Search term to match against contact names or phone numbers
166
-
165
+
167
166
  Returns:
168
167
  List[Dict[str, Any]]: Retrieved collection
169
-
168
+
170
169
  Raises:
171
170
  ValueError: Raised when required parameters are missing.
172
-
171
+
173
172
  Tags:
174
173
  whatsapp.contacts, important
175
174
  """
@@ -198,7 +197,7 @@ class WhatsappApp(BaseApplication):
198
197
  ) -> list[dict[str, Any]]:
199
198
  """
200
199
  Searches for and retrieves a paginated list of WhatsApp messages using various filters like date, sender, chat, or content query. It can optionally include surrounding contextual messages for each result, unlike `get_message_context` which targets a single message ID.
201
-
200
+
202
201
  Args:
203
202
  after (string): Optional ISO-8601 formatted string to only return messages after this date
204
203
  before (string): Optional ISO-8601 formatted string to only return messages before this date
@@ -210,13 +209,13 @@ class WhatsappApp(BaseApplication):
210
209
  include_context (boolean): Whether to include messages before and after matches (default True)
211
210
  context_before (integer): Number of messages to include before each match (default 1)
212
211
  context_after (integer): Number of messages to include after each match (default 1)
213
-
212
+
214
213
  Returns:
215
214
  List[Dict[str, Any]]: Retrieved collection
216
-
215
+
217
216
  Raises:
218
217
  ValueError: Raised when required parameters are missing.
219
-
218
+
220
219
  Tags:
221
220
  whatsapp.messages, important
222
221
  """
@@ -249,20 +248,20 @@ class WhatsappApp(BaseApplication):
249
248
  ) -> list[dict[str, Any]]:
250
249
  """
251
250
  Retrieves a paginated list of WhatsApp chats, allowing filtering by a search query and sorting by activity or name. Unlike `get_chat`, which fetches a single known chat, this function provides broad search and discovery capabilities across multiple user conversations.
252
-
251
+
253
252
  Args:
254
253
  query (string): Optional search term to filter chats by name or JID
255
254
  limit (integer): Maximum number of chats to return (default 20)
256
255
  page (integer): Page number for pagination (default 0)
257
256
  include_last_message (boolean): Whether to include the last message in each chat (default True)
258
257
  sort_by (string): Field to sort results by, either "last_active" or "name" (default "last_active")
259
-
258
+
260
259
  Returns:
261
260
  List[Dict[str, Any]]: Retrieved collection
262
-
261
+
263
262
  Raises:
264
263
  ValueError: Raised when required parameters are missing.
265
-
264
+
266
265
  Tags:
267
266
  whatsapp.chats, important
268
267
  """
@@ -287,17 +286,17 @@ class WhatsappApp(BaseApplication):
287
286
  ) -> dict[str, Any]:
288
287
  """
289
288
  Retrieves metadata for a specific WhatsApp chat (direct or group) using its unique JID. It can optionally include the most recent message. This precise JID-based lookup distinguishes it from `get_direct_chat_by_contact`, which uses a phone number, and `list_chats`, which performs a broader search.
290
-
289
+
291
290
  Args:
292
291
  chat_jid (string): The JID of the chat to retrieve
293
292
  include_last_message (boolean): Whether to include the last message (default True)
294
-
293
+
295
294
  Returns:
296
295
  Dict[str, Any]: Retrieved chat metadata
297
-
296
+
298
297
  Raises:
299
298
  ValueError: Raised when required parameters are missing.
300
-
299
+
301
300
  Tags:
302
301
  whatsapp.chat, important
303
302
  """
@@ -317,16 +316,16 @@ class WhatsappApp(BaseApplication):
317
316
  ) -> dict[str, Any]:
318
317
  """
319
318
  Retrieves metadata for a direct (one-on-one) WhatsApp chat using a contact's phone number. Unlike `get_chat` which requires a JID, this provides a simpler way to find direct conversations. Returns a dictionary containing the chat's details, such as its JID and name.
320
-
319
+
321
320
  Args:
322
321
  sender_phone_number (string): The phone number to search for
323
-
322
+
324
323
  Returns:
325
324
  Dict[str, Any]: Retrieved chat metadata
326
-
325
+
327
326
  Raises:
328
327
  ValueError: Raised when required parameters are missing.
329
-
328
+
330
329
  Tags:
331
330
  whatsapp.chat, important
332
331
  """
@@ -348,18 +347,18 @@ class WhatsappApp(BaseApplication):
348
347
  ) -> list[dict[str, Any]]:
349
348
  """
350
349
  Retrieves a paginated list of all WhatsApp chats, including direct messages and groups, that a specific contact participates in. The contact is identified by their unique JID. This differs from `get_direct_chat_by_contact` which only finds one-on-one chats.
351
-
350
+
352
351
  Args:
353
352
  jid (string): The contact's JID to search for
354
353
  limit (integer): Maximum number of chats to return (default 20)
355
354
  page (integer): Page number for pagination (default 0)
356
-
355
+
357
356
  Returns:
358
357
  List[Dict[str, Any]]: Retrieved collection
359
-
358
+
360
359
  Raises:
361
360
  ValueError: Raised when required parameters are missing.
362
-
361
+
363
362
  Tags:
364
363
  whatsapp.contact_chats, important
365
364
  """
@@ -379,16 +378,16 @@ class WhatsappApp(BaseApplication):
379
378
  ) -> str:
380
379
  """
381
380
  Retrieves the content of the most recent message involving a specific contact, identified by their JID. It authenticates the user and returns the message directly as a string, offering a quick way to view the last communication without fetching full message objects or chat histories.
382
-
381
+
383
382
  Args:
384
383
  jid (string): The JID of the contact to search for
385
-
384
+
386
385
  Returns:
387
386
  string: Retrieved message
388
-
387
+
389
388
  Raises:
390
389
  ValueError: Raised when required parameters are missing.
391
-
390
+
392
391
  Tags:
393
392
  whatsapp.interaction, important
394
393
  """
@@ -410,18 +409,18 @@ class WhatsappApp(BaseApplication):
410
409
  ) -> dict[str, Any]:
411
410
  """
412
411
  Fetches the conversational context surrounding a specific WhatsApp message ID. It retrieves a configurable number of messages immediately preceding and following the target message. This provides a focused view of a dialogue, unlike `list_messages` which performs broader, filter-based searches.
413
-
412
+
414
413
  Args:
415
414
  message_id (string): The ID of the message to get context for
416
415
  before (integer): Number of messages to include before the target message (default 5)
417
416
  after (integer): Number of messages to include after the target message (default 5)
418
-
417
+
419
418
  Returns:
420
419
  Dict[str, Any]: Retrieved message context
421
-
420
+
422
421
  Raises:
423
422
  ValueError: Raised when required parameters are missing.
424
-
423
+
425
424
  Tags:
426
425
  whatsapp.message_context, important
427
426
  """
@@ -442,18 +441,18 @@ class WhatsappApp(BaseApplication):
442
441
  ) -> dict[str, Any]:
443
442
  """
444
443
  Authenticates the user and sends a text message to a specified WhatsApp recipient. The recipient can be an individual (via phone number) or a group (via JID). It returns a dictionary indicating the operation's success status and a corresponding message.
445
-
444
+
446
445
  Args:
447
446
  recipient (string): The recipient - either a phone number with country code but no + or other symbols,
448
447
  or a JID (e.g., "123456789@s.whatsapp.net" or a group JID like "123456789@g.us")
449
448
  message (string): The message text to send
450
-
449
+
451
450
  Returns:
452
451
  Dict[str, Any]: A dictionary containing success status and a status message
453
-
452
+
454
453
  Raises:
455
454
  ValueError: Raised when required parameters are missing.
456
-
455
+
457
456
  Tags:
458
457
  whatsapp.send_message, important
459
458
  """
@@ -480,18 +479,18 @@ class WhatsappApp(BaseApplication):
480
479
  ) -> dict[str, Any]:
481
480
  """
482
481
  Sends a media file (image, video, document, raw audio) as a standard attachment to a WhatsApp contact or group using their phone number or JID. Unlike `send_audio_message`, which creates a playable voice note, this function handles general file transfers. Returns a success status dictionary.
483
-
482
+
484
483
  Args:
485
484
  recipient (string): The recipient - either a phone number with country code but no + or other symbols,
486
485
  or a JID (e.g., "123456789@s.whatsapp.net" or a group JID like "123456789@g.us")
487
486
  media_path (string): The absolute path to the media file to send (image, video, document)
488
-
487
+
489
488
  Returns:
490
489
  Dict[str, Any]: A dictionary containing success status and a status message
491
-
490
+
492
491
  Raises:
493
492
  ValueError: Raised when required parameters are missing.
494
-
493
+
495
494
  Tags:
496
495
  whatsapp.send_file, important
497
496
  """
@@ -518,18 +517,18 @@ class WhatsappApp(BaseApplication):
518
517
  ) -> dict[str, Any]:
519
518
  """
520
519
  Sends a local audio file as a playable WhatsApp voice message, converting it to the required format. Unlike `send_file` which sends audio as a document attachment, this function formats the audio as a voice note. It can be sent to an individual contact or a group chat.
521
-
520
+
522
521
  Args:
523
522
  recipient (string): The recipient - either a phone number with country code but no + or other symbols,
524
523
  or a JID (e.g., "123456789@s.whatsapp.net" or a group JID like "123456789@g.us")
525
524
  media_path (string): The absolute path to the audio file to send (will be converted to Opus .ogg if it's not a .ogg file)
526
-
525
+
527
526
  Returns:
528
527
  Dict[str, Any]: A dictionary containing success status and a status message
529
-
528
+
530
529
  Raises:
531
530
  ValueError: Raised when required parameters are missing.
532
-
531
+
533
532
  Tags:
534
533
  whatsapp.send_audio_message, important
535
534
  """
@@ -557,17 +556,17 @@ class WhatsappApp(BaseApplication):
557
556
  ) -> dict[str, Any]:
558
557
  """
559
558
  Downloads media from a specific WhatsApp message, identified by its ID and chat JID. It saves the content to a local file and returns the file's path upon success. The function automatically handles user authentication before initiating the download.
560
-
559
+
561
560
  Args:
562
561
  message_id (string): The ID of the message containing the media
563
562
  chat_jid (string): The JID of the chat containing the message
564
-
563
+
565
564
  Returns:
566
565
  Dict[str, Any]: A dictionary containing success status, a status message, and the file path if successful
567
-
566
+
568
567
  Raises:
569
568
  ValueError: Raised when required parameters are missing.
570
-
569
+
571
570
  Tags:
572
571
  whatsapp.download_media, important
573
572
  """
@@ -2,77 +2,84 @@ import os
2
2
  import subprocess
3
3
  import tempfile
4
4
 
5
+
5
6
  def convert_to_opus_ogg(input_file, output_file=None, bitrate="32k", sample_rate=24000):
6
7
  """
7
8
  Convert an audio file to Opus format in an Ogg container.
8
-
9
+
9
10
  Args:
10
11
  input_file (str): Path to the input audio file
11
12
  output_file (str, optional): Path to save the output file. If None, replaces the
12
13
  extension of input_file with .ogg
13
14
  bitrate (str, optional): Target bitrate for Opus encoding (default: "32k")
14
15
  sample_rate (int, optional): Sample rate for output (default: 24000)
15
-
16
+
16
17
  Returns:
17
18
  str: Path to the converted file
18
-
19
+
19
20
  Raises:
20
21
  FileNotFoundError: If the input file doesn't exist
21
22
  RuntimeError: If the ffmpeg conversion fails
22
23
  """
23
24
  if not os.path.isfile(input_file):
24
25
  raise FileNotFoundError(f"Input file not found: {input_file}")
25
-
26
+
26
27
  # If no output file is specified, replace the extension with .ogg
27
28
  if output_file is None:
28
29
  output_file = os.path.splitext(input_file)[0] + ".ogg"
29
-
30
+
30
31
  # Ensure the output directory exists
31
32
  output_dir = os.path.dirname(output_file)
32
33
  if output_dir and not os.path.exists(output_dir):
33
34
  os.makedirs(output_dir)
34
-
35
+
35
36
  # Build the ffmpeg command
36
37
  cmd = [
37
38
  "ffmpeg",
38
- "-i", input_file,
39
- "-c:a", "libopus",
40
- "-b:a", bitrate,
41
- "-ar", str(sample_rate),
42
- "-application", "voip", # Optimize for voice
43
- "-vbr", "on", # Variable bitrate
44
- "-compression_level", "10", # Maximum compression
45
- "-frame_duration", "60", # 60ms frames (good for voice)
46
- "-y", # Overwrite output file if it exists
47
- output_file
39
+ "-i",
40
+ input_file,
41
+ "-c:a",
42
+ "libopus",
43
+ "-b:a",
44
+ bitrate,
45
+ "-ar",
46
+ str(sample_rate),
47
+ "-application",
48
+ "voip", # Optimize for voice
49
+ "-vbr",
50
+ "on", # Variable bitrate
51
+ "-compression_level",
52
+ "10", # Maximum compression
53
+ "-frame_duration",
54
+ "60", # 60ms frames (good for voice)
55
+ "-y", # Overwrite output file if it exists
56
+ output_file,
48
57
  ]
49
-
58
+
50
59
  try:
51
60
  # Run the ffmpeg command and capture output
52
- process = subprocess.run(
53
- cmd,
54
- stdout=subprocess.PIPE,
55
- stderr=subprocess.PIPE,
56
- text=True,
57
- check=True
61
+ subprocess.run(
62
+ cmd, capture_output=True, text=True, check=True
58
63
  )
59
64
  return output_file
60
65
  except subprocess.CalledProcessError as e:
61
- raise RuntimeError(f"Failed to convert audio. You likely need to install ffmpeg {e.stderr}")
66
+ raise RuntimeError(
67
+ f"Failed to convert audio. You likely need to install ffmpeg {e.stderr}"
68
+ )
62
69
 
63
70
 
64
71
  def convert_to_opus_ogg_temp(input_file, bitrate="32k", sample_rate=24000):
65
72
  """
66
73
  Convert an audio file to Opus format in an Ogg container and store in a temporary file.
67
-
74
+
68
75
  Args:
69
76
  input_file (str): Path to the input audio file
70
77
  bitrate (str, optional): Target bitrate for Opus encoding (default: "32k")
71
78
  sample_rate (int, optional): Sample rate for output (default: 24000)
72
-
79
+
73
80
  Returns:
74
81
  str: Path to the temporary file with the converted audio
75
-
82
+
76
83
  Raises:
77
84
  FileNotFoundError: If the input file doesn't exist
78
85
  RuntimeError: If the ffmpeg conversion fails
@@ -80,7 +87,7 @@ def convert_to_opus_ogg_temp(input_file, bitrate="32k", sample_rate=24000):
80
87
  # Create a temporary file with .ogg extension
81
88
  temp_file = tempfile.NamedTemporaryFile(suffix=".ogg", delete=False)
82
89
  temp_file.close()
83
-
90
+
84
91
  try:
85
92
  # Convert the audio
86
93
  convert_to_opus_ogg(input_file, temp_file.name, bitrate, sample_rate)
@@ -95,16 +102,13 @@ def convert_to_opus_ogg_temp(input_file, bitrate="32k", sample_rate=24000):
95
102
  if __name__ == "__main__":
96
103
  # Example usage
97
104
  import sys
98
-
105
+
99
106
  if len(sys.argv) < 2:
100
- print("Usage: python audio.py input_file [output_file]")
101
107
  sys.exit(1)
102
-
108
+
103
109
  input_file = sys.argv[1]
104
-
110
+
105
111
  try:
106
112
  result = convert_to_opus_ogg_temp(input_file)
107
- print(f"Successfully converted to: {result}")
108
- except Exception as e:
109
- print(f"Error: {e}")
113
+ except Exception:
110
114
  sys.exit(1)