universal-mcp 0.1.8rc2__py3-none-any.whl → 0.1.8rc3__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 (45) hide show
  1. universal_mcp/__init__.py +0 -2
  2. universal_mcp/analytics.py +75 -0
  3. universal_mcp/applications/application.py +27 -5
  4. universal_mcp/applications/calendly/app.py +413 -160
  5. universal_mcp/applications/coda/README.md +133 -0
  6. universal_mcp/applications/coda/__init__.py +0 -0
  7. universal_mcp/applications/coda/app.py +3704 -0
  8. universal_mcp/applications/e2b/app.py +6 -7
  9. universal_mcp/applications/firecrawl/app.py +1 -1
  10. universal_mcp/applications/github/app.py +41 -42
  11. universal_mcp/applications/google_calendar/app.py +20 -20
  12. universal_mcp/applications/google_docs/app.py +22 -29
  13. universal_mcp/applications/google_drive/app.py +53 -59
  14. universal_mcp/applications/google_mail/app.py +40 -40
  15. universal_mcp/applications/google_sheet/app.py +44 -51
  16. universal_mcp/applications/markitdown/app.py +4 -4
  17. universal_mcp/applications/notion/app.py +93 -83
  18. universal_mcp/applications/perplexity/app.py +5 -5
  19. universal_mcp/applications/reddit/app.py +32 -32
  20. universal_mcp/applications/resend/app.py +4 -4
  21. universal_mcp/applications/serpapi/app.py +4 -4
  22. universal_mcp/applications/tavily/app.py +4 -4
  23. universal_mcp/applications/wrike/app.py +566 -226
  24. universal_mcp/applications/youtube/app.py +626 -166
  25. universal_mcp/applications/zenquotes/app.py +3 -3
  26. universal_mcp/exceptions.py +1 -0
  27. universal_mcp/integrations/__init__.py +11 -2
  28. universal_mcp/integrations/integration.py +2 -2
  29. universal_mcp/logger.py +3 -56
  30. universal_mcp/servers/__init__.py +2 -1
  31. universal_mcp/servers/server.py +76 -77
  32. universal_mcp/stores/store.py +5 -3
  33. universal_mcp/tools/__init__.py +1 -1
  34. universal_mcp/tools/adapters.py +4 -1
  35. universal_mcp/tools/func_metadata.py +5 -6
  36. universal_mcp/tools/tools.py +108 -51
  37. universal_mcp/utils/docgen.py +121 -69
  38. universal_mcp/utils/docstring_parser.py +44 -21
  39. universal_mcp/utils/dump_app_tools.py +33 -23
  40. universal_mcp/utils/openapi.py +121 -47
  41. {universal_mcp-0.1.8rc2.dist-info → universal_mcp-0.1.8rc3.dist-info}/METADATA +2 -2
  42. universal_mcp-0.1.8rc3.dist-info/RECORD +75 -0
  43. universal_mcp-0.1.8rc2.dist-info/RECORD +0 -71
  44. {universal_mcp-0.1.8rc2.dist-info → universal_mcp-0.1.8rc3.dist-info}/WHEEL +0 -0
  45. {universal_mcp-0.1.8rc2.dist-info → universal_mcp-0.1.8rc3.dist-info}/entry_points.txt +0 -0
universal_mcp/__init__.py CHANGED
@@ -1,2 +0,0 @@
1
- def hello() -> str:
2
- return "Hello from universal-mcp!"
@@ -0,0 +1,75 @@
1
+ import os
2
+ import uuid
3
+ from functools import lru_cache
4
+ from importlib.metadata import version
5
+
6
+ import posthog
7
+ from loguru import logger
8
+
9
+
10
+ class Analytics:
11
+ _instance = None
12
+
13
+ def __new__(cls):
14
+ if cls._instance is None:
15
+ cls._instance = super().__new__(cls)
16
+ cls._instance._initialize()
17
+ return cls._instance
18
+
19
+ def _initialize(self):
20
+ """Initialize the Analytics singleton"""
21
+ posthog.host = "https://us.i.posthog.com"
22
+ posthog.api_key = "phc_6HXMDi8CjfIW0l04l34L7IDkpCDeOVz9cOz1KLAHXh8"
23
+ self.enabled = os.getenv("TELEMETRY_DISABLED", "false").lower() != "true"
24
+ self.user_id = str(uuid.uuid4())[:8]
25
+
26
+ @staticmethod
27
+ @lru_cache(maxsize=1)
28
+ def get_version():
29
+ """
30
+ Get the version of the Universal MCP
31
+ """
32
+ try:
33
+ return version("universal_mcp")
34
+ except ImportError:
35
+ return "unknown"
36
+
37
+ def track_app_loaded(self, app_name: str):
38
+ """Track when the app is loaded"""
39
+ if not self.enabled:
40
+ return
41
+ try:
42
+ properties = {
43
+ "version": self.get_version(),
44
+ "app_name": app_name,
45
+ }
46
+ posthog.capture(self.user_id, "app_loaded", properties)
47
+ except Exception as e:
48
+ logger.error(f"Failed to track app_loaded event: {e}")
49
+
50
+ def track_tool_called(
51
+ self, tool_name: str, status: str, error: str = None, user_id=None
52
+ ):
53
+ """Track when a tool is called
54
+
55
+ Args:
56
+ tool_name: Name of the tool being called
57
+ status: Status of the tool call (success/error)
58
+ error: Error message if status is error
59
+ user_id: Optional user ID to track
60
+ """
61
+ if not self.enabled:
62
+ return
63
+ try:
64
+ properties = {
65
+ "tool_name": tool_name,
66
+ "status": status,
67
+ "error": error,
68
+ "version": self.get_version(),
69
+ }
70
+ posthog.capture(self.user_id, "tool_called", properties)
71
+ except Exception as e:
72
+ logger.error(f"Failed to track tool_called event: {e}")
73
+
74
+
75
+ analytics = Analytics()
@@ -36,7 +36,9 @@ class APIApplication(Application):
36
36
  def _get(self, url, params=None):
37
37
  try:
38
38
  headers = self._get_headers()
39
- response = httpx.get(url, headers=headers, params=params, timeout=self.default_timeout)
39
+ response = httpx.get(
40
+ url, headers=headers, params=params, timeout=self.default_timeout
41
+ )
40
42
  response.raise_for_status()
41
43
  return response
42
44
  except NotAuthorizedError as e:
@@ -49,7 +51,13 @@ class APIApplication(Application):
49
51
  def _post(self, url, data, params=None):
50
52
  try:
51
53
  headers = self._get_headers()
52
- response = httpx.post(url, headers=headers, json=data, params=params, timeout=self.default_timeout)
54
+ response = httpx.post(
55
+ url,
56
+ headers=headers,
57
+ json=data,
58
+ params=params,
59
+ timeout=self.default_timeout,
60
+ )
53
61
  response.raise_for_status()
54
62
  return response
55
63
  except NotAuthorizedError as e:
@@ -67,7 +75,13 @@ class APIApplication(Application):
67
75
  def _put(self, url, data, params=None):
68
76
  try:
69
77
  headers = self._get_headers()
70
- response = httpx.put(url, headers=headers, json=data, params=params, timeout=self.default_timeout)
78
+ response = httpx.put(
79
+ url,
80
+ headers=headers,
81
+ json=data,
82
+ params=params,
83
+ timeout=self.default_timeout,
84
+ )
71
85
  response.raise_for_status()
72
86
  return response
73
87
  except NotAuthorizedError as e:
@@ -80,7 +94,9 @@ class APIApplication(Application):
80
94
  def _delete(self, url, params=None):
81
95
  try:
82
96
  headers = self._get_headers()
83
- response = httpx.delete(url, headers=headers, params=params, timeout=self.default_timeout)
97
+ response = httpx.delete(
98
+ url, headers=headers, params=params, timeout=self.default_timeout
99
+ )
84
100
  response.raise_for_status()
85
101
  return response
86
102
  except NotAuthorizedError as e:
@@ -93,7 +109,13 @@ class APIApplication(Application):
93
109
  def _patch(self, url, data, params=None):
94
110
  try:
95
111
  headers = self._get_headers()
96
- response = httpx.patch(url, headers=headers, json=data, params=params, timeout=self.default_timeout)
112
+ response = httpx.patch(
113
+ url,
114
+ headers=headers,
115
+ json=data,
116
+ params=params,
117
+ timeout=self.default_timeout,
118
+ )
97
119
  response.raise_for_status()
98
120
  return response
99
121
  except NotAuthorizedError as e: