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.
Files changed (53) hide show
  1. universal_mcp/__init__.py +0 -2
  2. universal_mcp/analytics.py +75 -0
  3. universal_mcp/applications/ahrefs/README.md +76 -0
  4. universal_mcp/applications/ahrefs/__init__.py +0 -0
  5. universal_mcp/applications/ahrefs/app.py +2291 -0
  6. universal_mcp/applications/application.py +94 -5
  7. universal_mcp/applications/calendly/app.py +412 -171
  8. universal_mcp/applications/coda/README.md +133 -0
  9. universal_mcp/applications/coda/__init__.py +0 -0
  10. universal_mcp/applications/coda/app.py +3671 -0
  11. universal_mcp/applications/e2b/app.py +8 -35
  12. universal_mcp/applications/figma/README.md +74 -0
  13. universal_mcp/applications/figma/__init__.py +0 -0
  14. universal_mcp/applications/figma/app.py +1261 -0
  15. universal_mcp/applications/firecrawl/app.py +3 -33
  16. universal_mcp/applications/github/app.py +41 -42
  17. universal_mcp/applications/google_calendar/app.py +20 -31
  18. universal_mcp/applications/google_docs/app.py +21 -46
  19. universal_mcp/applications/google_drive/app.py +53 -76
  20. universal_mcp/applications/google_mail/app.py +40 -56
  21. universal_mcp/applications/google_sheet/app.py +43 -68
  22. universal_mcp/applications/markitdown/app.py +4 -4
  23. universal_mcp/applications/notion/app.py +93 -83
  24. universal_mcp/applications/perplexity/app.py +4 -38
  25. universal_mcp/applications/reddit/app.py +32 -32
  26. universal_mcp/applications/resend/app.py +4 -22
  27. universal_mcp/applications/serpapi/app.py +6 -32
  28. universal_mcp/applications/tavily/app.py +4 -24
  29. universal_mcp/applications/wrike/app.py +565 -237
  30. universal_mcp/applications/youtube/app.py +625 -183
  31. universal_mcp/applications/zenquotes/app.py +3 -3
  32. universal_mcp/exceptions.py +1 -0
  33. universal_mcp/integrations/__init__.py +11 -2
  34. universal_mcp/integrations/agentr.py +27 -4
  35. universal_mcp/integrations/integration.py +14 -6
  36. universal_mcp/logger.py +3 -56
  37. universal_mcp/servers/__init__.py +2 -1
  38. universal_mcp/servers/server.py +73 -77
  39. universal_mcp/stores/store.py +5 -3
  40. universal_mcp/tools/__init__.py +1 -1
  41. universal_mcp/tools/adapters.py +4 -1
  42. universal_mcp/tools/func_metadata.py +5 -6
  43. universal_mcp/tools/tools.py +108 -51
  44. universal_mcp/utils/docgen.py +121 -69
  45. universal_mcp/utils/docstring_parser.py +44 -21
  46. universal_mcp/utils/dump_app_tools.py +33 -23
  47. universal_mcp/utils/installation.py +199 -8
  48. universal_mcp/utils/openapi.py +121 -47
  49. {universal_mcp-0.1.8rc2.dist-info → universal_mcp-0.1.8rc4.dist-info}/METADATA +2 -2
  50. universal_mcp-0.1.8rc4.dist-info/RECORD +81 -0
  51. universal_mcp-0.1.8rc2.dist-info/RECORD +0 -71
  52. {universal_mcp-0.1.8rc2.dist-info → universal_mcp-0.1.8rc4.dist-info}/WHEEL +0 -0
  53. {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
- response = httpx.get(url, headers=headers, params=params, timeout=self.default_timeout)
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
- response = httpx.post(url, headers=headers, json=data, params=params, timeout=self.default_timeout)
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
- response = httpx.put(url, headers=headers, json=data, params=params, timeout=self.default_timeout)
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
- response = httpx.delete(url, headers=headers, params=params, timeout=self.default_timeout)
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
- response = httpx.patch(url, headers=headers, json=data, params=params, timeout=self.default_timeout)
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}")