universal-mcp-applications 0.1.33__py3-none-any.whl → 0.1.39rc16__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 (119) hide show
  1. universal_mcp/applications/BEST_PRACTICES.md +1 -1
  2. universal_mcp/applications/ahrefs/app.py +92 -238
  3. universal_mcp/applications/airtable/app.py +36 -135
  4. universal_mcp/applications/apollo/app.py +124 -477
  5. universal_mcp/applications/asana/app.py +605 -1755
  6. universal_mcp/applications/aws_s3/app.py +63 -119
  7. universal_mcp/applications/bill/app.py +644 -2055
  8. universal_mcp/applications/box/app.py +1246 -4159
  9. universal_mcp/applications/braze/app.py +410 -1476
  10. universal_mcp/applications/browser_use/README.md +15 -1
  11. universal_mcp/applications/browser_use/__init__.py +1 -0
  12. universal_mcp/applications/browser_use/app.py +91 -26
  13. universal_mcp/applications/cal_com_v2/app.py +207 -625
  14. universal_mcp/applications/calendly/app.py +103 -242
  15. universal_mcp/applications/canva/app.py +75 -140
  16. universal_mcp/applications/clickup/app.py +331 -798
  17. universal_mcp/applications/coda/app.py +240 -520
  18. universal_mcp/applications/confluence/app.py +497 -1285
  19. universal_mcp/applications/contentful/app.py +40 -155
  20. universal_mcp/applications/crustdata/app.py +44 -123
  21. universal_mcp/applications/dialpad/app.py +451 -924
  22. universal_mcp/applications/digitalocean/app.py +2071 -6082
  23. universal_mcp/applications/domain_checker/app.py +3 -54
  24. universal_mcp/applications/e2b/app.py +17 -68
  25. universal_mcp/applications/elevenlabs/README.md +27 -3
  26. universal_mcp/applications/elevenlabs/app.py +741 -74
  27. universal_mcp/applications/exa/README.md +8 -4
  28. universal_mcp/applications/exa/app.py +415 -186
  29. universal_mcp/applications/falai/README.md +5 -7
  30. universal_mcp/applications/falai/app.py +156 -232
  31. universal_mcp/applications/figma/app.py +91 -175
  32. universal_mcp/applications/file_system/app.py +2 -13
  33. universal_mcp/applications/firecrawl/app.py +198 -176
  34. universal_mcp/applications/fireflies/app.py +59 -281
  35. universal_mcp/applications/fpl/app.py +92 -529
  36. universal_mcp/applications/fpl/utils/fixtures.py +15 -49
  37. universal_mcp/applications/fpl/utils/helper.py +25 -89
  38. universal_mcp/applications/fpl/utils/league_utils.py +20 -64
  39. universal_mcp/applications/ghost_content/app.py +70 -179
  40. universal_mcp/applications/github/app.py +30 -67
  41. universal_mcp/applications/gong/app.py +142 -302
  42. universal_mcp/applications/google_calendar/app.py +26 -78
  43. universal_mcp/applications/google_docs/README.md +15 -14
  44. universal_mcp/applications/google_docs/app.py +103 -206
  45. universal_mcp/applications/google_drive/app.py +194 -793
  46. universal_mcp/applications/google_gemini/app.py +68 -59
  47. universal_mcp/applications/google_mail/README.md +1 -0
  48. universal_mcp/applications/google_mail/app.py +93 -214
  49. universal_mcp/applications/google_searchconsole/app.py +25 -58
  50. universal_mcp/applications/google_sheet/README.md +2 -1
  51. universal_mcp/applications/google_sheet/app.py +226 -624
  52. universal_mcp/applications/google_sheet/helper.py +26 -53
  53. universal_mcp/applications/hashnode/app.py +57 -269
  54. universal_mcp/applications/heygen/README.md +10 -32
  55. universal_mcp/applications/heygen/app.py +339 -811
  56. universal_mcp/applications/http_tools/app.py +10 -32
  57. universal_mcp/applications/hubspot/README.md +1 -1
  58. universal_mcp/applications/hubspot/app.py +7508 -99
  59. universal_mcp/applications/jira/app.py +2419 -8334
  60. universal_mcp/applications/klaviyo/app.py +739 -1621
  61. universal_mcp/applications/linkedin/README.md +18 -1
  62. universal_mcp/applications/linkedin/app.py +729 -251
  63. universal_mcp/applications/mailchimp/app.py +696 -1851
  64. universal_mcp/applications/markitdown/app.py +8 -20
  65. universal_mcp/applications/miro/app.py +333 -815
  66. universal_mcp/applications/ms_teams/app.py +420 -1407
  67. universal_mcp/applications/neon/app.py +144 -250
  68. universal_mcp/applications/notion/app.py +38 -53
  69. universal_mcp/applications/onedrive/app.py +26 -48
  70. universal_mcp/applications/openai/app.py +43 -166
  71. universal_mcp/applications/outlook/README.md +22 -9
  72. universal_mcp/applications/outlook/app.py +403 -141
  73. universal_mcp/applications/perplexity/README.md +2 -1
  74. universal_mcp/applications/perplexity/app.py +161 -20
  75. universal_mcp/applications/pipedrive/app.py +1021 -3331
  76. universal_mcp/applications/posthog/app.py +272 -541
  77. universal_mcp/applications/reddit/app.py +65 -164
  78. universal_mcp/applications/resend/app.py +72 -139
  79. universal_mcp/applications/retell/app.py +23 -50
  80. universal_mcp/applications/rocketlane/app.py +252 -965
  81. universal_mcp/applications/scraper/app.py +114 -142
  82. universal_mcp/applications/semanticscholar/app.py +36 -78
  83. universal_mcp/applications/semrush/app.py +44 -78
  84. universal_mcp/applications/sendgrid/app.py +826 -1576
  85. universal_mcp/applications/sentry/app.py +444 -1079
  86. universal_mcp/applications/serpapi/app.py +44 -146
  87. universal_mcp/applications/sharepoint/app.py +27 -49
  88. universal_mcp/applications/shopify/app.py +1748 -4486
  89. universal_mcp/applications/shortcut/app.py +275 -536
  90. universal_mcp/applications/slack/app.py +43 -125
  91. universal_mcp/applications/spotify/app.py +206 -405
  92. universal_mcp/applications/supabase/app.py +174 -283
  93. universal_mcp/applications/tavily/app.py +2 -2
  94. universal_mcp/applications/trello/app.py +853 -2816
  95. universal_mcp/applications/twilio/app.py +27 -62
  96. universal_mcp/applications/twitter/api_segments/compliance_api.py +4 -14
  97. universal_mcp/applications/twitter/api_segments/dm_conversations_api.py +6 -18
  98. universal_mcp/applications/twitter/api_segments/likes_api.py +1 -3
  99. universal_mcp/applications/twitter/api_segments/lists_api.py +5 -15
  100. universal_mcp/applications/twitter/api_segments/trends_api.py +1 -3
  101. universal_mcp/applications/twitter/api_segments/tweets_api.py +9 -31
  102. universal_mcp/applications/twitter/api_segments/usage_api.py +1 -5
  103. universal_mcp/applications/twitter/api_segments/users_api.py +14 -42
  104. universal_mcp/applications/whatsapp/app.py +35 -186
  105. universal_mcp/applications/whatsapp/audio.py +2 -6
  106. universal_mcp/applications/whatsapp/whatsapp.py +17 -51
  107. universal_mcp/applications/whatsapp_business/app.py +86 -299
  108. universal_mcp/applications/wrike/app.py +80 -153
  109. universal_mcp/applications/yahoo_finance/app.py +19 -65
  110. universal_mcp/applications/youtube/app.py +120 -306
  111. universal_mcp/applications/zenquotes/app.py +3 -3
  112. {universal_mcp_applications-0.1.33.dist-info → universal_mcp_applications-0.1.39rc16.dist-info}/METADATA +4 -2
  113. {universal_mcp_applications-0.1.33.dist-info → universal_mcp_applications-0.1.39rc16.dist-info}/RECORD +115 -119
  114. {universal_mcp_applications-0.1.33.dist-info → universal_mcp_applications-0.1.39rc16.dist-info}/WHEEL +1 -1
  115. universal_mcp/applications/hubspot/api_segments/__init__.py +0 -0
  116. universal_mcp/applications/hubspot/api_segments/api_segment_base.py +0 -54
  117. universal_mcp/applications/hubspot/api_segments/crm_api.py +0 -7337
  118. universal_mcp/applications/hubspot/api_segments/marketing_api.py +0 -1467
  119. {universal_mcp_applications-0.1.33.dist-info → universal_mcp_applications-0.1.39rc16.dist-info}/licenses/LICENSE +0 -0
@@ -9,4 +9,5 @@ This is automatically generated from OpenAPI schema for the PerplexityApp API.
9
9
 
10
10
  | Tool | Description |
11
11
  |------|-------------|
12
- | `answer_with_search` | Queries the Perplexity Chat Completions API for a web-search-grounded answer. It sends the user's prompt and model parameters to the `/chat/completions` endpoint, then parses the response to return the synthesized content and a list of supporting source citations, ideal for real-time information retrieval. |
12
+ | `answer_with_search` | Queries the Perplexity Chat Completions API for a web-search-grounded answer. It sends the user's prompt and model parameters to the `/chat/completions` endpoint, then parses the response to return the synthesized content and a list of supporting source citations. |
13
+ | `search` | Performs a real-time web search using Perplexity AI to answer a query with up-to-date information and citations. |
@@ -1,15 +1,74 @@
1
1
  from typing import Any, Literal
2
-
2
+ from loguru import logger
3
3
  from universal_mcp.applications.application import APIApplication
4
+ from universal_mcp.exceptions import NotAuthorizedError, ToolError
4
5
  from universal_mcp.integrations import Integration
5
6
 
7
+ try:
8
+ from perplexity import AsyncPerplexity
9
+ except ImportError:
10
+ AsyncPerplexity = None # type: ignore
11
+ logger.error("Failed to import perplexity. Please ensure 'perplexityai' is installed.")
12
+
6
13
 
7
14
  class PerplexityApp(APIApplication):
8
15
  def __init__(self, integration: Integration | None = None) -> None:
9
16
  super().__init__(name="perplexity", integration=integration)
10
- self.base_url = "https://api.perplexity.ai"
17
+ self._perplexity_api_key: str | None = None
18
+ if AsyncPerplexity is None:
19
+ logger.warning("Perplexity SDK is not available. Perplexity tools will not function.")
20
+
21
+ async def get_perplexity_api_key(self) -> str:
22
+ """
23
+ A property that lazily retrieves and caches the Perplexity API key from the configured integration.
24
+ """
25
+ if self._perplexity_api_key is None:
26
+ if not self.integration:
27
+ logger.error(f"{self.name.capitalize()} App: Integration not configured.")
28
+ raise NotAuthorizedError(f"Integration not configured for {self.name.capitalize()} App. Cannot retrieve API key.")
29
+ try:
30
+ credentials = await self.integration.get_credentials_async()
31
+ except NotAuthorizedError as e:
32
+ logger.error(f"{self.name.capitalize()} App: Authorization error when fetching credentials: {e.message}")
33
+ raise
34
+ except Exception as e:
35
+ logger.error(f"{self.name.capitalize()} App: Unexpected error when fetching credentials: {e}", exc_info=True)
36
+ raise NotAuthorizedError(f"Failed to get {self.name.capitalize()} credentials: {e}")
37
+
38
+ # Check for common key names
39
+ api_key = credentials.get("api_key") or credentials.get("API_KEY") or credentials.get("apiKey") or credentials.get("PPLX_API_KEY")
40
+
41
+ if not api_key:
42
+ logger.error(f"{self.name.capitalize()} App: API key not found in credentials.")
43
+ action_message = f"API key for {self.name.capitalize()} is missing. Please ensure it's set in the store."
44
+ if hasattr(self.integration, "authorize") and callable(self.integration.authorize):
45
+ try:
46
+ auth_details = self.integration.authorize()
47
+ if isinstance(auth_details, str):
48
+ action_message = auth_details
49
+ elif isinstance(auth_details, dict) and "url" in auth_details:
50
+ action_message = f"Please authorize via: {auth_details['url']}"
51
+ elif isinstance(auth_details, dict) and "message" in auth_details:
52
+ action_message = auth_details["message"]
53
+ except Exception as auth_e:
54
+ logger.warning(f"Could not retrieve specific authorization action for {self.name.capitalize()}: {auth_e}")
55
+ raise NotAuthorizedError(action_message)
56
+ self._perplexity_api_key = api_key
57
+ logger.info(f"{self.name.capitalize()} API Key successfully retrieved and cached.")
58
+ assert self._perplexity_api_key is not None
59
+ return self._perplexity_api_key
60
+
61
+ async def _get_client(self) -> AsyncPerplexity:
62
+ """
63
+ Initializes and returns the Perplexity client after ensuring API key is set.
64
+ """
65
+ if AsyncPerplexity is None:
66
+ logger.error("Perplexity SDK is not available.")
67
+ raise ToolError("Perplexity SDK is not installed or failed to import.")
68
+ current_api_key = await self.get_perplexity_api_key()
69
+ return AsyncPerplexity(api_key=current_api_key)
11
70
 
12
- def answer_with_search(
71
+ async def answer_with_search(
13
72
  self,
14
73
  query: str,
15
74
  model: Literal[
@@ -22,44 +81,126 @@ class PerplexityApp(APIApplication):
22
81
  ] = "sonar-pro",
23
82
  temperature: float = 1,
24
83
  system_prompt: str = "You are a helpful AI assistant that answers questions using real-time information from the web.",
84
+ response_format: dict[str, Any] | None = None,
85
+ json_schema: dict[str, Any] | None = None,
86
+ regex: str | None = None,
25
87
  ) -> dict[str, Any] | str:
26
88
  """
27
- Queries the Perplexity Chat Completions API for a web-search-grounded answer. It sends the user's prompt and model parameters to the `/chat/completions` endpoint, then parses the response to return the synthesized content and a list of supporting source citations, ideal for real-time information retrieval.
89
+ Queries the Perplexity Chat Completions API for a web-search-grounded answer. It sends the user's prompt and model parameters to the `/chat/completions` endpoint, then parses the response to return the synthesized content and a list of supporting source citations.
90
+
91
+ This method supports **Structured Outputs** using `json_schema` or `regex` to enforce specific response formats.
28
92
 
29
93
  Args:
30
- query: The search query or question to ask. For example: "What are the latest developments in AI regulation?"
94
+ query: The search query or question to ask.
31
95
  model: The Perplexity model to use.
32
- temperature: Controls randomness in the model's output. Higher values make the output more random, lower values make it more deterministic. Defaults to 1.
96
+ temperature: Controls randomness in the model's output.
33
97
  system_prompt: The initial system message to guide the model's behavior.
98
+ response_format: A dict specifying the response format (e.g. `{"type": "json_schema", ...}`).
99
+ json_schema: A dictionary for the JSON schema to enforce (shortcut for `response_format`).
100
+ regex: A regex string to enforce (shortcut for `response_format`, only available for 'sonar').
34
101
 
35
102
  Returns:
36
103
  A dictionary containing the generated 'content' (str) and a list of 'citations' (list) from the web search.
37
104
 
105
+ Examples:
106
+ **1. Financial Analysis with JSON Schema**
107
+ ```python
108
+ from pydantic import BaseModel
109
+ class FinancialMetrics(BaseModel):
110
+ company: str
111
+ revenue: float
112
+ net_income: float
113
+
114
+ app.answer_with_search(
115
+ "Analyze Apple's latest earnings",
116
+ model="sonar-pro",
117
+ json_schema={"schema": FinancialMetrics.model_json_schema()}
118
+ )
119
+ ```
120
+
121
+ **2. Extract Contact Information with Regex**
122
+ ```python
123
+ app.answer_with_search(
124
+ "Find the investor relations email for Tesla",
125
+ model="sonar",
126
+ regex=r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"
127
+ )
128
+ ```
129
+
38
130
  Raises:
39
- AuthenticationError: Raised when API authentication fails due to missing or invalid credentials.
40
- HTTPError: Raised when the API request fails or returns an error status.
131
+ AuthenticationError: Raised when API authentication fails.
132
+ HTTPError: Raised when the API request fails.
133
+ ValueError: If conflicting structured output parameters are provided.
41
134
 
42
135
  Tags:
43
- search, web, research, citations, current events, important
136
+ chat, answer, search, web, research, citations, structured_output, extraction, important
44
137
  """
45
- endpoint = f"{self.base_url}/chat/completions"
138
+ if sum(x is not None for x in [response_format, json_schema, regex]) > 1:
139
+ raise ValueError("Only one of 'response_format', 'json_schema', or 'regex' can be provided.")
140
+
141
+ if json_schema:
142
+ response_format = {"type": "json_schema", "json_schema": json_schema}
143
+ elif regex:
144
+ response_format = {"type": "regex", "regex": {"regex": regex}}
145
+
46
146
  messages = []
47
147
  if system_prompt:
48
148
  messages.append({"role": "system", "content": system_prompt})
49
149
  messages.append({"role": "user", "content": query})
50
- payload = {
150
+
151
+ client = await self._get_client()
152
+ # client.chat.completions.create supports response_format
153
+ kwargs: dict[str, Any] = {
51
154
  "model": model,
52
- "messages": messages,
155
+ "messages": messages, # type: ignore
53
156
  "temperature": temperature,
54
- # "max_tokens": 512,
55
157
  }
56
- data = self._post(endpoint, data=payload)
57
- response = data.json()
58
- content = response["choices"][0]["message"]["content"]
59
- citations = response.get("citations", [])
158
+ if response_format:
159
+ kwargs["response_format"] = response_format
160
+
161
+ response = await client.chat.completions.create(**kwargs)
162
+
163
+ # Assuming response structure matches OpenAI-like usage or Custom as per SDK docs
164
+ content = response.choices[0].message.content
165
+ citations = getattr(response, "citations", [])
60
166
  return {"content": content, "citations": citations}
61
167
 
168
+ async def search(
169
+ self,
170
+ query: str,
171
+ max_results: int = 5,
172
+ country: str = "US",
173
+ ) -> list[dict[str, Any]]:
174
+ """
175
+ Performs a real-time web search using Perplexity AI to answer a query with up-to-date information and citations.
176
+ This tool is ideal for questions about current events, facts, or any topic that requires access to the latest information from the internet.
177
+
178
+ Args:
179
+ query: The search query or question to ask.
180
+ max_results: Maximum number of results to return.
181
+ country: ISO 3166-1 alpha-2 country code for localized results.
182
+
183
+ Returns:
184
+ A list of search results.
185
+
186
+ Tags:
187
+ search, web, research, citations, current events, important
188
+ """
189
+ client = await self._get_client()
190
+ response = await client.search.create(
191
+ query=query,
192
+ max_results=max_results,
193
+ country=country,
194
+ )
195
+ results = []
196
+ for result in response.results:
197
+ results.append({
198
+ "title": result.title,
199
+ "url": result.url,
200
+ "snippet": result.snippet,
201
+ "date": result.date,
202
+ })
203
+ return results
204
+
62
205
  def list_tools(self):
63
- return [
64
- self.answer_with_search,
65
- ]
206
+ return [self.answer_with_search, self.search]