universal-mcp 0.1.8rc4__py3-none-any.whl → 0.1.9rc1__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 (38) hide show
  1. universal_mcp/applications/__init__.py +7 -2
  2. universal_mcp/applications/application.py +162 -114
  3. universal_mcp/applications/cal_com_v2/README.md +175 -0
  4. universal_mcp/applications/cal_com_v2/__init__.py +0 -0
  5. universal_mcp/applications/cal_com_v2/app.py +4735 -0
  6. universal_mcp/applications/clickup/README.md +160 -0
  7. universal_mcp/applications/clickup/__init__.py +0 -0
  8. universal_mcp/applications/clickup/app.py +4359 -0
  9. universal_mcp/applications/hashnode/app.py +77 -0
  10. universal_mcp/applications/hashnode/prompt.md +21 -0
  11. universal_mcp/applications/mailchimp/README.md +306 -0
  12. universal_mcp/applications/mailchimp/__init__.py +0 -0
  13. universal_mcp/applications/mailchimp/app.py +8883 -0
  14. universal_mcp/applications/markitdown/app.py +2 -2
  15. universal_mcp/applications/perplexity/app.py +0 -1
  16. universal_mcp/applications/replicate/README.md +53 -0
  17. universal_mcp/applications/replicate/app.py +969 -0
  18. universal_mcp/applications/retell_ai/README.md +46 -0
  19. universal_mcp/applications/retell_ai/__init__.py +0 -0
  20. universal_mcp/applications/retell_ai/app.py +316 -0
  21. universal_mcp/applications/rocketlane/README.md +42 -0
  22. universal_mcp/applications/rocketlane/__init__.py +0 -0
  23. universal_mcp/applications/rocketlane/app.py +180 -0
  24. universal_mcp/applications/spotify/README.md +116 -0
  25. universal_mcp/applications/spotify/__init__.py +0 -0
  26. universal_mcp/applications/spotify/app.py +2231 -0
  27. universal_mcp/applications/supabase/README.md +112 -0
  28. universal_mcp/applications/supabase/__init__.py +0 -0
  29. universal_mcp/applications/supabase/app.py +2644 -0
  30. universal_mcp/integrations/integration.py +1 -1
  31. universal_mcp/servers/server.py +3 -3
  32. universal_mcp/stores/store.py +7 -0
  33. universal_mcp/tools/tools.py +2 -2
  34. universal_mcp/utils/docstring_parser.py +171 -104
  35. {universal_mcp-0.1.8rc4.dist-info → universal_mcp-0.1.9rc1.dist-info}/METADATA +2 -1
  36. {universal_mcp-0.1.8rc4.dist-info → universal_mcp-0.1.9rc1.dist-info}/RECORD +38 -13
  37. {universal_mcp-0.1.8rc4.dist-info → universal_mcp-0.1.9rc1.dist-info}/WHEEL +0 -0
  38. {universal_mcp-0.1.8rc4.dist-info → universal_mcp-0.1.9rc1.dist-info}/entry_points.txt +0 -0
@@ -102,7 +102,7 @@ class ApiKeyIntegration(Integration):
102
102
  if not self._api_key:
103
103
  try:
104
104
  credentials = self.store.get(self.name)
105
- self.api_key = credentials
105
+ self._api_key = credentials
106
106
  except KeyNotFoundError as e:
107
107
  action = self.authorize()
108
108
  raise NotAuthorizedError(action) from e
@@ -9,7 +9,7 @@ from loguru import logger
9
9
  from mcp.server.fastmcp import FastMCP
10
10
  from mcp.types import TextContent
11
11
 
12
- from universal_mcp.applications import Application, app_from_slug
12
+ from universal_mcp.applications import BaseApplication, app_from_slug
13
13
  from universal_mcp.config import AppConfig, ServerConfig, StoreConfig
14
14
  from universal_mcp.integrations import AgentRIntegration, integration_from_config
15
15
  from universal_mcp.stores import BaseStore, store_from_config
@@ -129,7 +129,7 @@ class LocalServer(BaseServer):
129
129
  self.add_tool(store.delete)
130
130
  return store
131
131
 
132
- def _load_app(self, app_config: AppConfig) -> Application | None:
132
+ def _load_app(self, app_config: AppConfig) -> BaseApplication | None:
133
133
  """Load a single application with its integration.
134
134
 
135
135
  Args:
@@ -202,7 +202,7 @@ class AgentRServer(BaseServer):
202
202
  logger.error(f"Failed to fetch apps from AgentR: {e}", exc_info=True)
203
203
  raise
204
204
 
205
- def _load_app(self, app_config: AppConfig) -> Application | None:
205
+ def _load_app(self, app_config: AppConfig) -> BaseApplication | None:
206
206
  """Load a single application with AgentR integration.
207
207
 
208
208
  Args:
@@ -69,6 +69,13 @@ class BaseStore(ABC):
69
69
  """
70
70
  pass
71
71
 
72
+ def __repr__(self):
73
+ return f"{self.__class__.__name__}()"
74
+
75
+ def __str__(self):
76
+ return self.__repr__()
77
+
78
+
72
79
 
73
80
  class MemoryStore(BaseStore):
74
81
  """
@@ -9,7 +9,7 @@ from loguru import logger
9
9
  from pydantic import BaseModel, Field
10
10
 
11
11
  from universal_mcp.analytics import analytics
12
- from universal_mcp.applications.application import Application
12
+ from universal_mcp.applications import BaseApplication
13
13
  from universal_mcp.exceptions import NotAuthorizedError, ToolError
14
14
  from universal_mcp.utils.docstring_parser import parse_docstring
15
15
 
@@ -253,7 +253,7 @@ class ToolManager:
253
253
 
254
254
  def register_tools_from_app(
255
255
  self,
256
- app: Application,
256
+ app: BaseApplication,
257
257
  tools: list[str] | None = None,
258
258
  tags: list[str] | None = None,
259
259
  ) -> None:
@@ -4,16 +4,23 @@ from typing import Any
4
4
 
5
5
  def parse_docstring(docstring: str | None) -> dict[str, Any]:
6
6
  """
7
- Parses a standard Python docstring into summary, args, returns, raises, and tags.
7
+ Parses a Python docstring into structured components: summary, arguments,
8
+ return value, raised exceptions, and custom tags.
9
+
10
+ Supports multi-line descriptions for each section. Recognizes common section
11
+ headers like 'Args:', 'Returns:', 'Raises:', 'Tags:', etc. Also attempts
12
+ to parse key-value pairs within 'Args:' and 'Raises:' sections.
8
13
 
9
14
  Args:
10
- docstring: The docstring to parse.
15
+ docstring: The docstring string to parse, or None.
11
16
 
12
17
  Returns:
13
- A dictionary with keys 'summary', 'args', 'returns', 'raises', 'tags'.
14
- 'args' is a dict mapping arg names to descriptions.
15
- 'raises' is a dict mapping exception type names to descriptions.
16
- 'tags' is a list of strings extracted from the 'Tags:' section, comma-separated.
18
+ A dictionary containing the parsed components:
19
+ - 'summary': The first paragraph of the docstring.
20
+ - 'args': A dictionary mapping argument names to their descriptions.
21
+ - 'returns': The description of the return value.
22
+ - 'raises': A dictionary mapping exception types to their descriptions.
23
+ - 'tags': A list of strings found in the 'Tags:' section.
17
24
  """
18
25
  if not docstring:
19
26
  return {"summary": "", "args": {}, "returns": "", "raises": {}, "tags": []}
@@ -22,123 +29,183 @@ def parse_docstring(docstring: str | None) -> dict[str, Any]:
22
29
  if not lines:
23
30
  return {"summary": "", "args": {}, "returns": "", "raises": {}, "tags": []}
24
31
 
25
- summary = lines[0].strip()
26
- args = {}
27
- returns = ""
28
- raises = {}
29
- tags: list[str] = [] # Final list of parsed tags
30
- current_section = None
31
- current_key = None
32
- current_desc_lines = [] # Accumulator for multi-line descriptions/tag content
32
+ summary: str = ""
33
+ summary_lines: list[str] = []
34
+ args: dict[str, str] = {}
35
+ returns: str = ""
36
+ raises: dict[str, str] = {}
37
+ tags: list[str] = []
38
+
39
+ current_section: str | None = None
40
+ current_key: str | None = None
41
+ current_desc_lines: list[str] = []
42
+
43
+ # Pattern to capture item key and the start of its description
44
+ # Matches "key:" or "key (type):" followed by description
33
45
  key_pattern = re.compile(r"^\s*([\w\.]+)\s*(?:\(.*\))?:\s*(.*)")
34
46
 
35
47
  def finalize_current_item():
36
- """Helper function to finalize the currently parsed item."""
37
- nonlocal returns, tags # Allow modification of outer scope variables
48
+ """Processes the collected current_desc_lines and assigns them."""
49
+ nonlocal returns, tags, args, raises
38
50
  desc = " ".join(current_desc_lines).strip()
51
+
39
52
  if current_section == "args" and current_key:
40
- args[current_key] = desc
53
+ if desc:
54
+ args[current_key] = desc
41
55
  elif current_section == "raises" and current_key:
42
- raises[current_key] = desc
56
+ if desc:
57
+ raises[current_key] = desc
43
58
  elif current_section == "returns":
44
59
  returns = desc
45
- # SIM102 applied: Combine nested if
46
- elif current_section == "tags" and desc: # Only process if there's content
47
- tags = [tag.strip() for tag in desc.split(",") if tag.strip()]
60
+ elif current_section == "tags":
61
+ # Tags section content is treated as a comma-separated list
62
+ tags.clear() # Clear existing tags in case of multiple tag sections (unlikely but safe)
63
+ tags.extend([tag.strip() for tag in desc.split(",") if tag.strip()])
64
+ # 'other' sections are ignored in the final output
65
+
66
+ def check_for_section_header(line: str) -> tuple[bool, str | None, str]:
67
+ """Checks if a line is a recognized section header."""
68
+ stripped_lower = line.strip().lower()
69
+ section_type: str | None = None
70
+ header_content = ""
48
71
 
49
- # B007 applied: Rename unused loop variable i to _
50
- for _, line in enumerate(lines[1:]):
72
+ if stripped_lower in ("args:", "arguments:", "parameters:"):
73
+ section_type = "args"
74
+ elif stripped_lower in ("returns:", "yields:"):
75
+ section_type = "returns"
76
+ elif stripped_lower in ("raises:", "errors:", "exceptions:"):
77
+ section_type = "raises"
78
+ elif stripped_lower in ("tags:",):
79
+ section_type = "tags"
80
+ # Allow "Raises Description:" or "Tags content:"
81
+ elif stripped_lower.startswith(("raises ", "errors ", "exceptions ")):
82
+ section_type = "raises"
83
+ # Capture content after header word and potential colon/space
84
+ parts = re.split(r"[:\s]+", line.strip(), maxsplit=1) # B034: Use keyword maxsplit
85
+ if len(parts) > 1:
86
+ header_content = parts[1].strip()
87
+ elif stripped_lower.startswith(("tags",)):
88
+ section_type = "tags"
89
+ # Capture content after header word and potential colon/space
90
+ parts = re.split(r"[:\s]+", line.strip(), maxsplit=1) # B034: Use keyword maxsplit
91
+ if len(parts) > 1:
92
+ header_content = parts[1].strip()
93
+
94
+
95
+ # Identify other known sections, but don't store their content
96
+ elif stripped_lower.endswith(":") and stripped_lower[:-1] in (
97
+ "attributes", "see also", "example", "examples", "notes", "todo", "fixme", "warning", "warnings"
98
+ ):
99
+ section_type = "other"
100
+
101
+ return section_type is not None, section_type, header_content
102
+
103
+
104
+ in_summary = True
105
+
106
+ for line in lines:
51
107
  stripped_line = line.strip()
52
108
  original_indentation = len(line) - len(line.lstrip(" "))
53
109
 
54
- section_line = stripped_line.lower()
55
- is_new_section_header = False
56
- new_section_type = None
57
- header_content = ""
110
+ is_new_section_header, new_section_type_this_line, header_content_this_line = check_for_section_header(line)
111
+
112
+ should_finalize_previous = False
113
+
114
+ # --- Summary Handling ---
115
+ if in_summary:
116
+ if not stripped_line or is_new_section_header:
117
+ # Empty line or section header marks the end of the summary
118
+ in_summary = False
119
+ summary = " ".join(summary_lines).strip()
120
+ summary_lines = [] # Clear summary_lines after finalizing summary
121
+
122
+ if not stripped_line:
123
+ # If the line was just empty, continue to the next line
124
+ # The new_section_header check will happen on the next iteration if it exists
125
+ continue
126
+ # If it was a header, fall through to section handling below
127
+
128
+ else:
129
+ # Still in summary, append line
130
+ summary_lines.append(stripped_line)
131
+ continue # Process next line
132
+
133
+
134
+ # --- Section and Item Handling ---
135
+
136
+ # Decide if the previous item/section block should be finalized BEFORE processing the current line
137
+ # Finalize if:
138
+ # 1. A new section header is encountered.
139
+ # 2. An empty line is encountered AFTER we've started collecting content for an item or section.
140
+ # 3. In 'args' or 'raises', we encounter a line that looks like a new key: value pair, or a non-indented line.
141
+ # 4. In 'returns', 'tags', or 'other', we encounter a non-indented line after collecting content.
142
+ if is_new_section_header or (not stripped_line and (current_desc_lines or current_key is not None)) or \
143
+ (current_section in ["args", "raises"] and current_key is not None and (key_pattern.match(line) or (original_indentation == 0 and stripped_line))) or \
144
+ (current_section in ["returns", "tags", "other"] and current_desc_lines and original_indentation == 0 and stripped_line):
145
+ should_finalize_previous = True
146
+ elif current_section in ["args", "raises"] and current_key is not None:
147
+ # Inside args/raises, processing an item (current_key is set)
148
+ pass # Logic moved to the combined if statement
149
+ elif current_section in ["returns", "tags", "other"] and current_desc_lines:
150
+ # Inside returns/tags/other, collecting description lines
151
+ pass # Logic moved to the combined if statement
152
+
153
+ # If finalizing the previous item/section
154
+ if should_finalize_previous:
155
+ finalize_current_item()
156
+ # Reset state after finalizing the previous item/section block
157
+ # If it was a new section header, reset everything
158
+ # If it was an end-of-item/block signal within a section, reset key and description lines
159
+ # (The condition for resetting key here is complex but matches the original logic)
160
+ if is_new_section_header or (current_section in ["args", "raises"] and current_key is not None and not key_pattern.match(line) and (not stripped_line or original_indentation == 0)):
161
+ current_key = None
162
+ current_desc_lines = [] # Always clear description lines
58
163
 
59
- if section_line in ("args:", "arguments:", "parameters:"):
60
- new_section_type = "args"
61
- is_new_section_header = True
62
- elif section_line in ("returns:", "yields:"):
63
- new_section_type = "returns"
64
- is_new_section_header = True
65
- elif section_line.startswith(("raises ", "raises:", "errors:", "exceptions:")):
66
- new_section_type = "raises"
67
- is_new_section_header = True
68
- elif section_line.startswith(
69
- ("tags:", "tags")
70
- ): # Match "Tags:" or "Tags" potentially followed by content
71
- new_section_type = "tags"
72
- is_new_section_header = True
73
- if ":" in stripped_line:
74
- header_content = stripped_line.split(":", 1)[1].strip()
75
- elif section_line.endswith(":") and section_line[:-1] in (
76
- "attributes",
77
- "see also",
78
- "example",
79
- "examples",
80
- "notes",
81
- ):
82
- new_section_type = "other"
83
- is_new_section_header = True
164
+ # --- Process the current line ---
84
165
 
85
- finalize_previous = False
166
+ # If the current line is a section header
86
167
  if is_new_section_header:
87
- finalize_previous = True
88
- elif current_section in ["args", "raises"] and current_key:
89
- if key_pattern.match(line) or (original_indentation == 0 and stripped_line):
90
- finalize_previous = True
91
- elif current_section in ["returns", "tags"] and current_desc_lines:
92
- if original_indentation == 0 and stripped_line:
93
- finalize_previous = True
94
- # SIM102 applied: Combine nested if/elif
95
- elif (
96
- not stripped_line
97
- and current_desc_lines
98
- and current_section in ["args", "raises", "returns", "tags"]
99
- and (current_section not in ["args", "raises"] or current_key)
100
- ):
101
- finalize_previous = True
102
-
103
- if finalize_previous:
104
- finalize_current_item()
105
- current_key = None
106
- current_desc_lines = []
107
- if not is_new_section_header or new_section_type == "other":
108
- current_section = None
109
-
110
- if is_new_section_header and new_section_type != "other":
111
- current_section = new_section_type
112
- # If Tags header had content, start accumulating it
113
- if new_section_type == "tags" and header_content:
114
- current_desc_lines.append(header_content)
115
- # Don't process the header line itself further
116
- continue
168
+ current_section = new_section_type_this_line
169
+ if header_content_this_line:
170
+ # Add content immediately following the header on the same line
171
+ current_desc_lines.append(header_content_this_line)
172
+ continue # Move to the next line, header is processed
117
173
 
174
+ # If the line is empty, and not a section header (handled above), skip it
118
175
  if not stripped_line:
119
176
  continue
120
177
 
178
+ # If we are inside a section, process the line's content
121
179
  if current_section == "args" or current_section == "raises":
122
180
  match = key_pattern.match(line)
123
181
  if match:
182
+ # Found a new key: value item within args/raises
124
183
  current_key = match.group(1)
125
- current_desc_lines = [match.group(2).strip()] # Start new description
126
- elif (
127
- current_key and original_indentation > 0
128
- ): # Check for indentation for continuation
129
- current_desc_lines.append(stripped_line)
184
+ current_desc_lines = [match.group(2).strip()] # Start new description
185
+ elif current_key is not None:
186
+ # Not a new key, but processing an existing item - append to description
187
+ current_desc_lines.append(stripped_line)
188
+ # Lines that don't match key_pattern and occur when current_key is None
189
+ # within args/raises are effectively ignored by this block, which seems
190
+ # consistent with needing a key: description format.
191
+
192
+ elif current_section in ["returns", "tags", "other"]:
193
+ # In these sections, all non-empty, non-header lines are description lines
194
+ current_desc_lines.append(stripped_line)
195
+
196
+ # --- Finalization after loop ---
197
+ # Finalize any pending item/section block that was being collected
198
+ finalize_current_item()
130
199
 
131
- elif current_section == "returns":
132
- if not current_desc_lines or original_indentation > 0:
133
- current_desc_lines.append(stripped_line)
200
+ # If the docstring only had a summary (no empty line or section header)
201
+ # ensure the summary is captured. This check is technically redundant
202
+ # because summary is finalized upon hitting the first empty line or header,
203
+ # or falls through to the final finalize call if neither occurs.
204
+ # Keeping it for clarity, though the logic flow should cover it.
205
+ if in_summary:
206
+ summary = " ".join(summary_lines).strip()
134
207
 
135
- elif current_section == "tags":
136
- if (
137
- original_indentation > 0 or not current_desc_lines
138
- ): # Indented or first line
139
- current_desc_lines.append(stripped_line)
140
208
 
141
- finalize_current_item()
142
209
  return {
143
210
  "summary": summary,
144
211
  "args": args,
@@ -147,9 +214,9 @@ def parse_docstring(docstring: str | None) -> dict[str, Any]:
147
214
  "tags": tags,
148
215
  }
149
216
 
150
-
151
217
  docstring_example = """
152
- Starts a crawl job for a given URL using Firecrawl. Returns the job ID immediately.
218
+ Starts a crawl job for a given URL using Firecrawl.
219
+ Returns the job ID immediately.
153
220
 
154
221
  Args:
155
222
  url: The starting URL for the crawl.
@@ -163,17 +230,17 @@ docstring_example = """
163
230
  or a string containing an error message on failure. This description
164
231
  can also span multiple lines.
165
232
 
166
- Raises:
233
+ Raises:
167
234
  ValueError: If the URL is invalid.
168
- requests.exceptions.ConnectionError: If connection fails.
235
+ ConnectionError: If connection fails.
169
236
 
170
237
  Tags:
171
238
  crawl, async_job, start, api, long_tag_example , another
172
239
  , final_tag
173
- """
240
+ """
174
241
 
175
242
  if __name__ == "__main__":
176
- parsed = parse_docstring(docstring_example)
177
243
  import json
178
244
 
179
- print(json.dumps(parsed, indent=4))
245
+ parsed = parse_docstring(docstring_example)
246
+ print(json.dumps(parsed, indent=4))
@@ -1,9 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: universal-mcp
3
- Version: 0.1.8rc4
3
+ Version: 0.1.9rc1
4
4
  Summary: Universal MCP acts as a middle ware for your API applications. It can store your credentials, authorize, enable disable apps on the fly and much more.
5
5
  Author-email: Manoj Bajaj <manojbajaj95@gmail.com>
6
6
  Requires-Python: >=3.11
7
+ Requires-Dist: gql[all]>=3.5.2
7
8
  Requires-Dist: keyring>=25.6.0
8
9
  Requires-Dist: litellm>=1.30.7
9
10
  Requires-Dist: loguru>=0.7.3
@@ -5,14 +5,20 @@ universal_mcp/config.py,sha256=sJaPI4q51CDPPG0z32rMJiE7a64eaa9nxbjJgYnaFA4,838
5
5
  universal_mcp/exceptions.py,sha256=WApedvzArNujD0gZfUofYBxjQo97ZDJLqDibtLWZoRk,373
6
6
  universal_mcp/logger.py,sha256=D947u1roUf6WqlcEsPpvmWDqGc8L41qF3MO1suK5O1Q,308
7
7
  universal_mcp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- universal_mcp/applications/__init__.py,sha256=qeWnbdIudyMR7ST4XTc0gpEM9o6TsM1ZnZ92dMAPSBA,754
9
- universal_mcp/applications/application.py,sha256=CUNUUfPl4JVmnFPBuYYhhlwRo63_SRvVw1HuKZ3u0js,6772
8
+ universal_mcp/applications/__init__.py,sha256=7s5c2j3Cbgdmqp7J1QKdehUQmlzSE0NLfkDE1U8Dl1A,825
9
+ universal_mcp/applications/application.py,sha256=Q_DSnvYCgoZH6gS8M1FtrrOetOTXI6bIaKPjgUgQJVA,8240
10
10
  universal_mcp/applications/ahrefs/README.md,sha256=bQQ5AmPFxM52gL-tllIFWC_64f7KYkBiD1tYfdTwDu4,5370
11
11
  universal_mcp/applications/ahrefs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  universal_mcp/applications/ahrefs/app.py,sha256=8iYYmQ5bD6nd_JmHOk4bcEPqG162FtQ14WrnJPeTrBQ,91468
13
+ universal_mcp/applications/cal_com_v2/README.md,sha256=iSgL5yZygDTV19Kh6me7mqaDyjO88Ka9TjE5rDrT8x4,20773
14
+ universal_mcp/applications/cal_com_v2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ universal_mcp/applications/cal_com_v2/app.py,sha256=68o-10yoMCwb1iwzBPTm3kYyHYXktzeGaTnK3j7MKLw,234189
13
16
  universal_mcp/applications/calendly/README.md,sha256=85m3XXLPhQ99oghl8SeazCZ2fjBR81-f1y_mjjhx2B0,5911
14
17
  universal_mcp/applications/calendly/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
18
  universal_mcp/applications/calendly/app.py,sha256=ANuUfiXBwDZjQC-xfB06JoVu5ebQPEy12COBO5LY3DI,49874
19
+ universal_mcp/applications/clickup/README.md,sha256=A7q0j9IXm2jKBxGtWa4SUuTYF5_nJYEht4xeIbKhLHI,15482
20
+ universal_mcp/applications/clickup/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ universal_mcp/applications/clickup/app.py,sha256=_K5ZyMY3R8Ha88xvhh6xxbCsWRGr12j_of6FRrG9WVE,206947
16
22
  universal_mcp/applications/coda/README.md,sha256=4i6o_R-qtTuxfS1A7VoIb8_85FHAj-WVb8YG5fNvwL4,11411
17
23
  universal_mcp/applications/coda/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
24
  universal_mcp/applications/coda/app.py,sha256=47ZmtYF5A2Cn0rh3Dpc3VtkIHR1Xu2PCYe1JDH8kJbY,155862
@@ -34,18 +40,37 @@ universal_mcp/applications/google_mail/README.md,sha256=LL6TjjmwEqyuRVvIfCh-I_Pl
34
40
  universal_mcp/applications/google_mail/app.py,sha256=1XI1hve2FXOqkzgJNYu2ki5J1yGKfeMx3cO_Qyflp_o,27286
35
41
  universal_mcp/applications/google_sheet/README.md,sha256=yW1b_qlb_pbIJzCxZc58581kKzC5vyP8Mj4iwXgidtQ,1108
36
42
  universal_mcp/applications/google_sheet/app.py,sha256=O6g8P697ve93CsljLK9ejWbIyzGbZ-_ThK_A_3cTpIk,6310
37
- universal_mcp/applications/markitdown/app.py,sha256=j1AidXGmDwEV4sBw0A-mEe8O4V4Yln4Atrqnk29AHiI,1836
43
+ universal_mcp/applications/hashnode/app.py,sha256=TGfJmumJGOkS_M0QONWHR0dcBmrHcsWslJYsUvxjj7A,2286
44
+ universal_mcp/applications/hashnode/prompt.md,sha256=RTXicHP_JrI9pMoMf6oay0OxkJUI1yBWyMGCYP6X7xE,1651
45
+ universal_mcp/applications/mailchimp/README.md,sha256=9p7rVwf0aX2lClVeMliUz8rFWtXNhi55AAKj3hbHSng,33688
46
+ universal_mcp/applications/mailchimp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
+ universal_mcp/applications/mailchimp/app.py,sha256=Qg8GupbjKMueThLVtUi-_6Xh0qzIKvv531XDLPj6zKI,453966
48
+ universal_mcp/applications/markitdown/app.py,sha256=FeJONdT-Jwe5mJBelOfgpM1A3T-hIDfFGf1PCR5x1gA,1832
38
49
  universal_mcp/applications/notion/README.md,sha256=45NmPOmSQv99qBvWdwmnV5vbaYc9_8vq8I-FA7veVAA,2600
39
50
  universal_mcp/applications/notion/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
51
  universal_mcp/applications/notion/app.py,sha256=nc-p531L-L6gMFqOOkYu5Irn9SReWAYRmJ8ZOIv5LrQ,20834
41
52
  universal_mcp/applications/perplexity/README.md,sha256=QGV1iReH5p-Np7vvkZsVHxxDKQ0YaitHEwomNmGEyQs,732
42
- universal_mcp/applications/perplexity/app.py,sha256=W2wXe2ltQKqKM6hKcVW0DczsULdmPhcsJdjx93wSXaM,2673
53
+ universal_mcp/applications/perplexity/app.py,sha256=WAFDLH6jkH5eo9LLQfD_MRhpJdgZgHXchqs_ARJe3oI,2633
43
54
  universal_mcp/applications/reddit/README.md,sha256=YVbJ1RN6NWlB-P6w2LxCk_DuUWl7mwaKZScY-mIMnNc,1271
44
55
  universal_mcp/applications/reddit/app.py,sha256=Jd-Pr-IMhROun82kuLf0mNJ3P-LDfGfvj1bn_8qNIAI,15748
56
+ universal_mcp/applications/replicate/README.md,sha256=9ZT3LPpsSG_SSyAJhw0Y60NhynmWoxSttmDkJyCsT9I,4415
57
+ universal_mcp/applications/replicate/app.py,sha256=CkvUjWAWxp06WE_H8CEQkUfj6bwUDUMuWYdu0XXNR5c,39674
45
58
  universal_mcp/applications/resend/README.md,sha256=k-sb2UwbFvDPEz6qQPLWd2cJj8hDx5f3NW7dz2jAfjI,719
46
59
  universal_mcp/applications/resend/app.py,sha256=dWhijrx73hw2OLMAC01keVj7hVgu4CUZsURyRjxD7ew,1370
60
+ universal_mcp/applications/retell_ai/README.md,sha256=XKBvMeA-wQAZYGMXogWMyvcqclhLGVnK72G4q2ZdfVM,1882
61
+ universal_mcp/applications/retell_ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
62
+ universal_mcp/applications/retell_ai/app.py,sha256=Ux0XuH65Y51CPgInfLZxGMLRdfATd31yMptcusUk-BA,13601
63
+ universal_mcp/applications/rocketlane/README.md,sha256=WtWkCt5nRN-SESaQXpYxgZwGEcxOXIcn9aQB7dDY8vM,1245
64
+ universal_mcp/applications/rocketlane/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
65
+ universal_mcp/applications/rocketlane/app.py,sha256=ZjqpgyT059Z8wlXCcz5WOiU37VjXImSOplyx-bhDhgk,6875
47
66
  universal_mcp/applications/serpapi/README.md,sha256=hX4VeT2iL_67ZsMhKd60DAujQCh9K3IdHroHIq808RY,691
48
67
  universal_mcp/applications/serpapi/app.py,sha256=krx9STkJI0vLarXo34emySv3fs9o9lmQ2qfjWbzxtg4,2918
68
+ universal_mcp/applications/spotify/README.md,sha256=iT8nx2_1jcpNqf6Xn3E0-oxmimg1Z1CYFAJg9qNR_4g,9626
69
+ universal_mcp/applications/spotify/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
+ universal_mcp/applications/spotify/app.py,sha256=Lk-zLUAleiTNhEIBSdePGbNoBwxPnEz7pyBafwk6Zyo,104644
71
+ universal_mcp/applications/supabase/README.md,sha256=CqCTwOGJWwOgwPQSblgUxYMWHBn5jZn6gOxB568sVrs,9743
72
+ universal_mcp/applications/supabase/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
73
+ universal_mcp/applications/supabase/app.py,sha256=Dp1pMdKgJyFIx8iIxhUFUMmCET1knNiWrawsnRe-01g,135495
49
74
  universal_mcp/applications/tavily/README.md,sha256=cNg4EwX5wBbkDpPtNBNC3A_GxglfSVhdAJuweSrXN20,721
50
75
  universal_mcp/applications/tavily/app.py,sha256=rU9IRyhzYkchjs8rqQMU89hkBQy13W8yeQqpQhPCCFA,1924
51
76
  universal_mcp/applications/wrike/README.md,sha256=4EHVPlA8B_dzTA1-HQQqp89z6QL37RTyD2l6DD7vG9E,5156
@@ -59,23 +84,23 @@ universal_mcp/applications/zenquotes/app.py,sha256=xp_nlW4LFi0Mw1GRWIde4k8eexXUJ
59
84
  universal_mcp/integrations/README.md,sha256=lTAPXO2nivcBe1q7JT6PRa6v9Ns_ZersQMIdw-nmwEA,996
60
85
  universal_mcp/integrations/__init__.py,sha256=YY8Uw0XGNUpAQ1j-qgCOrwHTcuSew4W92cEtYXMxry4,963
61
86
  universal_mcp/integrations/agentr.py,sha256=Bap4PA2-K4BkBhscgAVsBdvXNN19dkuCLO82sQFRvUM,3952
62
- universal_mcp/integrations/integration.py,sha256=33i1-E4EMTZOhCcbLXSrJfnGUCmRROk7m7DxXUVCt5c,9985
87
+ universal_mcp/integrations/integration.py,sha256=imzxq1jZJAjq9odF6ttCUS2-yQW7qqG8NNUj1LudT9U,9986
63
88
  universal_mcp/servers/__init__.py,sha256=dDtvvMzbWskABlobTZHztrWMb3hbzgidza3BmEmIAD8,474
64
- universal_mcp/servers/server.py,sha256=azsUnzMwJXttNDBs4C6_yuds0A3gEsXkf0Dyc-vHeI8,7941
89
+ universal_mcp/servers/server.py,sha256=g2BJklnkPiO1yX9G1CQdRjb-bs_mg9NGYgPKgiKHEyg,7953
65
90
  universal_mcp/stores/__init__.py,sha256=quvuwhZnpiSLuojf0NfmBx2xpaCulv3fbKtKaSCEmuM,603
66
- universal_mcp/stores/store.py,sha256=8Hobd55WCXGXTJeADn7d_Qe-U8VkC6X3QDTgaKr3Saw,6774
91
+ universal_mcp/stores/store.py,sha256=-yCedzDQafYvQh17RjTPzUwXdjyOOO_0UP_QmAqeClE,6913
67
92
  universal_mcp/tools/__init__.py,sha256=hVL-elJLwD_K87Gpw_s2_o43sQRPyRNOnxlzt0_Pfn8,72
68
93
  universal_mcp/tools/adapters.py,sha256=2HvpyFiI0zg9dp0XshnG7t6KrVqFHM7hgtmgY1bsHN0,927
69
94
  universal_mcp/tools/func_metadata.py,sha256=f_5LdDNsOu1DpXvDUeZYiJswVmwGZz6IMPtpJJ5B2-Y,7975
70
- universal_mcp/tools/tools.py,sha256=27PkcxoxdADoUqQHUyvKX1GJCOYb1lrOtqcR24tR7fs,12982
95
+ universal_mcp/tools/tools.py,sha256=PbvTi8A1hT7F8UXiiP291aP_oZmIPBfhfcgqR8XeSqo,12978
71
96
  universal_mcp/utils/__init__.py,sha256=8wi4PGWu-SrFjNJ8U7fr2iFJ1ktqlDmSKj1xYd7KSDc,41
72
97
  universal_mcp/utils/api_generator.py,sha256=-wRBpLVfJQXy1R-8FpDNs6b8_eeekVDuPc_uwjSGgiY,8883
73
98
  universal_mcp/utils/docgen.py,sha256=yGBcBIr7dz3mNMGrCb_-JFsDf-ShmCKWWiPpuEj2SIU,21878
74
- universal_mcp/utils/docstring_parser.py,sha256=-QVW9u9i1_FlAMwSkFil6ZNaKIkXKI6gqOYtURdp5ak,6745
99
+ universal_mcp/utils/docstring_parser.py,sha256=UU_PzE4nYTPNWNJhVS4t-uvFiA3IL-zK-_H6rSerCOI,10976
75
100
  universal_mcp/utils/dump_app_tools.py,sha256=9bQePJ4ZKzGtcIYrBgLxbKDOZmL7ajIAHhXljT_AlyA,2041
76
101
  universal_mcp/utils/installation.py,sha256=KPBojDlt2YfFY2DfJ9pUr5evFJ9QQGp99KQUsRkz9GQ,10235
77
102
  universal_mcp/utils/openapi.py,sha256=AgmcyntPyovic2mRqr-a7P4kEc7hU-yk9gRVIsO4078,20673
78
- universal_mcp-0.1.8rc4.dist-info/METADATA,sha256=ird6s_0F2lBSsDljkytljaU7rRwLPgx_mGjeLDEfyrk,11271
79
- universal_mcp-0.1.8rc4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
80
- universal_mcp-0.1.8rc4.dist-info/entry_points.txt,sha256=QlBrVKmA2jIM0q-C-3TQMNJTTWOsOFQvgedBq2rZTS8,56
81
- universal_mcp-0.1.8rc4.dist-info/RECORD,,
103
+ universal_mcp-0.1.9rc1.dist-info/METADATA,sha256=478HydebTfGc-zWDh_7tfhUqPQKDqP_Gfd6BcWnra-s,11302
104
+ universal_mcp-0.1.9rc1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
105
+ universal_mcp-0.1.9rc1.dist-info/entry_points.txt,sha256=QlBrVKmA2jIM0q-C-3TQMNJTTWOsOFQvgedBq2rZTS8,56
106
+ universal_mcp-0.1.9rc1.dist-info/RECORD,,