universal-mcp 0.1.0__py3-none-any.whl → 0.1.1rc1__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 (35) 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/reddit/app.py +112 -71
  11. universal_mcp/applications/resend/app.py +3 -8
  12. universal_mcp/applications/serp/app.py +84 -0
  13. universal_mcp/applications/tavily/app.py +11 -10
  14. universal_mcp/applications/zenquotes/app.py +3 -3
  15. universal_mcp/cli.py +131 -77
  16. universal_mcp/config.py +20 -3
  17. universal_mcp/exceptions.py +1 -3
  18. universal_mcp/integrations/__init__.py +6 -2
  19. universal_mcp/integrations/agentr.py +26 -24
  20. universal_mcp/integrations/integration.py +72 -35
  21. universal_mcp/servers/__init__.py +21 -1
  22. universal_mcp/servers/server.py +77 -45
  23. universal_mcp/stores/__init__.py +15 -2
  24. universal_mcp/stores/store.py +123 -13
  25. universal_mcp/utils/__init__.py +1 -0
  26. universal_mcp/utils/api_generator.py +269 -0
  27. universal_mcp/utils/docgen.py +360 -0
  28. universal_mcp/utils/installation.py +104 -0
  29. universal_mcp/utils/openapi.py +202 -104
  30. {universal_mcp-0.1.0.dist-info → universal_mcp-0.1.1rc1.dist-info}/METADATA +47 -5
  31. universal_mcp-0.1.1rc1.dist-info/RECORD +37 -0
  32. universal_mcp/applications/agentr.py +0 -0
  33. universal_mcp-0.1.0.dist-info/RECORD +0 -29
  34. {universal_mcp-0.1.0.dist-info → universal_mcp-0.1.1rc1.dist-info}/WHEEL +0 -0
  35. {universal_mcp-0.1.0.dist-info → universal_mcp-0.1.1rc1.dist-info}/entry_points.txt +0 -0
@@ -1,210 +1,311 @@
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
  """
117
180
  # Determine function name
118
- if 'operationId' in operation:
119
- raw_name = operation['operationId']
120
- cleaned_name = raw_name.replace('.', '_').replace('-', '_')
181
+ if "operationId" in operation:
182
+ raw_name = operation["operationId"]
183
+ cleaned_name = raw_name.replace(".", "_").replace("-", "_")
121
184
  func_name = convert_to_snake_case(cleaned_name)
122
185
  else:
123
186
  # Generate name from path and method
124
- path_parts = path.strip('/').split('/')
187
+ path_parts = path.strip("/").split("/")
125
188
  name_parts = [method]
126
189
  for part in path_parts:
127
- if part.startswith('{') and part.endswith('}'):
128
- name_parts.append('by_' + part[1:-1])
190
+ if part.startswith("{") and part.endswith("}"):
191
+ name_parts.append("by_" + part[1:-1])
129
192
  else:
130
193
  name_parts.append(part)
131
- func_name = '_'.join(name_parts).replace('-', '_').lower()
132
-
194
+ func_name = "_".join(name_parts).replace("-", "_").lower()
195
+
196
+ # Add tool name prefix if provided
197
+ if tool_name:
198
+ func_name = f"{tool_name}_{func_name}"
199
+
133
200
  # 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
-
201
+ parameters = operation.get("parameters", [])
202
+ has_body = "requestBody" in operation
203
+ body_required = has_body and operation["requestBody"].get("required", False)
204
+
138
205
  # Build function arguments
139
- args = []
206
+ required_args = []
207
+ optional_args = []
140
208
  for param in parameters:
141
- if param.get('required', False):
142
- args.append(param['name'])
209
+ if param.get("required", False):
210
+ required_args.append(param["name"])
143
211
  else:
144
- args.append(f"{param['name']}=None")
145
-
212
+ optional_args.append(f"{param['name']}=None")
213
+
214
+ # Add request body parameter
146
215
  if has_body:
147
- args.append('request_body' if body_required else 'request_body=None')
148
-
149
- signature = f" def {func_name}(self, {', '.join(args)}) -> Dict[str, Any]:"
150
-
216
+ if body_required:
217
+ required_args.append("request_body")
218
+ else:
219
+ optional_args.append("request_body=None")
220
+
221
+ # Combine required and optional arguments
222
+ args = required_args + optional_args
223
+
224
+ # Determine return type
225
+ return_type = determine_return_type(operation)
226
+
227
+ signature = f" def {func_name}(self, {', '.join(args)}) -> {return_type}:"
228
+
151
229
  # Build method body
152
230
  body_lines = []
153
-
231
+
154
232
  # Validate required parameters
155
233
  for param in parameters:
156
- if param.get('required', False):
234
+ if param.get("required", False):
157
235
  body_lines.append(f" if {param['name']} is None:")
158
- body_lines.append(f" raise ValueError(\"Missing required parameter '{param['name']}'\")")
159
-
236
+ body_lines.append(
237
+ f" raise ValueError(\"Missing required parameter '{param['name']}'\")"
238
+ )
239
+
160
240
  # Validate required body
161
241
  if has_body and body_required:
162
242
  body_lines.append(" if request_body is None:")
163
- body_lines.append(" raise ValueError(\"Missing required request body\")")
164
-
243
+ body_lines.append(
244
+ ' raise ValueError("Missing required request body")'
245
+ )
246
+
165
247
  # 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])
248
+ path_params = [p for p in parameters if p["in"] == "path"]
249
+ path_params_dict = ", ".join([f"'{p['name']}': {p['name']}" for p in path_params])
168
250
  body_lines.append(f" path_params = {{{path_params_dict}}}")
169
-
251
+
170
252
  # Format URL
171
- body_lines.append(f" url = f\"{{self.base_url}}{path}\".format_map(path_params)")
172
-
173
- # 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
253
  body_lines.append(
177
- f" query_params = {{k: v for k, v in [{query_params_items}] if v is not None}}"
254
+ f' url = f"{{self.base_url}}{path}".format_map(path_params)'
178
255
  )
179
-
256
+
257
+ # Query parameters
258
+ query_params = [p for p in parameters if p["in"] == "query"]
259
+ if query_params:
260
+ query_params_items = ", ".join(
261
+ [f"('{p['name']}', {p['name']})" for p in query_params]
262
+ )
263
+ body_lines.append(
264
+ f" query_params = {{k: v for k, v in [{query_params_items}] if v is not None}}"
265
+ )
266
+ else:
267
+ body_lines.append(" query_params = {}")
268
+
180
269
  # Request body handling for JSON
181
270
  if has_body:
182
- body_lines.append(" json_body = request_body if request_body is not None else None")
183
-
271
+ body_lines.append(
272
+ " json_body = request_body if request_body is not None else None"
273
+ )
274
+
184
275
  # Make HTTP request using the proper method
185
276
  method_lower = method.lower()
186
- if method_lower == 'get':
277
+ if method_lower == "get":
187
278
  body_lines.append(" response = self._get(url, params=query_params)")
188
- elif method_lower == 'post':
279
+ elif method_lower == "post":
189
280
  if has_body:
190
- body_lines.append(" response = self._post(url, data=json_body)")
281
+ body_lines.append(
282
+ " response = self._post(url, data=json_body, params=query_params)"
283
+ )
191
284
  else:
192
- body_lines.append(" response = self._post(url, data={})")
193
- elif method_lower == 'put':
285
+ body_lines.append(
286
+ " response = self._post(url, data={}, params=query_params)"
287
+ )
288
+ elif method_lower == "put":
194
289
  if has_body:
195
- body_lines.append(" response = self._put(url, data=json_body)")
290
+ body_lines.append(
291
+ " response = self._put(url, data=json_body, params=query_params)"
292
+ )
196
293
  else:
197
- body_lines.append(" response = self._put(url, data={})")
198
- elif method_lower == 'delete':
199
- body_lines.append(" response = self._delete(url)")
294
+ body_lines.append(
295
+ " response = self._put(url, data={}, params=query_params)"
296
+ )
297
+ elif method_lower == "delete":
298
+ body_lines.append(" response = self._delete(url, params=query_params)")
200
299
  else:
201
- body_lines.append(f" response = self._{method_lower}(url, data={{}})")
202
-
300
+ body_lines.append(
301
+ f" response = self._{method_lower}(url, data={{}}, params=query_params)"
302
+ )
303
+
203
304
  # Handle response
204
305
  body_lines.append(" response.raise_for_status()")
205
306
  body_lines.append(" return response.json()")
206
-
207
- method_code = signature + '\n' + '\n'.join(body_lines)
307
+
308
+ method_code = signature + "\n" + "\n".join(body_lines)
208
309
  return method_code, func_name
209
310
 
210
311
 
@@ -221,15 +322,17 @@ if __name__ == "__main__":
221
322
  "name": "limit",
222
323
  "in": "query",
223
324
  "required": False,
224
- "schema": {"type": "integer"}
325
+ "schema": {"type": "integer"},
225
326
  }
226
327
  ],
227
328
  "responses": {
228
329
  "200": {
229
330
  "description": "A list of users",
230
- "content": {"application/json": {"schema": {"type": "array"}}}
331
+ "content": {
332
+ "application/json": {"schema": {"type": "array"}}
333
+ },
231
334
  }
232
- }
335
+ },
233
336
  },
234
337
  "post": {
235
338
  "summary": "Create a user",
@@ -239,15 +342,13 @@ if __name__ == "__main__":
239
342
  "application/json": {
240
343
  "schema": {
241
344
  "type": "object",
242
- "properties": {"name": {"type": "string"}}
345
+ "properties": {"name": {"type": "string"}},
243
346
  }
244
347
  }
245
- }
348
+ },
246
349
  },
247
- "responses": {
248
- "201": {"description": "User created"}
249
- }
250
- }
350
+ "responses": {"201": {"description": "User created"}},
351
+ },
251
352
  },
252
353
  "/users/{user_id}": {
253
354
  "get": {
@@ -257,18 +358,15 @@ if __name__ == "__main__":
257
358
  "name": "user_id",
258
359
  "in": "path",
259
360
  "required": True,
260
- "schema": {"type": "string"}
361
+ "schema": {"type": "string"},
261
362
  }
262
363
  ],
263
- "responses": {
264
- "200": {"description": "User details"}
265
- }
364
+ "responses": {"200": {"description": "User details"}},
266
365
  }
267
- }
366
+ },
268
367
  }
269
368
  }
270
-
271
369
 
272
- schema = load_schema('openapi.yaml')
370
+ schema = load_schema("openapi.yaml")
273
371
  code = generate_api_client(schema)
274
- print(code)
372
+ print(code)
@@ -1,18 +1,38 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: universal-mcp
3
- Version: 0.1.0
3
+ Version: 0.1.1rc1
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
13
- Provides-Extra: test
14
- Requires-Dist: pytest-asyncio>=0.26.0; extra == 'test'
15
- Requires-Dist: pytest>=8.3.5; extra == 'test'
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'
26
+ Provides-Extra: playground
27
+ Requires-Dist: fastapi[standard]>=0.115.12; extra == 'playground'
28
+ Requires-Dist: langchain-anthropic>=0.3.10; extra == 'playground'
29
+ Requires-Dist: langchain-mcp-adapters>=0.0.3; extra == 'playground'
30
+ Requires-Dist: langgraph-checkpoint-sqlite>=2.0.6; extra == 'playground'
31
+ Requires-Dist: langgraph>=0.3.24; extra == 'playground'
32
+ Requires-Dist: python-dotenv>=1.0.1; extra == 'playground'
33
+ Requires-Dist: streamlit>=1.44.1; extra == 'playground'
34
+ Provides-Extra: serpapi
35
+ Requires-Dist: google-search-results>=2.4.2; extra == 'serpapi'
16
36
  Description-Content-Type: text/markdown
17
37
 
18
38
  # Universal MCP
@@ -87,6 +107,24 @@ if __name__ == "__main__":
87
107
  server.run()
88
108
  ```
89
109
 
110
+ ## Using Playground
111
+
112
+ Start MCP Server
113
+ ```bash
114
+ universal_mcp run -t sse
115
+ ```
116
+
117
+ Start FastAPI app
118
+ ```bash
119
+ fastapi run src/playground
120
+ ```
121
+
122
+ Start Frontend
123
+ ```bash
124
+ streamlit run src/playground/streamlit.py
125
+ ```
126
+
127
+
90
128
  ## 🧩 Available Applications
91
129
  AgentR comes with several pre-built applications:
92
130
 
@@ -141,7 +179,11 @@ AgentR includes a command-line interface for common operations:
141
179
  agentr version
142
180
 
143
181
  # Generate API client from OpenAPI schema
144
- agentr generate --schema path/to/openapi.yaml
182
+
183
+ # Use the name of the API as the output filename (e.g., twitter, petstore, github)
184
+ universal_mcp generate --schema petstore.json --output outputfilename
185
+
186
+ # The tool will create src/universal_mcp/applications/petstore/ with app.py and README.md
145
187
 
146
188
  # Run the test server
147
189
  agentr run
@@ -0,0 +1,37 @@
1
+ universal_mcp/__init__.py,sha256=2gdHpHaDDcsRjZjJ01FLN-1iidN_wbDAolNpxhGoFB4,59
2
+ universal_mcp/cli.py,sha256=rLdE1CRiouPIL2nBYv9EInOk_1uJfUBHmakWHBZ0roc,5555
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/reddit/app.py,sha256=leU__w5VxX1vMK-kfuy-dvY97Pn8Mn80X2payVshirU,13562
16
+ universal_mcp/applications/resend/app.py,sha256=bRo-CRDuk65EUSHOJnbVHWV6TuiUHtedz6FXKRS1ym0,1386
17
+ universal_mcp/applications/serp/app.py,sha256=hPXu1sBiRZRCCzr4q2uvt54F0-B3aZK2Uz4wfKokkZ4,3131
18
+ universal_mcp/applications/tavily/app.py,sha256=FSIRFz7jwm2i9wiDpRWIoQBxHaJ6dl89AjSZY9WP2VE,1776
19
+ universal_mcp/applications/zenquotes/app.py,sha256=nidRGwVORIU25QGCCbjDIv1UNFUj5nWA3qqK4JP0Tdg,621
20
+ universal_mcp/integrations/README.md,sha256=lTAPXO2nivcBe1q7JT6PRa6v9Ns_ZersQMIdw-nmwEA,996
21
+ universal_mcp/integrations/__init__.py,sha256=8e11JZyctaR9CmlNkfEZ6HhGDvhlvf9iug2wdjb5pwY,270
22
+ universal_mcp/integrations/agentr.py,sha256=l0mo79oeDML19udFfoCo9lyhbDAf0X94_lnpOgbTrb0,3331
23
+ universal_mcp/integrations/integration.py,sha256=zMrSoubzdmJLYr-SKSDMBLWOwIG4-g0l5UEu3xmzsjY,5622
24
+ universal_mcp/servers/__init__.py,sha256=dgRW_khG537GeLKC5_U5jhxCuu1L_1YeTujeDg0601E,654
25
+ universal_mcp/servers/server.py,sha256=tSbHfFVqsksMIrMcKDPiJdBZYhrpsx3QXyEuvBnUAU0,5893
26
+ universal_mcp/stores/__init__.py,sha256=Sc4AWtee_qtK5hpEVUAH2XM_6EBhcfikQXWiGXdNfes,560
27
+ universal_mcp/stores/store.py,sha256=CNOnmKeOCkSU2ZA9t12AIWJcmqZZX_LSyZaV8FQf8Xk,4545
28
+ universal_mcp/utils/__init__.py,sha256=8wi4PGWu-SrFjNJ8U7fr2iFJ1ktqlDmSKj1xYd7KSDc,41
29
+ universal_mcp/utils/api_generator.py,sha256=-wRBpLVfJQXy1R-8FpDNs6b8_eeekVDuPc_uwjSGgiY,8883
30
+ universal_mcp/utils/bridge.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
+ universal_mcp/utils/docgen.py,sha256=UUjiRcIeb96xbogF96Ujzw3Hdd5ExckOao5rzIpRsBQ,12651
32
+ universal_mcp/utils/installation.py,sha256=nyuQDl8S6KftjukCOKE4vtiqSzpVO7M5U-W00ivp444,2939
33
+ universal_mcp/utils/openapi.py,sha256=A719DaYc7AgN-n_uW868MBmpG_oPvuvMOgUCauInLeY,12629
34
+ universal_mcp-0.1.1rc1.dist-info/METADATA,sha256=WzlkXc7fMZooD6COVYEDNBUCYQBFegz-YPMpEhxWhOo,5871
35
+ universal_mcp-0.1.1rc1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
36
+ universal_mcp-0.1.1rc1.dist-info/entry_points.txt,sha256=QlBrVKmA2jIM0q-C-3TQMNJTTWOsOFQvgedBq2rZTS8,56
37
+ universal_mcp-0.1.1rc1.dist-info/RECORD,,
File without changes
@@ -1,29 +0,0 @@
1
- universal_mcp/__init__.py,sha256=2gdHpHaDDcsRjZjJ01FLN-1iidN_wbDAolNpxhGoFB4,59
2
- universal_mcp/cli.py,sha256=vbhiDfxnsJcMKhSpJyEA-YemrnlHgEMZ7BZll9pH8Xk,4152
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/agentr.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- universal_mcp/applications/application.py,sha256=gHVR7jS0SusNiIm98yoy6RpR-uJsgnvYNa_6DanaPsY,3409
9
- universal_mcp/applications/github/app.py,sha256=yXVukXPvAtTAuwNRjo6VU_bt52Sp91P3-K7GUpa3jt4,14020
10
- universal_mcp/applications/google_calendar/app.py,sha256=tovWxCW6YM2DiXM7aEZyA7_u2w2IHlcGcvmj3NsQzyA,19897
11
- universal_mcp/applications/google_mail/app.py,sha256=AiTj3uv_fX5J9d5aoIj3zNfhq2y2FRqCfonw3DUuFiY,23745
12
- universal_mcp/applications/reddit/app.py,sha256=MaHjqgmkHEaJMh3UMvAx4J6HtWQyWA8kOglk699AnlY,13317
13
- universal_mcp/applications/resend/app.py,sha256=Zy702XAxjL4_X_JxHv-3gFfEFZznHdazgde-Z9102S8,1456
14
- universal_mcp/applications/tavily/app.py,sha256=d8mSUzvgzBA07WTzMLEKbFNwGpzEeVWGczJKnvu_2aQ,1833
15
- universal_mcp/applications/zenquotes/app.py,sha256=S86lTiIypXkDqx3kbc3Couk5L_7ejH-Qy-gLTLcCfwM,627
16
- universal_mcp/integrations/README.md,sha256=lTAPXO2nivcBe1q7JT6PRa6v9Ns_ZersQMIdw-nmwEA,996
17
- universal_mcp/integrations/__init__.py,sha256=frBUL7zHnCeukXLuGL8g2P8Rx7D_1TryJQFKKZE2mT4,252
18
- universal_mcp/integrations/agentr.py,sha256=wmac5vwMvftv8PWR54_42fYGlQloFejx76e-vxa597Q,3385
19
- universal_mcp/integrations/integration.py,sha256=R4yATIL6JcuPmFZQknQgWv9mb5eGIcpoismOx2VkKPs,4647
20
- universal_mcp/servers/__init__.py,sha256=IA9hGn0pebJx4hzTdcsRlH4rPD6BAeuw-7VG_WlRzFw,105
21
- universal_mcp/servers/server.py,sha256=RzqX0slJOzgU9rxJJECEHQyVGNUAlAeDaOrLrtdwXRc,5276
22
- universal_mcp/stores/__init__.py,sha256=2_qV1Np4GIrFPdH5CIKLeXEXn2b_ImoOTXmaEuHLc6g,135
23
- universal_mcp/stores/store.py,sha256=fB3uAaobnWf2ILcDBmg3ToDaqAIPYlLtmHBdpmkcGcI,1585
24
- universal_mcp/utils/bridge.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
- universal_mcp/utils/openapi.py,sha256=YvDAUohGkcUf2j1-C9jXCF9DaM4ovnr2NlwejFRxGdI,9590
26
- universal_mcp-0.1.0.dist-info/METADATA,sha256=pRUC64lsW_p2E9lBiRnQe_SnjIcbWkr1N1enzRGFMNY,4506
27
- universal_mcp-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
28
- universal_mcp-0.1.0.dist-info/entry_points.txt,sha256=QlBrVKmA2jIM0q-C-3TQMNJTTWOsOFQvgedBq2rZTS8,56
29
- universal_mcp-0.1.0.dist-info/RECORD,,