universal-mcp 0.1.8rc2__py3-none-any.whl → 0.1.8rc4__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.
- universal_mcp/__init__.py +0 -2
- universal_mcp/analytics.py +75 -0
- universal_mcp/applications/ahrefs/README.md +76 -0
- universal_mcp/applications/ahrefs/__init__.py +0 -0
- universal_mcp/applications/ahrefs/app.py +2291 -0
- universal_mcp/applications/application.py +94 -5
- universal_mcp/applications/calendly/app.py +412 -171
- universal_mcp/applications/coda/README.md +133 -0
- universal_mcp/applications/coda/__init__.py +0 -0
- universal_mcp/applications/coda/app.py +3671 -0
- universal_mcp/applications/e2b/app.py +8 -35
- universal_mcp/applications/figma/README.md +74 -0
- universal_mcp/applications/figma/__init__.py +0 -0
- universal_mcp/applications/figma/app.py +1261 -0
- universal_mcp/applications/firecrawl/app.py +3 -33
- universal_mcp/applications/github/app.py +41 -42
- universal_mcp/applications/google_calendar/app.py +20 -31
- universal_mcp/applications/google_docs/app.py +21 -46
- universal_mcp/applications/google_drive/app.py +53 -76
- universal_mcp/applications/google_mail/app.py +40 -56
- universal_mcp/applications/google_sheet/app.py +43 -68
- universal_mcp/applications/markitdown/app.py +4 -4
- universal_mcp/applications/notion/app.py +93 -83
- universal_mcp/applications/perplexity/app.py +4 -38
- universal_mcp/applications/reddit/app.py +32 -32
- universal_mcp/applications/resend/app.py +4 -22
- universal_mcp/applications/serpapi/app.py +6 -32
- universal_mcp/applications/tavily/app.py +4 -24
- universal_mcp/applications/wrike/app.py +565 -237
- universal_mcp/applications/youtube/app.py +625 -183
- universal_mcp/applications/zenquotes/app.py +3 -3
- universal_mcp/exceptions.py +1 -0
- universal_mcp/integrations/__init__.py +11 -2
- universal_mcp/integrations/agentr.py +27 -4
- universal_mcp/integrations/integration.py +14 -6
- universal_mcp/logger.py +3 -56
- universal_mcp/servers/__init__.py +2 -1
- universal_mcp/servers/server.py +73 -77
- universal_mcp/stores/store.py +5 -3
- universal_mcp/tools/__init__.py +1 -1
- universal_mcp/tools/adapters.py +4 -1
- universal_mcp/tools/func_metadata.py +5 -6
- universal_mcp/tools/tools.py +108 -51
- universal_mcp/utils/docgen.py +121 -69
- universal_mcp/utils/docstring_parser.py +44 -21
- universal_mcp/utils/dump_app_tools.py +33 -23
- universal_mcp/utils/installation.py +199 -8
- universal_mcp/utils/openapi.py +121 -47
- {universal_mcp-0.1.8rc2.dist-info → universal_mcp-0.1.8rc4.dist-info}/METADATA +2 -2
- universal_mcp-0.1.8rc4.dist-info/RECORD +81 -0
- universal_mcp-0.1.8rc2.dist-info/RECORD +0 -71
- {universal_mcp-0.1.8rc2.dist-info → universal_mcp-0.1.8rc4.dist-info}/WHEEL +0 -0
- {universal_mcp-0.1.8rc2.dist-info → universal_mcp-0.1.8rc4.dist-info}/entry_points.txt +0 -0
@@ -3,6 +3,7 @@ from abc import ABC, abstractmethod
|
|
3
3
|
import httpx
|
4
4
|
from loguru import logger
|
5
5
|
|
6
|
+
from universal_mcp.analytics import analytics
|
6
7
|
from universal_mcp.exceptions import NotAuthorizedError
|
7
8
|
from universal_mcp.integrations import Integration
|
8
9
|
|
@@ -14,6 +15,8 @@ class Application(ABC):
|
|
14
15
|
|
15
16
|
def __init__(self, name: str, **kwargs):
|
16
17
|
self.name = name
|
18
|
+
logger.debug(f"Initializing Application '{name}' with kwargs: {kwargs}")
|
19
|
+
analytics.track_app_loaded(name) # Track app loading
|
17
20
|
|
18
21
|
@abstractmethod
|
19
22
|
def list_tools(self):
|
@@ -29,15 +32,58 @@ class APIApplication(Application):
|
|
29
32
|
super().__init__(name, **kwargs)
|
30
33
|
self.default_timeout = 30
|
31
34
|
self.integration = integration
|
35
|
+
logger.debug(
|
36
|
+
f"Initializing APIApplication '{name}' with integration: {integration}"
|
37
|
+
)
|
32
38
|
|
33
39
|
def _get_headers(self):
|
40
|
+
if not self.integration:
|
41
|
+
logger.debug("No integration configured, returning empty headers")
|
42
|
+
return {}
|
43
|
+
credentials = self.integration.get_credentials()
|
44
|
+
logger.debug(f"Got credentials for integration: {credentials.keys()}")
|
45
|
+
|
46
|
+
# Check if direct headers are provided
|
47
|
+
headers = credentials.get("headers")
|
48
|
+
if headers:
|
49
|
+
logger.debug("Using direct headers from credentials")
|
50
|
+
return headers
|
51
|
+
|
52
|
+
# Check if api key is provided
|
53
|
+
api_key = (
|
54
|
+
credentials.get("api_key")
|
55
|
+
or credentials.get("API_KEY")
|
56
|
+
or credentials.get("apiKey")
|
57
|
+
)
|
58
|
+
if api_key:
|
59
|
+
logger.debug("Using API key from credentials")
|
60
|
+
return {
|
61
|
+
"Authorization": f"Bearer {api_key}",
|
62
|
+
"Content-Type": "application/json",
|
63
|
+
}
|
64
|
+
|
65
|
+
# Check if access token is provided
|
66
|
+
access_token = credentials.get("access_token")
|
67
|
+
if access_token:
|
68
|
+
logger.debug("Using access token from credentials")
|
69
|
+
return {
|
70
|
+
"Authorization": f"Bearer {access_token}",
|
71
|
+
"Content-Type": "application/json",
|
72
|
+
}
|
73
|
+
logger.debug("No authentication found in credentials, returning empty headers")
|
34
74
|
return {}
|
35
75
|
|
36
76
|
def _get(self, url, params=None):
|
37
77
|
try:
|
38
78
|
headers = self._get_headers()
|
39
|
-
|
79
|
+
logger.debug(f"Making GET request to {url} with params: {params}")
|
80
|
+
response = httpx.get(
|
81
|
+
url, headers=headers, params=params, timeout=self.default_timeout
|
82
|
+
)
|
40
83
|
response.raise_for_status()
|
84
|
+
logger.debug(
|
85
|
+
f"GET request successful with status code: {response.status_code}"
|
86
|
+
)
|
41
87
|
return response
|
42
88
|
except NotAuthorizedError as e:
|
43
89
|
logger.warning(f"Authorization needed: {e.message}")
|
@@ -49,14 +95,27 @@ class APIApplication(Application):
|
|
49
95
|
def _post(self, url, data, params=None):
|
50
96
|
try:
|
51
97
|
headers = self._get_headers()
|
52
|
-
|
98
|
+
logger.debug(
|
99
|
+
f"Making POST request to {url} with params: {params} and data: {data}"
|
100
|
+
)
|
101
|
+
response = httpx.post(
|
102
|
+
url,
|
103
|
+
headers=headers,
|
104
|
+
json=data,
|
105
|
+
params=params,
|
106
|
+
timeout=self.default_timeout,
|
107
|
+
)
|
53
108
|
response.raise_for_status()
|
109
|
+
logger.debug(
|
110
|
+
f"POST request successful with status code: {response.status_code}"
|
111
|
+
)
|
54
112
|
return response
|
55
113
|
except NotAuthorizedError as e:
|
56
114
|
logger.warning(f"Authorization needed: {e.message}")
|
57
115
|
raise e
|
58
116
|
except httpx.HTTPStatusError as e:
|
59
117
|
if e.response.status_code == 429:
|
118
|
+
logger.warning(f"Rate limit exceeded for {url}")
|
60
119
|
return e.response.text or "Rate limit exceeded. Please try again later."
|
61
120
|
else:
|
62
121
|
raise e
|
@@ -67,8 +126,20 @@ class APIApplication(Application):
|
|
67
126
|
def _put(self, url, data, params=None):
|
68
127
|
try:
|
69
128
|
headers = self._get_headers()
|
70
|
-
|
129
|
+
logger.debug(
|
130
|
+
f"Making PUT request to {url} with params: {params} and data: {data}"
|
131
|
+
)
|
132
|
+
response = httpx.put(
|
133
|
+
url,
|
134
|
+
headers=headers,
|
135
|
+
json=data,
|
136
|
+
params=params,
|
137
|
+
timeout=self.default_timeout,
|
138
|
+
)
|
71
139
|
response.raise_for_status()
|
140
|
+
logger.debug(
|
141
|
+
f"PUT request successful with status code: {response.status_code}"
|
142
|
+
)
|
72
143
|
return response
|
73
144
|
except NotAuthorizedError as e:
|
74
145
|
logger.warning(f"Authorization needed: {e.message}")
|
@@ -80,8 +151,14 @@ class APIApplication(Application):
|
|
80
151
|
def _delete(self, url, params=None):
|
81
152
|
try:
|
82
153
|
headers = self._get_headers()
|
83
|
-
|
154
|
+
logger.debug(f"Making DELETE request to {url} with params: {params}")
|
155
|
+
response = httpx.delete(
|
156
|
+
url, headers=headers, params=params, timeout=self.default_timeout
|
157
|
+
)
|
84
158
|
response.raise_for_status()
|
159
|
+
logger.debug(
|
160
|
+
f"DELETE request successful with status code: {response.status_code}"
|
161
|
+
)
|
85
162
|
return response
|
86
163
|
except NotAuthorizedError as e:
|
87
164
|
logger.warning(f"Authorization needed: {e.message}")
|
@@ -93,8 +170,20 @@ class APIApplication(Application):
|
|
93
170
|
def _patch(self, url, data, params=None):
|
94
171
|
try:
|
95
172
|
headers = self._get_headers()
|
96
|
-
|
173
|
+
logger.debug(
|
174
|
+
f"Making PATCH request to {url} with params: {params} and data: {data}"
|
175
|
+
)
|
176
|
+
response = httpx.patch(
|
177
|
+
url,
|
178
|
+
headers=headers,
|
179
|
+
json=data,
|
180
|
+
params=params,
|
181
|
+
timeout=self.default_timeout,
|
182
|
+
)
|
97
183
|
response.raise_for_status()
|
184
|
+
logger.debug(
|
185
|
+
f"PATCH request successful with status code: {response.status_code}"
|
186
|
+
)
|
98
187
|
return response
|
99
188
|
except NotAuthorizedError as e:
|
100
189
|
logger.warning(f"Authorization needed: {e.message}")
|