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