universal-mcp 0.1.1__py3-none-any.whl → 0.1.2__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 (37) hide show
  1. universal_mcp/applications/__init__.py +23 -28
  2. universal_mcp/applications/application.py +13 -8
  3. universal_mcp/applications/e2b/app.py +74 -0
  4. universal_mcp/applications/firecrawl/app.py +381 -0
  5. universal_mcp/applications/github/README.md +35 -0
  6. universal_mcp/applications/github/app.py +133 -100
  7. universal_mcp/applications/google_calendar/app.py +170 -139
  8. universal_mcp/applications/google_mail/app.py +185 -160
  9. universal_mcp/applications/markitdown/app.py +32 -0
  10. universal_mcp/applications/notion/README.md +32 -0
  11. universal_mcp/applications/notion/__init__.py +0 -0
  12. universal_mcp/applications/notion/app.py +415 -0
  13. universal_mcp/applications/reddit/app.py +112 -71
  14. universal_mcp/applications/resend/app.py +3 -8
  15. universal_mcp/applications/serp/app.py +84 -0
  16. universal_mcp/applications/tavily/app.py +11 -10
  17. universal_mcp/applications/zenquotes/app.py +3 -3
  18. universal_mcp/cli.py +98 -16
  19. universal_mcp/config.py +20 -3
  20. universal_mcp/exceptions.py +1 -3
  21. universal_mcp/integrations/__init__.py +6 -2
  22. universal_mcp/integrations/agentr.py +26 -24
  23. universal_mcp/integrations/integration.py +72 -35
  24. universal_mcp/servers/__init__.py +21 -1
  25. universal_mcp/servers/server.py +77 -44
  26. universal_mcp/stores/__init__.py +15 -2
  27. universal_mcp/stores/store.py +123 -13
  28. universal_mcp/utils/__init__.py +1 -0
  29. universal_mcp/utils/api_generator.py +269 -0
  30. universal_mcp/utils/docgen.py +360 -0
  31. universal_mcp/utils/installation.py +17 -2
  32. universal_mcp/utils/openapi.py +216 -111
  33. {universal_mcp-0.1.1.dist-info → universal_mcp-0.1.2.dist-info}/METADATA +23 -5
  34. universal_mcp-0.1.2.dist-info/RECORD +40 -0
  35. universal_mcp-0.1.1.dist-info/RECORD +0 -29
  36. {universal_mcp-0.1.1.dist-info → universal_mcp-0.1.2.dist-info}/WHEEL +0 -0
  37. {universal_mcp-0.1.1.dist-info → universal_mcp-0.1.2.dist-info}/entry_points.txt +0 -0
@@ -1,210 +1,318 @@
1
1
  import json
2
- import yaml
3
2
  import re
4
3
  from pathlib import Path
4
+ from typing import Any
5
+
6
+ import yaml
5
7
 
6
8
 
7
9
  def convert_to_snake_case(identifier: str) -> str:
8
10
  """
9
11
  Convert a camelCase or PascalCase identifier to snake_case.
10
-
12
+
11
13
  Args:
12
14
  identifier (str): The string to convert
13
-
15
+
14
16
  Returns:
15
17
  str: The converted snake_case string
16
18
  """
17
19
  if not identifier:
18
20
  return identifier
19
21
  # Add underscore between lowercase and uppercase letters
20
- result = re.sub(r'([a-z0-9])([A-Z])', r'\1_\2', identifier)
22
+ result = re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", identifier)
21
23
  # Convert to lowercase
22
24
  return result.lower()
23
25
 
24
26
 
25
27
  def load_schema(path: Path):
26
- if path.suffix == '.yaml':
27
- type = 'yaml'
28
- else:
29
- type = 'json'
30
- with open(path, 'r') as f:
31
- if type == 'yaml':
28
+ type = "yaml" if path.suffix == ".yaml" else "json"
29
+ with open(path) as f:
30
+ if type == "yaml":
32
31
  return yaml.safe_load(f)
33
32
  else:
34
33
  return json.load(f)
35
34
 
36
35
 
36
+ def determine_return_type(operation: dict[str, Any]) -> str:
37
+ """
38
+ Determine the return type from the response schema.
39
+
40
+ Args:
41
+ operation (dict): The operation details from the schema.
42
+
43
+ Returns:
44
+ str: The appropriate return type annotation (List[Any], Dict[str, Any], or Any)
45
+ """
46
+ responses = operation.get("responses", {})
47
+ # Find successful response (2XX)
48
+ success_response = None
49
+ for code in responses:
50
+ if code.startswith("2"):
51
+ success_response = responses[code]
52
+ break
53
+
54
+ if not success_response:
55
+ return "Any" # Default to Any if no success response
56
+
57
+ # Check if there's content with schema
58
+ if "content" in success_response:
59
+ for content_type, content_info in success_response["content"].items():
60
+ if content_type.startswith("application/json") and "schema" in content_info:
61
+ schema = content_info["schema"]
62
+
63
+ # Only determine if it's a list, dict, or unknown (Any)
64
+ if schema.get("type") == "array":
65
+ return "List[Any]"
66
+ elif schema.get("type") == "object" or "$ref" in schema:
67
+ return "Dict[str, Any]"
68
+
69
+ # Default to Any if unable to determine
70
+ return "Any"
71
+
72
+
37
73
  def generate_api_client(schema):
38
74
  """
39
75
  Generate a Python API client class from an OpenAPI schema.
40
-
76
+
41
77
  Args:
42
78
  schema (dict): The OpenAPI schema as a dictionary.
43
-
79
+
44
80
  Returns:
45
81
  str: A string containing the Python code for the API client class.
46
82
  """
47
83
  methods = []
48
84
  method_names = []
49
-
85
+
50
86
  # Extract API info for naming and base URL
51
- info = schema.get('info', {})
52
- api_title = info.get('title', 'API')
53
-
87
+ info = schema.get("info", {})
88
+ api_title = info.get("title", "API")
89
+
54
90
  # Get base URL from servers array if available
55
91
  base_url = ""
56
- servers = schema.get('servers', [])
57
- if servers and isinstance(servers, list) and 'url' in servers[0]:
58
- base_url = servers[0]['url'].rstrip('/')
59
-
92
+ servers = schema.get("servers", [])
93
+ if servers and isinstance(servers, list) and "url" in servers[0]:
94
+ base_url = servers[0]["url"].rstrip("/")
95
+
60
96
  # Create a clean class name from API title
61
97
  if api_title:
62
98
  # Convert API title to a clean class name
63
99
  base_name = "".join(word.capitalize() for word in api_title.split())
64
- clean_name = ''.join(c for c in base_name if c.isalnum())
100
+ clean_name = "".join(c for c in base_name if c.isalnum())
65
101
  class_name = f"{clean_name}App"
102
+
103
+ # Extract tool name - remove spaces and convert to lowercase
104
+ tool_name = api_title.lower()
105
+
106
+ # Remove version numbers (like 3.0, v1, etc.)
107
+ tool_name = re.sub(r"\s*v?\d+(\.\d+)*", "", tool_name)
108
+
109
+ # Remove common words that aren't needed
110
+ common_words = ["api", "openapi", "open", "swagger", "spec", "specification"]
111
+ for word in common_words:
112
+ tool_name = tool_name.replace(word, "")
113
+
114
+ # Remove spaces, hyphens, underscores
115
+ tool_name = tool_name.replace(" ", "").replace("-", "").replace("_", "")
116
+
117
+ # Remove any non-alphanumeric characters
118
+ tool_name = "".join(c for c in tool_name if c.isalnum())
119
+
120
+ # If empty (after cleaning), use generic name
121
+ if not tool_name:
122
+ tool_name = "api"
66
123
  else:
67
124
  class_name = "APIClient"
68
-
125
+ tool_name = "api"
126
+
69
127
  # Iterate over paths and their operations
70
- for path, path_info in schema.get('paths', {}).items():
128
+ for path, path_info in schema.get("paths", {}).items():
71
129
  for method in path_info:
72
- if method in ['get', 'post', 'put', 'delete', 'patch', 'options', 'head']:
130
+ if method in ["get", "post", "put", "delete", "patch", "options", "head"]:
73
131
  operation = path_info[method]
74
- method_code, func_name = generate_method_code(path, method, operation)
132
+ method_code, func_name = generate_method_code(
133
+ path, method, operation, tool_name
134
+ )
75
135
  methods.append(method_code)
76
136
  method_names.append(func_name)
77
-
137
+
78
138
  # Generate list_tools method with all the function names
79
139
  tools_list = ",\n ".join([f"self.{name}" for name in method_names])
80
140
  list_tools_method = f""" def list_tools(self):
81
141
  return [
82
142
  {tools_list}
83
143
  ]"""
84
-
144
+
85
145
  # Generate class imports
86
146
  imports = [
87
147
  "from universal_mcp.applications import APIApplication",
88
148
  "from universal_mcp.integrations import Integration",
89
- "from typing import Any, Dict"
149
+ "from typing import Any, Dict, List",
90
150
  ]
91
-
151
+
92
152
  # Construct the class code
93
153
  class_code = (
94
154
  "\n".join(imports) + "\n\n"
95
155
  f"class {class_name}(APIApplication):\n"
96
156
  f" def __init__(self, integration: Integration = None, **kwargs) -> None:\n"
97
157
  f" super().__init__(name='{class_name.lower()}', integration=integration, **kwargs)\n"
98
- f" self.base_url = \"{base_url}\"\n\n" +
99
- '\n\n'.join(methods) + "\n\n" +
100
- list_tools_method + "\n"
158
+ f' self.base_url = "{base_url}"\n\n'
159
+ + "\n\n".join(methods)
160
+ + "\n\n"
161
+ + list_tools_method
162
+ + "\n"
101
163
  )
102
164
  return class_code
103
165
 
104
166
 
105
- def generate_method_code(path, method, operation):
167
+ def generate_method_code(path, method, operation, tool_name=None):
106
168
  """
107
169
  Generate the code for a single API method.
108
-
170
+
109
171
  Args:
110
172
  path (str): The API path (e.g., '/users/{user_id}').
111
173
  method (str): The HTTP method (e.g., 'get').
112
174
  operation (dict): The operation details from the schema.
113
-
175
+ tool_name (str, optional): The name of the tool/app to prefix the function name with.
176
+
114
177
  Returns:
115
178
  tuple: (method_code, func_name) - The Python code for the method and its name.
116
179
  """
180
+ # Extract path parameters from the URL path
181
+ path_params_in_url = re.findall(r'{([^}]+)}', path)
182
+
117
183
  # Determine function name
118
- if 'operationId' in operation:
119
- raw_name = operation['operationId']
120
- cleaned_name = raw_name.replace('.', '_').replace('-', '_')
184
+ if "operationId" in operation:
185
+ raw_name = operation["operationId"]
186
+ cleaned_name = raw_name.replace(".", "_").replace("-", "_")
121
187
  func_name = convert_to_snake_case(cleaned_name)
122
188
  else:
123
189
  # Generate name from path and method
124
- path_parts = path.strip('/').split('/')
190
+ path_parts = path.strip("/").split("/")
125
191
  name_parts = [method]
126
192
  for part in path_parts:
127
- if part.startswith('{') and part.endswith('}'):
128
- name_parts.append('by_' + part[1:-1])
193
+ if part.startswith("{") and part.endswith("}"):
194
+ name_parts.append("by_" + part[1:-1])
129
195
  else:
130
196
  name_parts.append(part)
131
- func_name = '_'.join(name_parts).replace('-', '_').lower()
132
-
197
+ func_name = "_".join(name_parts).replace("-", "_").lower()
198
+
199
+ # Add tool name prefix if provided
200
+ if tool_name:
201
+ func_name = f"{tool_name}_{func_name}"
202
+
133
203
  # Get parameters and request body
134
- parameters = operation.get('parameters', [])
135
- has_body = 'requestBody' in operation
136
- body_required = has_body and operation['requestBody'].get('required', False)
137
-
204
+ # Filter out header parameters
205
+ parameters = [param for param in operation.get("parameters", []) if param.get("in") != "header"]
206
+ has_body = "requestBody" in operation
207
+ body_required = has_body and operation["requestBody"].get("required", False)
208
+
138
209
  # Build function arguments
139
- args = []
140
- for param in parameters:
141
- if param.get('required', False):
142
- args.append(param['name'])
143
- else:
144
- args.append(f"{param['name']}=None")
210
+ required_args = []
211
+ optional_args = []
145
212
 
146
- if has_body:
147
- args.append('request_body' if body_required else 'request_body=None')
148
213
 
149
- signature = f" def {func_name}(self, {', '.join(args)}) -> Dict[str, Any]:"
214
+ for param_name in path_params_in_url:
215
+ if param_name not in required_args:
216
+ required_args.append(param_name)
217
+
150
218
 
219
+ for param in parameters:
220
+ param_name = param["name"]
221
+ if param_name not in required_args:
222
+ if param.get("required", False):
223
+ required_args.append(param_name)
224
+ else:
225
+ optional_args.append(f"{param_name}=None")
226
+
227
+ # Add request body parameter
228
+ if has_body:
229
+ if body_required:
230
+ required_args.append("request_body")
231
+ else:
232
+ optional_args.append("request_body=None")
233
+
234
+ # Combine required and optional arguments
235
+ args = required_args + optional_args
236
+
237
+ # Determine return type
238
+ return_type = determine_return_type(operation)
239
+
240
+ signature = f" def {func_name}(self, {', '.join(args)}) -> {return_type}:"
241
+
151
242
  # Build method body
152
243
  body_lines = []
153
-
154
- # Validate required parameters
155
- for param in parameters:
156
- if param.get('required', False):
157
- body_lines.append(f" if {param['name']} is None:")
158
- body_lines.append(f" raise ValueError(\"Missing required parameter '{param['name']}'\")")
159
-
244
+
245
+ # Validate required parameters including path parameters
246
+ for param_name in required_args:
247
+ if param_name != "request_body": # Skip validation for request body as it's handled separately
248
+ body_lines.append(f" if {param_name} is None:")
249
+ body_lines.append(
250
+ f" raise ValueError(\"Missing required parameter '{param_name}'\")"
251
+ )
252
+
160
253
  # Validate required body
161
254
  if has_body and body_required:
162
255
  body_lines.append(" if request_body is None:")
163
- body_lines.append(" raise ValueError(\"Missing required request body\")")
164
-
165
- # Path parameters
166
- path_params = [p for p in parameters if p['in'] == 'path']
167
- path_params_dict = ', '.join([f"'{p['name']}': {p['name']}" for p in path_params])
168
- body_lines.append(f" path_params = {{{path_params_dict}}}")
169
-
170
- # Format URL
171
- body_lines.append(f" url = f\"{{self.base_url}}{path}\".format_map(path_params)")
172
-
256
+ body_lines.append(
257
+ ' raise ValueError("Missing required request body")'
258
+ )
259
+
260
+ # Format URL directly with path parameters
261
+ url_line = f' url = f"{{self.base_url}}{path}"'
262
+ body_lines.append(url_line)
263
+
173
264
  # Query parameters
174
- query_params = [p for p in parameters if p['in'] == 'query']
175
- query_params_items = ', '.join([f"('{p['name']}', {p['name']})" for p in query_params])
176
- body_lines.append(
177
- f" query_params = {{k: v for k, v in [{query_params_items}] if v is not None}}"
178
- )
179
-
265
+ query_params = [p for p in parameters if p["in"] == "query"]
266
+ if query_params:
267
+ query_params_items = ", ".join(
268
+ [f"('{p['name']}', {p['name']})" for p in query_params]
269
+ )
270
+ body_lines.append(
271
+ f" query_params = {{k: v for k, v in [{query_params_items}] if v is not None}}"
272
+ )
273
+ else:
274
+ body_lines.append(" query_params = {}")
275
+
180
276
  # Request body handling for JSON
181
277
  if has_body:
182
- body_lines.append(" json_body = request_body if request_body is not None else None")
183
-
278
+ body_lines.append(
279
+ " json_body = request_body if request_body is not None else None"
280
+ )
281
+
184
282
  # Make HTTP request using the proper method
185
283
  method_lower = method.lower()
186
- if method_lower == 'get':
284
+ if method_lower == "get":
187
285
  body_lines.append(" response = self._get(url, params=query_params)")
188
- elif method_lower == 'post':
286
+ elif method_lower == "post":
189
287
  if has_body:
190
- body_lines.append(" response = self._post(url, data=json_body)")
288
+ body_lines.append(
289
+ " response = self._post(url, data=json_body, params=query_params)"
290
+ )
191
291
  else:
192
- body_lines.append(" response = self._post(url, data={})")
193
- elif method_lower == 'put':
292
+ body_lines.append(
293
+ " response = self._post(url, data={}, params=query_params)"
294
+ )
295
+ elif method_lower == "put":
194
296
  if has_body:
195
- body_lines.append(" response = self._put(url, data=json_body)")
297
+ body_lines.append(
298
+ " response = self._put(url, data=json_body, params=query_params)"
299
+ )
196
300
  else:
197
- body_lines.append(" response = self._put(url, data={})")
198
- elif method_lower == 'delete':
199
- body_lines.append(" response = self._delete(url)")
301
+ body_lines.append(
302
+ " response = self._put(url, data={}, params=query_params)"
303
+ )
304
+ elif method_lower == "delete":
305
+ body_lines.append(" response = self._delete(url, params=query_params)")
200
306
  else:
201
- body_lines.append(f" response = self._{method_lower}(url, data={{}})")
202
-
307
+ body_lines.append(
308
+ f" response = self._{method_lower}(url, data={{}}, params=query_params)"
309
+ )
310
+
203
311
  # Handle response
204
312
  body_lines.append(" response.raise_for_status()")
205
313
  body_lines.append(" return response.json()")
206
-
207
- method_code = signature + '\n' + '\n'.join(body_lines)
314
+
315
+ method_code = signature + "\n" + "\n".join(body_lines)
208
316
  return method_code, func_name
209
317
 
210
318
 
@@ -221,15 +329,17 @@ if __name__ == "__main__":
221
329
  "name": "limit",
222
330
  "in": "query",
223
331
  "required": False,
224
- "schema": {"type": "integer"}
332
+ "schema": {"type": "integer"},
225
333
  }
226
334
  ],
227
335
  "responses": {
228
336
  "200": {
229
337
  "description": "A list of users",
230
- "content": {"application/json": {"schema": {"type": "array"}}}
338
+ "content": {
339
+ "application/json": {"schema": {"type": "array"}}
340
+ },
231
341
  }
232
- }
342
+ },
233
343
  },
234
344
  "post": {
235
345
  "summary": "Create a user",
@@ -239,15 +349,13 @@ if __name__ == "__main__":
239
349
  "application/json": {
240
350
  "schema": {
241
351
  "type": "object",
242
- "properties": {"name": {"type": "string"}}
352
+ "properties": {"name": {"type": "string"}},
243
353
  }
244
354
  }
245
- }
355
+ },
246
356
  },
247
- "responses": {
248
- "201": {"description": "User created"}
249
- }
250
- }
357
+ "responses": {"201": {"description": "User created"}},
358
+ },
251
359
  },
252
360
  "/users/{user_id}": {
253
361
  "get": {
@@ -257,18 +365,15 @@ if __name__ == "__main__":
257
365
  "name": "user_id",
258
366
  "in": "path",
259
367
  "required": True,
260
- "schema": {"type": "string"}
368
+ "schema": {"type": "string"},
261
369
  }
262
370
  ],
263
- "responses": {
264
- "200": {"description": "User details"}
265
- }
371
+ "responses": {"200": {"description": "User details"}},
266
372
  }
267
- }
373
+ },
268
374
  }
269
375
  }
270
-
271
376
 
272
- schema = load_schema('openapi.yaml')
377
+ schema = load_schema("openapi.yaml")
273
378
  code = generate_api_client(schema)
274
- print(code)
379
+ print(code)
@@ -1,25 +1,39 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: universal-mcp
3
- Version: 0.1.1
3
+ Version: 0.1.2
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: keyring>=25.6.0
8
+ Requires-Dist: litellm>=1.30.7
7
9
  Requires-Dist: loguru>=0.7.3
10
+ Requires-Dist: markitdown[all]>=0.1.1
8
11
  Requires-Dist: mcp>=1.5.0
9
12
  Requires-Dist: pydantic-settings>=2.8.1
10
13
  Requires-Dist: pydantic>=2.11.1
11
14
  Requires-Dist: pyyaml>=6.0.2
12
15
  Requires-Dist: typer>=0.15.2
16
+ Provides-Extra: dev
17
+ Requires-Dist: litellm>=1.30.7; extra == 'dev'
18
+ Requires-Dist: pyright>=1.1.398; extra == 'dev'
19
+ Requires-Dist: pytest-asyncio>=0.26.0; extra == 'dev'
20
+ Requires-Dist: pytest>=8.3.5; extra == 'dev'
21
+ Requires-Dist: ruff>=0.11.4; extra == 'dev'
22
+ Provides-Extra: e2b
23
+ Requires-Dist: e2b-code-interpreter>=1.2.0; extra == 'e2b'
24
+ Provides-Extra: firecrawl
25
+ Requires-Dist: firecrawl-py>=1.15.0; extra == 'firecrawl'
13
26
  Provides-Extra: playground
14
27
  Requires-Dist: fastapi[standard]>=0.115.12; extra == 'playground'
15
28
  Requires-Dist: langchain-anthropic>=0.3.10; extra == 'playground'
16
29
  Requires-Dist: langchain-mcp-adapters>=0.0.3; extra == 'playground'
30
+ Requires-Dist: langchain-openai>=0.3.12; extra == 'playground'
17
31
  Requires-Dist: langgraph-checkpoint-sqlite>=2.0.6; extra == 'playground'
18
32
  Requires-Dist: langgraph>=0.3.24; extra == 'playground'
33
+ Requires-Dist: python-dotenv>=1.0.1; extra == 'playground'
19
34
  Requires-Dist: streamlit>=1.44.1; extra == 'playground'
20
- Provides-Extra: test
21
- Requires-Dist: pytest-asyncio>=0.26.0; extra == 'test'
22
- Requires-Dist: pytest>=8.3.5; extra == 'test'
35
+ Provides-Extra: serpapi
36
+ Requires-Dist: google-search-results>=2.4.2; extra == 'serpapi'
23
37
  Description-Content-Type: text/markdown
24
38
 
25
39
  # Universal MCP
@@ -166,7 +180,11 @@ AgentR includes a command-line interface for common operations:
166
180
  agentr version
167
181
 
168
182
  # Generate API client from OpenAPI schema
169
- agentr generate --schema path/to/openapi.yaml
183
+
184
+ # Use the name of the API as the output filename (e.g., twitter, petstore, github)
185
+ universal_mcp generate --schema petstore.json --output outputfilename
186
+
187
+ # The tool will create src/universal_mcp/applications/petstore/ with app.py and README.md
170
188
 
171
189
  # Run the test server
172
190
  agentr run
@@ -0,0 +1,40 @@
1
+ universal_mcp/__init__.py,sha256=2gdHpHaDDcsRjZjJ01FLN-1iidN_wbDAolNpxhGoFB4,59
2
+ universal_mcp/cli.py,sha256=7-8LZR9GiCW_iAz040XdgocCm6CvuJwBmKoZpFZDf-I,5557
3
+ universal_mcp/config.py,sha256=9eb3DDg4PBBr1MlGeBrA4bja3Y6howOH-UKpo7JIbs8,828
4
+ universal_mcp/exceptions.py,sha256=Zp2_v_m3L7GDAmD1ZyuwFtY6ngapdhxuIygrvpZAQtM,271
5
+ universal_mcp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ universal_mcp/applications/__init__.py,sha256=qeWnbdIudyMR7ST4XTc0gpEM9o6TsM1ZnZ92dMAPSBA,754
7
+ universal_mcp/applications/application.py,sha256=dqp8lgIi2xhY62imwo7C6769URQtNmqd6Ok6PiTr6wc,3399
8
+ universal_mcp/applications/e2b/app.py,sha256=5piCipi1TC_KuKLFP17do1Y1r28fqApvRsZy76equ9w,2573
9
+ universal_mcp/applications/firecrawl/app.py,sha256=AOEQkaOpbkXeMtNdsPKmVQFd-4YHgjpjZy4j0ZvY9co,16831
10
+ universal_mcp/applications/github/README.md,sha256=m_6FlPpx9l5y29TkFMewCXMXSRHriDtj71qyYYeFzCw,643
11
+ universal_mcp/applications/github/app.py,sha256=L201f5MSx1YVx0nqgduZ5gyHPZdX0UfcEhPmDWiWK6s,13686
12
+ universal_mcp/applications/google_calendar/app.py,sha256=g_3vrsM2ltwpTySgC5I4SYg47n4UJiYigECO0ax1EHM,19134
13
+ universal_mcp/applications/google_mail/app.py,sha256=VXeD3_TRgYIUDFUzDPCKgR47XvovxLFulD-HG22hls8,22716
14
+ universal_mcp/applications/markitdown/app.py,sha256=LV8cvkhmacsal-mJmKL9DH5BMypL9MGHIiCkhal6Jtg,1019
15
+ universal_mcp/applications/notion/README.md,sha256=kmdjfXyoEQyk1UTP3zZiGYj2XdMcWieGjs0iscaQ028,2109
16
+ universal_mcp/applications/notion/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ universal_mcp/applications/notion/app.py,sha256=OpoCbGyq3ieNMXHYxaZkqBfjvWRNUwEt3n1-Yj23AyY,17981
18
+ universal_mcp/applications/reddit/app.py,sha256=leU__w5VxX1vMK-kfuy-dvY97Pn8Mn80X2payVshirU,13562
19
+ universal_mcp/applications/resend/app.py,sha256=bRo-CRDuk65EUSHOJnbVHWV6TuiUHtedz6FXKRS1ym0,1386
20
+ universal_mcp/applications/serp/app.py,sha256=hPXu1sBiRZRCCzr4q2uvt54F0-B3aZK2Uz4wfKokkZ4,3131
21
+ universal_mcp/applications/tavily/app.py,sha256=FSIRFz7jwm2i9wiDpRWIoQBxHaJ6dl89AjSZY9WP2VE,1776
22
+ universal_mcp/applications/zenquotes/app.py,sha256=nidRGwVORIU25QGCCbjDIv1UNFUj5nWA3qqK4JP0Tdg,621
23
+ universal_mcp/integrations/README.md,sha256=lTAPXO2nivcBe1q7JT6PRa6v9Ns_ZersQMIdw-nmwEA,996
24
+ universal_mcp/integrations/__init__.py,sha256=8e11JZyctaR9CmlNkfEZ6HhGDvhlvf9iug2wdjb5pwY,270
25
+ universal_mcp/integrations/agentr.py,sha256=l0mo79oeDML19udFfoCo9lyhbDAf0X94_lnpOgbTrb0,3331
26
+ universal_mcp/integrations/integration.py,sha256=zMrSoubzdmJLYr-SKSDMBLWOwIG4-g0l5UEu3xmzsjY,5622
27
+ universal_mcp/servers/__init__.py,sha256=dgRW_khG537GeLKC5_U5jhxCuu1L_1YeTujeDg0601E,654
28
+ universal_mcp/servers/server.py,sha256=tSbHfFVqsksMIrMcKDPiJdBZYhrpsx3QXyEuvBnUAU0,5893
29
+ universal_mcp/stores/__init__.py,sha256=Sc4AWtee_qtK5hpEVUAH2XM_6EBhcfikQXWiGXdNfes,560
30
+ universal_mcp/stores/store.py,sha256=CNOnmKeOCkSU2ZA9t12AIWJcmqZZX_LSyZaV8FQf8Xk,4545
31
+ universal_mcp/utils/__init__.py,sha256=8wi4PGWu-SrFjNJ8U7fr2iFJ1ktqlDmSKj1xYd7KSDc,41
32
+ universal_mcp/utils/api_generator.py,sha256=-wRBpLVfJQXy1R-8FpDNs6b8_eeekVDuPc_uwjSGgiY,8883
33
+ universal_mcp/utils/bridge.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
+ universal_mcp/utils/docgen.py,sha256=UUjiRcIeb96xbogF96Ujzw3Hdd5ExckOao5rzIpRsBQ,12651
35
+ universal_mcp/utils/installation.py,sha256=nyuQDl8S6KftjukCOKE4vtiqSzpVO7M5U-W00ivp444,2939
36
+ universal_mcp/utils/openapi.py,sha256=XhCqWI4pUWX9s_-WNPx03EBboe6CjYUaFip1OqZWRRQ,12926
37
+ universal_mcp-0.1.2.dist-info/METADATA,sha256=fGQ1eC4wBti2_acIAMjXlQNmzQ26b8DIktE-uM5Ij6Q,5931
38
+ universal_mcp-0.1.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
39
+ universal_mcp-0.1.2.dist-info/entry_points.txt,sha256=QlBrVKmA2jIM0q-C-3TQMNJTTWOsOFQvgedBq2rZTS8,56
40
+ universal_mcp-0.1.2.dist-info/RECORD,,
@@ -1,29 +0,0 @@
1
- universal_mcp/__init__.py,sha256=2gdHpHaDDcsRjZjJ01FLN-1iidN_wbDAolNpxhGoFB4,59
2
- universal_mcp/cli.py,sha256=BmO-_huXXKvBsg0S3REPDNdu1YNmmSz5-fXlFceJyg4,2848
3
- universal_mcp/config.py,sha256=YTygfFJPUuL-epRuILvt5tc5ACzByWIFFNhpFwHlDCE,387
4
- universal_mcp/exceptions.py,sha256=cPpdoioDrtNXD6QvH_GKs7bcQ5XRfX8wTrklC4mLS2g,273
5
- universal_mcp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- universal_mcp/applications/__init__.py,sha256=8X1y7kzqBRYsx7vP5jKSagyi4wa5hkbj6d8f8XanKU4,1132
7
- universal_mcp/applications/application.py,sha256=gHVR7jS0SusNiIm98yoy6RpR-uJsgnvYNa_6DanaPsY,3409
8
- universal_mcp/applications/github/app.py,sha256=yXVukXPvAtTAuwNRjo6VU_bt52Sp91P3-K7GUpa3jt4,14020
9
- universal_mcp/applications/google_calendar/app.py,sha256=tovWxCW6YM2DiXM7aEZyA7_u2w2IHlcGcvmj3NsQzyA,19897
10
- universal_mcp/applications/google_mail/app.py,sha256=AiTj3uv_fX5J9d5aoIj3zNfhq2y2FRqCfonw3DUuFiY,23745
11
- universal_mcp/applications/reddit/app.py,sha256=MaHjqgmkHEaJMh3UMvAx4J6HtWQyWA8kOglk699AnlY,13317
12
- universal_mcp/applications/resend/app.py,sha256=Zy702XAxjL4_X_JxHv-3gFfEFZznHdazgde-Z9102S8,1456
13
- universal_mcp/applications/tavily/app.py,sha256=d8mSUzvgzBA07WTzMLEKbFNwGpzEeVWGczJKnvu_2aQ,1833
14
- universal_mcp/applications/zenquotes/app.py,sha256=S86lTiIypXkDqx3kbc3Couk5L_7ejH-Qy-gLTLcCfwM,627
15
- universal_mcp/integrations/README.md,sha256=lTAPXO2nivcBe1q7JT6PRa6v9Ns_ZersQMIdw-nmwEA,996
16
- universal_mcp/integrations/__init__.py,sha256=frBUL7zHnCeukXLuGL8g2P8Rx7D_1TryJQFKKZE2mT4,252
17
- universal_mcp/integrations/agentr.py,sha256=wmac5vwMvftv8PWR54_42fYGlQloFejx76e-vxa597Q,3385
18
- universal_mcp/integrations/integration.py,sha256=R4yATIL6JcuPmFZQknQgWv9mb5eGIcpoismOx2VkKPs,4647
19
- universal_mcp/servers/__init__.py,sha256=IA9hGn0pebJx4hzTdcsRlH4rPD6BAeuw-7VG_WlRzFw,105
20
- universal_mcp/servers/server.py,sha256=vGnqdCd6AA1uh0G87GyfbCXuKqjR12gC2TDnGhFzFYU,5275
21
- universal_mcp/stores/__init__.py,sha256=2_qV1Np4GIrFPdH5CIKLeXEXn2b_ImoOTXmaEuHLc6g,135
22
- universal_mcp/stores/store.py,sha256=fB3uAaobnWf2ILcDBmg3ToDaqAIPYlLtmHBdpmkcGcI,1585
23
- universal_mcp/utils/bridge.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
- universal_mcp/utils/installation.py,sha256=DXcl6voNn1Ihx717KIo2LBzFi_67ffDUxvPu8j0bMyY,2498
25
- universal_mcp/utils/openapi.py,sha256=YvDAUohGkcUf2j1-C9jXCF9DaM4ovnr2NlwejFRxGdI,9590
26
- universal_mcp-0.1.1.dist-info/METADATA,sha256=KFEK1nblKHpSOiRxmJEpbPHAIFzcaYF1vXCCVTlhJAc,5123
27
- universal_mcp-0.1.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
28
- universal_mcp-0.1.1.dist-info/entry_points.txt,sha256=QlBrVKmA2jIM0q-C-3TQMNJTTWOsOFQvgedBq2rZTS8,56
29
- universal_mcp-0.1.1.dist-info/RECORD,,