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.
- universal_mcp/applications/__init__.py +7 -2
- universal_mcp/applications/application.py +162 -114
- universal_mcp/applications/cal_com_v2/README.md +175 -0
- universal_mcp/applications/cal_com_v2/__init__.py +0 -0
- universal_mcp/applications/cal_com_v2/app.py +4735 -0
- universal_mcp/applications/clickup/README.md +160 -0
- universal_mcp/applications/clickup/__init__.py +0 -0
- universal_mcp/applications/clickup/app.py +4359 -0
- universal_mcp/applications/hashnode/app.py +77 -0
- universal_mcp/applications/hashnode/prompt.md +21 -0
- universal_mcp/applications/mailchimp/README.md +306 -0
- universal_mcp/applications/mailchimp/__init__.py +0 -0
- universal_mcp/applications/mailchimp/app.py +8883 -0
- universal_mcp/applications/markitdown/app.py +2 -2
- universal_mcp/applications/perplexity/app.py +0 -1
- universal_mcp/applications/replicate/README.md +53 -0
- universal_mcp/applications/replicate/app.py +969 -0
- universal_mcp/applications/retell_ai/README.md +46 -0
- universal_mcp/applications/retell_ai/__init__.py +0 -0
- universal_mcp/applications/retell_ai/app.py +316 -0
- universal_mcp/applications/rocketlane/README.md +42 -0
- universal_mcp/applications/rocketlane/__init__.py +0 -0
- universal_mcp/applications/rocketlane/app.py +180 -0
- universal_mcp/applications/spotify/README.md +116 -0
- universal_mcp/applications/spotify/__init__.py +0 -0
- universal_mcp/applications/spotify/app.py +2231 -0
- universal_mcp/applications/supabase/README.md +112 -0
- universal_mcp/applications/supabase/__init__.py +0 -0
- universal_mcp/applications/supabase/app.py +2644 -0
- universal_mcp/integrations/integration.py +1 -1
- universal_mcp/servers/server.py +3 -3
- universal_mcp/stores/store.py +7 -0
- universal_mcp/tools/tools.py +2 -2
- universal_mcp/utils/docstring_parser.py +171 -104
- {universal_mcp-0.1.8rc4.dist-info → universal_mcp-0.1.9rc1.dist-info}/METADATA +2 -1
- {universal_mcp-0.1.8rc4.dist-info → universal_mcp-0.1.9rc1.dist-info}/RECORD +38 -13
- {universal_mcp-0.1.8rc4.dist-info → universal_mcp-0.1.9rc1.dist-info}/WHEEL +0 -0
- {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.
|
105
|
+
self._api_key = credentials
|
106
106
|
except KeyNotFoundError as e:
|
107
107
|
action = self.authorize()
|
108
108
|
raise NotAuthorizedError(action) from e
|
universal_mcp/servers/server.py
CHANGED
@@ -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
|
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) ->
|
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) ->
|
205
|
+
def _load_app(self, app_config: AppConfig) -> BaseApplication | None:
|
206
206
|
"""Load a single application with AgentR integration.
|
207
207
|
|
208
208
|
Args:
|
universal_mcp/stores/store.py
CHANGED
universal_mcp/tools/tools.py
CHANGED
@@ -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
|
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:
|
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
|
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
|
14
|
-
'
|
15
|
-
'
|
16
|
-
'
|
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 =
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
-
"""
|
37
|
-
nonlocal returns, tags
|
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
|
-
|
53
|
+
if desc:
|
54
|
+
args[current_key] = desc
|
41
55
|
elif current_section == "raises" and current_key:
|
42
|
-
|
56
|
+
if desc:
|
57
|
+
raises[current_key] = desc
|
43
58
|
elif current_section == "returns":
|
44
59
|
returns = desc
|
45
|
-
|
46
|
-
|
47
|
-
tags
|
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
|
-
|
50
|
-
|
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
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|
-
|
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
|
-
|
166
|
+
# If the current line is a section header
|
86
167
|
if is_new_section_header:
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
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()]
|
126
|
-
elif
|
127
|
-
|
128
|
-
|
129
|
-
|
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
|
-
|
132
|
-
|
133
|
-
|
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.
|
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
|
-
|
233
|
+
Raises:
|
167
234
|
ValueError: If the URL is invalid.
|
168
|
-
|
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
|
-
|
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.
|
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=
|
9
|
-
universal_mcp/applications/application.py,sha256=
|
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/
|
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=
|
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=
|
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=
|
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
|
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=
|
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
|
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.
|
79
|
-
universal_mcp-0.1.
|
80
|
-
universal_mcp-0.1.
|
81
|
-
universal_mcp-0.1.
|
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,,
|
File without changes
|
File without changes
|