universal-mcp 0.1.13rc3__py3-none-any.whl → 0.1.13rc7__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.
@@ -1,4 +1,5 @@
1
1
  import importlib
2
+ import subprocess
2
3
 
3
4
  from loguru import logger
4
5
 
@@ -7,12 +8,12 @@ from universal_mcp.applications.application import (
7
8
  BaseApplication,
8
9
  GraphQLApplication,
9
10
  )
10
- import subprocess
11
11
 
12
12
  # Name are in the format of "app-name", eg, google-calendar
13
13
  # Folder name is "app_name", eg, google_calendar
14
14
  # Class name is NameApp, eg, GoogleCalendarApp
15
15
 
16
+
16
17
  def _import_class(module_path: str, class_name: str):
17
18
  """
18
19
  Helper to import a class by name from a module.
@@ -27,7 +28,10 @@ def _import_class(module_path: str, class_name: str):
27
28
  return getattr(module, class_name)
28
29
  except AttributeError as e:
29
30
  logger.error(f"Class '{class_name}' not found in module '{module_path}'")
30
- raise ModuleNotFoundError(f"Class '{class_name}' not found in module '{module_path}'") from e
31
+ raise ModuleNotFoundError(
32
+ f"Class '{class_name}' not found in module '{module_path}'"
33
+ ) from e
34
+
31
35
 
32
36
  def _install_package(slug_clean: str):
33
37
  """
@@ -40,10 +44,13 @@ def _install_package(slug_clean: str):
40
44
  subprocess.check_call(cmd)
41
45
  except subprocess.CalledProcessError as e:
42
46
  logger.error(f"Installation failed for '{slug_clean}': {e}")
43
- raise ModuleNotFoundError(f"Installation failed for package '{slug_clean}'") from e
47
+ raise ModuleNotFoundError(
48
+ f"Installation failed for package '{slug_clean}'"
49
+ ) from e
44
50
  else:
45
51
  logger.info(f"Package '{slug_clean}' installed successfully")
46
52
 
53
+
47
54
  def app_from_slug(slug: str):
48
55
  """
49
56
  Dynamically resolve and return the application class for the given slug.
@@ -54,19 +61,26 @@ def app_from_slug(slug: str):
54
61
  package_prefix = f"universal_mcp_{slug_clean.replace('-', '_')}"
55
62
  module_path = f"{package_prefix}.app"
56
63
 
57
- logger.info(f"Resolving app for slug '{slug}' → module '{module_path}', class '{class_name}'")
64
+ logger.info(
65
+ f"Resolving app for slug '{slug}' → module '{module_path}', class '{class_name}'"
66
+ )
58
67
  try:
59
68
  return _import_class(module_path, class_name)
60
69
  except ModuleNotFoundError as orig_err:
61
- logger.warning(f"Module '{module_path}' not found locally: {orig_err}. Installing...")
70
+ logger.warning(
71
+ f"Module '{module_path}' not found locally: {orig_err}. Installing..."
72
+ )
62
73
  _install_package(slug_clean)
63
74
  # Retry import after installation
64
75
  try:
65
76
  return _import_class(module_path, class_name)
66
77
  except ModuleNotFoundError as retry_err:
67
- logger.error(f"Still cannot import '{module_path}' after installation: {retry_err}")
78
+ logger.error(
79
+ f"Still cannot import '{module_path}' after installation: {retry_err}"
80
+ )
68
81
  raise
69
82
 
83
+
70
84
  __all__ = [
71
85
  "app_from_slug",
72
86
  "BaseApplication",
universal_mcp/cli.py CHANGED
@@ -1,11 +1,9 @@
1
- import asyncio
2
- import os
1
+ import re
3
2
  from pathlib import Path
4
3
 
5
4
  import typer
6
5
  from rich import print as rprint
7
6
  from rich.panel import Panel
8
- import re
9
7
 
10
8
  from universal_mcp.utils.installation import (
11
9
  get_supported_apps,
@@ -41,9 +39,9 @@ def generate(
41
39
  try:
42
40
  # Run the async function in the event loop
43
41
  result = generate_api_from_schema(
44
- schema_path=schema_path,
45
- output_path=output_path,
46
- )
42
+ schema_path=schema_path,
43
+ output_path=output_path,
44
+ )
47
45
 
48
46
  if not output_path:
49
47
  # Print to stdout if no output path
@@ -68,7 +66,6 @@ def docgen(
68
66
  "-m",
69
67
  help="Model to use for generating docstrings",
70
68
  ),
71
-
72
69
  ):
73
70
  """Generate docstrings for Python files using LLMs.
74
71
 
@@ -154,6 +151,7 @@ def install(app_name: str = typer.Argument(..., help="Name of app to install")):
154
151
  typer.echo(f"Error installing app: {e}", err=True)
155
152
  raise typer.Exit(1) from e
156
153
 
154
+
157
155
  @app.command()
158
156
  def init(
159
157
  output_dir: Path | None = typer.Option(
@@ -162,13 +160,13 @@ def init(
162
160
  "-o",
163
161
  help="Output directory for the project (must exist)",
164
162
  ),
165
- app_name: str|None = typer.Option(
163
+ app_name: str | None = typer.Option(
166
164
  None,
167
165
  "--app-name",
168
166
  "-a",
169
167
  help="App name (letters, numbers, hyphens, underscores only)",
170
168
  ),
171
- integration_type: str|None = typer.Option(
169
+ integration_type: str | None = typer.Option(
172
170
  None,
173
171
  "--integration-type",
174
172
  "-i",
@@ -195,7 +193,7 @@ def init(
195
193
  app_name = typer.prompt(
196
194
  "Enter the app name",
197
195
  default="app_name",
198
- prompt_suffix=" (e.g., reddit, youtube): "
196
+ prompt_suffix=" (e.g., reddit, youtube): ",
199
197
  ).strip()
200
198
  validate_pattern(app_name, "app name")
201
199
 
@@ -203,10 +201,10 @@ def init(
203
201
  path_str = typer.prompt(
204
202
  "Enter the output directory for the project",
205
203
  default=str(Path.cwd()),
206
- prompt_suffix=": "
204
+ prompt_suffix=": ",
207
205
  ).strip()
208
206
  output_dir = Path(path_str)
209
-
207
+
210
208
  if not output_dir.exists():
211
209
  try:
212
210
  output_dir.mkdir(parents=True, exist_ok=True)
@@ -219,7 +217,7 @@ def init(
219
217
  f"❌ Failed to create output directory '{output_dir}': {e}",
220
218
  fg=typer.colors.RED,
221
219
  )
222
- raise typer.Exit(code=1)
220
+ raise typer.Exit(code=1) from e
223
221
  elif not output_dir.is_dir():
224
222
  typer.secho(
225
223
  f"❌ Output path '{output_dir}' exists but is not a directory.",
@@ -232,7 +230,7 @@ def init(
232
230
  integration_type = typer.prompt(
233
231
  "Choose the integration type",
234
232
  default="agentr",
235
- prompt_suffix=" (api_key, oauth, agentr, none): "
233
+ prompt_suffix=" (api_key, oauth, agentr, none): ",
236
234
  ).lower()
237
235
  if integration_type not in ("api_key", "oauth", "agentr", "none"):
238
236
  typer.secho(
@@ -240,7 +238,6 @@ def init(
240
238
  fg=typer.colors.RED,
241
239
  )
242
240
  raise typer.Exit(code=1)
243
-
244
241
 
245
242
  typer.secho("🚀 Generating project using cookiecutter...", fg=typer.colors.BLUE)
246
243
  try:
@@ -255,10 +252,11 @@ def init(
255
252
  )
256
253
  except Exception as exc:
257
254
  typer.secho(f"❌ Project generation failed: {exc}", fg=typer.colors.RED)
258
- raise typer.Exit(code=1)
255
+ raise typer.Exit(code=1) from exc
259
256
 
260
257
  project_dir = output_dir / f"universal-mcp-{app_name}"
261
258
  typer.secho(f"✅ Project created at {project_dir}", fg=typer.colors.GREEN)
262
259
 
260
+
263
261
  if __name__ == "__main__":
264
262
  app()
@@ -1,9 +1,9 @@
1
1
  from universal_mcp.config import IntegrationConfig
2
2
  from universal_mcp.integrations.integration import (
3
+ AgentRIntegration,
3
4
  ApiKeyIntegration,
4
5
  Integration,
5
6
  OAuthIntegration,
6
- AgentRIntegration,
7
7
  )
8
8
  from universal_mcp.stores.store import BaseStore
9
9
 
@@ -1,8 +1,6 @@
1
- import os
2
1
  from abc import ABC, abstractmethod
3
2
  from collections.abc import Callable
4
3
  from typing import Any
5
- from urllib.parse import urlparse
6
4
 
7
5
  import httpx
8
6
  from loguru import logger
@@ -11,11 +11,13 @@ class StoreError(Exception):
11
11
 
12
12
  pass
13
13
 
14
+
14
15
  class KeyNotFoundError(StoreError):
15
16
  """Exception raised when a key is not found in the store."""
16
17
 
17
18
  pass
18
19
 
20
+
19
21
  class BaseStore(ABC):
20
22
  """
21
23
  Abstract base class defining the interface for credential stores.
@@ -260,9 +260,7 @@ class ToolManager:
260
260
  try:
261
261
  available_tool_functions = app.list_tools()
262
262
  except TypeError as e:
263
- logger.error(
264
- f"Error calling list_tools for app '{app.name}'. Error: {e}"
265
- )
263
+ logger.error(f"Error calling list_tools for app '{app.name}'. Error: {e}")
266
264
  return
267
265
  except Exception as e:
268
266
  logger.error(f"Failed to get tool list from app '{app.name}': {e}")
@@ -1,9 +1,13 @@
1
- from loguru import logger
2
1
  import os
2
+
3
3
  import httpx
4
+ from loguru import logger
5
+
4
6
  from universal_mcp.config import AppConfig
7
+ from universal_mcp.exceptions import NotAuthorizedError
5
8
  from universal_mcp.utils.singleton import Singleton
6
9
 
10
+
7
11
  class AgentrClient(metaclass=Singleton):
8
12
  """Helper class for AgentR API operations.
9
13
 
@@ -22,7 +26,9 @@ class AgentrClient(metaclass=Singleton):
22
26
  "API key for AgentR is missing. Please visit https://agentr.dev to create an API key, then set it as AGENTR_API_KEY environment variable."
23
27
  )
24
28
  raise ValueError("AgentR API key required - get one at https://agentr.dev")
25
- self.base_url = (base_url or os.getenv("AGENTR_BASE_URL", "https://api.agentr.dev")).rstrip("/")
29
+ self.base_url = (
30
+ base_url or os.getenv("AGENTR_BASE_URL", "https://api.agentr.dev")
31
+ ).rstrip("/")
26
32
 
27
33
  def get_credentials(self, integration_name: str) -> dict:
28
34
  """Get credentials for an integration from the AgentR API.
@@ -87,4 +93,3 @@ class AgentrClient(metaclass=Singleton):
87
93
  response.raise_for_status()
88
94
  data = response.json()
89
95
  return [AppConfig.model_validate(app) for app in data]
90
-
@@ -1,10 +1,11 @@
1
+ import importlib.util
1
2
  import inspect
2
3
  import os
3
- from pathlib import Path
4
- from loguru import logger
5
4
  import shutil
6
- import importlib.util
5
+ from pathlib import Path
6
+
7
7
  from jinja2 import Environment, FileSystemLoader, TemplateError, select_autoescape
8
+ from loguru import logger
8
9
 
9
10
  from universal_mcp.utils.openapi import generate_api_client, load_schema
10
11
 
@@ -26,6 +27,7 @@ def validate_and_load_schema(schema_path: Path) -> dict:
26
27
  echo(f"Error loading schema: {e}", err=True)
27
28
  raise
28
29
 
30
+
29
31
  def get_class_info(module: any) -> tuple[str | None, any]:
30
32
  """Find the main class in the generated module."""
31
33
  for name, obj in inspect.getmembers(module):
@@ -33,19 +35,18 @@ def get_class_info(module: any) -> tuple[str | None, any]:
33
35
  return name, obj
34
36
  return None, None
35
37
 
36
- def generate_readme(
37
- app_dir: Path, folder_name: str, tools: list
38
- ) -> Path:
38
+
39
+ def generate_readme(app_dir: Path, folder_name: str, tools: list) -> Path:
39
40
  """Generate README.md with API documentation.
40
-
41
+
41
42
  Args:
42
43
  app_dir: Directory where the README will be generated
43
44
  folder_name: Name of the application folder
44
45
  tools: List of Function objects from the OpenAPI schema
45
-
46
+
46
47
  Returns:
47
48
  Path to the generated README file
48
-
49
+
49
50
  Raises:
50
51
  FileNotFoundError: If the template directory doesn't exist
51
52
  TemplateError: If there's an error rendering the template
@@ -69,23 +70,19 @@ def generate_readme(
69
70
 
70
71
  try:
71
72
  env = Environment(
72
- loader=FileSystemLoader(template_dir),
73
- autoescape=select_autoescape()
73
+ loader=FileSystemLoader(template_dir), autoescape=select_autoescape()
74
74
  )
75
75
  template = env.get_template("README.md.j2")
76
76
  except Exception as e:
77
77
  logger.error(f"Error loading template: {e}")
78
- raise TemplateError(f"Error loading template: {e}")
78
+ raise TemplateError(f"Error loading template: {e}") from e
79
79
 
80
80
  # Render the template
81
81
  try:
82
- readme_content = template.render(
83
- name=app,
84
- tools=formatted_tools
85
- )
82
+ readme_content = template.render(name=app, tools=formatted_tools)
86
83
  except Exception as e:
87
84
  logger.error(f"Error rendering template: {e}")
88
- raise TemplateError(f"Error rendering template: {e}")
85
+ raise TemplateError(f"Error rendering template: {e}") from e
89
86
 
90
87
  # Write the README file
91
88
  readme_file = app_dir / "README.md"
@@ -95,10 +92,11 @@ def generate_readme(
95
92
  logger.info(f"Documentation generated at: {readme_file}")
96
93
  except Exception as e:
97
94
  logger.error(f"Error writing README file: {e}")
98
- raise IOError(f"Error writing README file: {e}")
95
+ raise OSError(f"Error writing README file: {e}") from e
99
96
 
100
97
  return readme_file
101
98
 
99
+
102
100
  def test_correct_output(gen_file: Path):
103
101
  # Check file is non-empty
104
102
  if gen_file.stat().st_size == 0:
@@ -137,7 +135,6 @@ def generate_api_from_schema(
137
135
  """
138
136
  # Local imports for logging and file operations
139
137
 
140
-
141
138
  logger.info("Starting API generation for schema: %s", schema_path)
142
139
 
143
140
  # 1. Parse and validate schema
@@ -174,10 +171,15 @@ def generate_api_from_schema(
174
171
  f.write(code)
175
172
 
176
173
  if not test_correct_output(gen_file):
177
- logger.error("Generated code validation failed for '%s'. Aborting generation.", gen_file)
174
+ logger.error(
175
+ "Generated code validation failed for '%s'. Aborting generation.", gen_file
176
+ )
178
177
  logger.info("Next steps:")
179
178
  logger.info(" 1) Review your OpenAPI schema for potential mismatches.")
180
- logger.info(" 2) Inspect '%s' for syntax or logic errors in the generated code.", gen_file)
179
+ logger.info(
180
+ " 2) Inspect '%s' for syntax or logic errors in the generated code.",
181
+ gen_file,
182
+ )
181
183
  logger.info(" 3) Correct the issues and re-run the command.")
182
184
  return {"error": "Validation failed. See logs above for detailed instructions."}
183
185
 
@@ -210,13 +212,14 @@ def generate_api_from_schema(
210
212
  client = cls()
211
213
  tools = client.list_tools()
212
214
  except Exception as e:
213
- logger.warning("Failed to instantiate '%s' or list tools: %s", class_name, e)
215
+ logger.warning(
216
+ "Failed to instantiate '%s' or list tools: %s", class_name, e
217
+ )
214
218
  else:
215
219
  logger.warning("No generated class found in module 'temp_module'")
216
220
  readme_file = generate_readme(target_dir, output_path.stem, tools)
217
221
  logger.info("README generated at: %s", readme_file)
218
222
 
219
-
220
223
  # Cleanup intermediate file
221
224
  try:
222
225
  os.remove(gen_file)
@@ -1,15 +1,16 @@
1
1
  import json
2
2
  import re
3
+ from dataclasses import dataclass
3
4
  from pathlib import Path
4
- from typing import Any, Dict, List, Literal
5
- from loguru import logger
5
+ from typing import Any, Literal
6
6
 
7
7
  import yaml
8
8
  from jinja2 import Environment, FileSystemLoader, select_autoescape
9
- from dataclasses import dataclass
9
+ from loguru import logger
10
+
10
11
 
11
12
  def convert_to_snake_case(identifier: str) -> str:
12
- """
13
+ """
13
14
  Convert a camelCase or PascalCase identifier to snake_case.
14
15
 
15
16
  Args:
@@ -140,15 +141,13 @@ def generate_api_client(schema):
140
141
  # Set up Jinja2 environment
141
142
  env = Environment(
142
143
  loader=FileSystemLoader(Path(__file__).parent.parent / "templates"),
143
- autoescape=select_autoescape()
144
+ autoescape=select_autoescape(),
144
145
  )
145
146
  template = env.get_template("api_client.py.j2")
146
147
 
147
148
  # Render the template
148
149
  class_code = template.render(
149
- class_name=class_name,
150
- base_url=base_url,
151
- methods=methods
150
+ class_name=class_name, base_url=base_url, methods=methods
152
151
  )
153
152
 
154
153
  return class_code
@@ -158,10 +157,10 @@ def generate_api_client(schema):
158
157
  class Function:
159
158
  name: str
160
159
  type: Literal["get", "post", "put", "delete", "patch", "options", "head"]
161
- args: Dict[str, str]
160
+ args: dict[str, str]
162
161
  return_type: str
163
162
  description: str
164
- tags: List[str]
163
+ tags: list[str]
165
164
  implementation: str
166
165
 
167
166
  @property
@@ -171,10 +170,7 @@ class Function:
171
170
 
172
171
 
173
172
  def generate_method_code(
174
- path: str,
175
- method: str,
176
- operation: dict[str, Any],
177
- full_schema: dict[str, Any]
173
+ path: str, method: str, operation: dict[str, Any], full_schema: dict[str, Any]
178
174
  ) -> Function:
179
175
  """
180
176
  Generate a Function object for a single API method.
@@ -205,7 +201,6 @@ def generate_method_code(
205
201
  return "dict[str, Any]"
206
202
  return "Any"
207
203
 
208
-
209
204
  # Determine function name
210
205
  if op_id := operation.get("operationId"):
211
206
  cleaned_id = op_id.replace(".", "_").replace("-", "_")
@@ -240,10 +235,12 @@ def generate_method_code(
240
235
  # Analyze requestBody
241
236
  has_body = "requestBody" in operation
242
237
  body_required = bool(has_body and operation["requestBody"].get("required"))
243
- content = (operation.get("requestBody", {}) or {}).get("content", {}) if has_body else {}
238
+ content = (
239
+ (operation.get("requestBody", {}) or {}).get("content", {}) if has_body else {}
240
+ )
244
241
  is_array_body = False
245
- request_props: Dict[str, Any] = {}
246
- required_fields: List[str] = []
242
+ request_props: dict[str, Any] = {}
243
+ required_fields: list[str] = []
247
244
  if has_body and content:
248
245
  for mime, info in content.items():
249
246
  if not mime.startswith("application/json") or "schema" not in info:
@@ -258,10 +255,12 @@ def generate_method_code(
258
255
  request_props = schema.get("properties", {}) or {}
259
256
  for name, prop_schema in list(request_props.items()):
260
257
  if pre := prop_schema.get("$ref"):
261
- request_props[name] = resolve_schema_reference(pre, full_schema) or prop_schema
258
+ request_props[name] = (
259
+ resolve_schema_reference(pre, full_schema) or prop_schema
260
+ )
262
261
 
263
262
  # Build function arguments with Annotated[type, description]
264
- arg_defs: Dict[str, str] = {}
263
+ arg_defs: dict[str, str] = {}
265
264
  for p in path_params:
266
265
  name = p["name"]
267
266
  ty = map_type(p.get("schema", {}))
@@ -304,7 +303,7 @@ def generate_method_code(
304
303
  # Assemble description
305
304
  summary = operation.get("summary", "")
306
305
  operation_desc = operation.get("description", "")
307
- desc_parts: List[str] = []
306
+ desc_parts: list[str] = []
308
307
  if summary:
309
308
  desc_parts.append(summary)
310
309
  if operation_desc:
@@ -315,25 +314,33 @@ def generate_method_code(
315
314
 
316
315
  # Generate implementation code
317
316
  implementation_lines = []
318
-
317
+
319
318
  # Add parameter validation for required fields
320
319
  for param in path_params + query_params:
321
320
  if param.get("required"):
322
321
  name = param["name"]
323
322
  implementation_lines.append(f"if {name} is None:")
324
- implementation_lines.append(f" raise ValueError(\"Missing required parameter '{name}'\")")
325
-
323
+ implementation_lines.append(
324
+ f" raise ValueError(\"Missing required parameter '{name}'\")"
325
+ )
326
+
326
327
  if has_body and body_required:
327
328
  if is_array_body:
328
329
  implementation_lines.append("if items is None:")
329
- implementation_lines.append(" raise ValueError(\"Missing required parameter 'items'\")")
330
+ implementation_lines.append(
331
+ " raise ValueError(\"Missing required parameter 'items'\")"
332
+ )
330
333
  elif request_props:
331
334
  for prop in required_fields:
332
335
  implementation_lines.append(f"if {prop} is None:")
333
- implementation_lines.append(f" raise ValueError(\"Missing required parameter '{prop}'\")")
336
+ implementation_lines.append(
337
+ f" raise ValueError(\"Missing required parameter '{prop}'\")"
338
+ )
334
339
  else:
335
340
  implementation_lines.append("if request_body is None:")
336
- implementation_lines.append(" raise ValueError(\"Missing required parameter 'request_body'\")")
341
+ implementation_lines.append(
342
+ " raise ValueError(\"Missing required parameter 'request_body'\")"
343
+ )
337
344
 
338
345
  # Build request body
339
346
  if has_body:
@@ -342,35 +349,43 @@ def generate_method_code(
342
349
  elif request_props:
343
350
  implementation_lines.append("request_body = {")
344
351
  for prop in request_props:
345
- implementation_lines.append(f" \"{prop}\": {prop},")
352
+ implementation_lines.append(f' "{prop}": {prop},')
346
353
  implementation_lines.append("}")
347
- implementation_lines.append("request_body = {k: v for k, v in request_body.items() if v is not None}")
354
+ implementation_lines.append(
355
+ "request_body = {k: v for k, v in request_body.items() if v is not None}"
356
+ )
348
357
  else:
349
358
  implementation_lines.append("request_body = request_body")
350
359
 
351
360
  # Build URL with path parameters
352
- path = "/".join([path_params["name"] for path_params in path_params]) or '\"\"'
353
- url = '\"{self.base_url}{path}\"'
354
- implementation_lines.append(f'path = {path}')
355
- implementation_lines.append(f'url = f{url}')
361
+ path = "/".join([path_params["name"] for path_params in path_params]) or '""'
362
+ url = '"{self.base_url}{path}"'
363
+ implementation_lines.append(f"path = {path}")
364
+ implementation_lines.append(f"url = f{url}")
356
365
 
357
366
  # Build query parameters
358
367
  if query_params:
359
368
  implementation_lines.append("query_params = {")
360
369
  for param in query_params:
361
370
  name = param["name"]
362
- implementation_lines.append(f" \"{name}\": {name},")
371
+ implementation_lines.append(f' "{name}": {name},')
363
372
  implementation_lines.append(" }")
364
- implementation_lines.append("query_params = {k: v for k, v in query_params.items() if v is not None}")
373
+ implementation_lines.append(
374
+ "query_params = {k: v for k, v in query_params.items() if v is not None}"
375
+ )
365
376
  else:
366
377
  implementation_lines.append("query_params = {}")
367
378
 
368
379
  # Make the request using the appropriate method
369
380
  http_method = method.lower()
370
381
  if has_body:
371
- implementation_lines.append(f"response = self._{http_method}(url, data=request_body, params=query_params)")
382
+ implementation_lines.append(
383
+ f"response = self._{http_method}(url, data=request_body, params=query_params)"
384
+ )
372
385
  else:
373
- implementation_lines.append(f"response = self._{http_method}(url, params=query_params)")
386
+ implementation_lines.append(
387
+ f"response = self._{http_method}(url, params=query_params)"
388
+ )
374
389
 
375
390
  # Handle response
376
391
  implementation_lines.append("response.raise_for_status()")
@@ -386,12 +401,13 @@ def generate_method_code(
386
401
  return_type=return_type,
387
402
  description=description_text,
388
403
  tags=tags,
389
- implementation=implementation
404
+ implementation=implementation,
390
405
  )
391
406
 
392
407
  logger.debug(f"Generated function: {function}")
393
408
  return function
394
409
 
410
+
395
411
  # Example usage
396
412
  if __name__ == "__main__":
397
413
  # Sample OpenAPI schema
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: universal-mcp
3
- Version: 0.1.13rc3
3
+ Version: 0.1.13rc7
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
  License: MIT
@@ -1,38 +1,38 @@
1
1
  universal_mcp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  universal_mcp/analytics.py,sha256=aGCg0Okpcy06W70qCA9I8_ySOiCgAtzJAIWAdhBsOeA,2212
3
- universal_mcp/cli.py,sha256=kJwUvIG3RJQ62wiZaf8qgO_N7Gnrye7kH5BBOCDXeaQ,8212
3
+ universal_mcp/cli.py,sha256=x9riyxrOLBFGICdaMqxqUrSQTKDiyEcfM0V5uqWSxYc,8186
4
4
  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=7rghGqPTKDqpzjYo7JshagaAEyU8CcLaQ07TjwM4TTU,2808
8
+ universal_mcp/applications/__init__.py,sha256=LmjBvzqvFDYZpoCX4jrfUNbu-pQaJAOxJhyFnIWCi5E,2922
9
9
  universal_mcp/applications/application.py,sha256=0eC9D4HHRwIGpuFusaCxTZ0u64U68VbBpRSxjxGB5y8,8152
10
10
  universal_mcp/integrations/README.md,sha256=lTAPXO2nivcBe1q7JT6PRa6v9Ns_ZersQMIdw-nmwEA,996
11
- universal_mcp/integrations/__init__.py,sha256=fx0dn79M1YhCwxtDy5QKrqtez2fbowhfJtQeWCLm1wU,922
11
+ universal_mcp/integrations/__init__.py,sha256=tg6Yk59AEhwPsrTp0hZQ3NBfmJuYGu2sNCOXuph-h9k,922
12
12
  universal_mcp/integrations/integration.py,sha256=genBiaWuzCs-XCf3UD1j8PQYyGU3GiVO4uupSdJRHnA,12601
13
13
  universal_mcp/servers/README.md,sha256=ytFlgp8-LO0oogMrHkMOp8SvFTwgsKgv7XhBVZGNTbM,2284
14
14
  universal_mcp/servers/__init__.py,sha256=dDtvvMzbWskABlobTZHztrWMb3hbzgidza3BmEmIAD8,474
15
- universal_mcp/servers/server.py,sha256=j_WreQSSed-e_QFFffsuQxhr7XDexiR5Cls50qkdX-g,9439
15
+ universal_mcp/servers/server.py,sha256=0oJQQUiwPdG2q79tzsVv3WPMV5YIFbF14PRvBF-SxMQ,9395
16
16
  universal_mcp/stores/README.md,sha256=jrPh_ow4ESH4BDGaSafilhOVaN8oQ9IFlFW-j5Z5hLA,2465
17
17
  universal_mcp/stores/__init__.py,sha256=quvuwhZnpiSLuojf0NfmBx2xpaCulv3fbKtKaSCEmuM,603
18
- universal_mcp/stores/store.py,sha256=eaS2S2V0pg-9qj5gUup0de7RY1lW8GglA-isJdeg6l8,6898
18
+ universal_mcp/stores/store.py,sha256=lYaFd-9YKC404BPeqzNw_Xm3ziQjksZyvQtaW1yd9FM,6900
19
19
  universal_mcp/templates/README.md.j2,sha256=gNry-IrGUdumhgWyHFVxOKgXf_MR4RFK6SI6jF3Tuns,2564
20
20
  universal_mcp/templates/api_client.py.j2,sha256=972Im7LNUAq3yZTfwDcgivnb-b8u6_JLKWXwoIwXXXQ,908
21
21
  universal_mcp/tools/README.md,sha256=RuxliOFqV1ZEyeBdj3m8UKfkxAsfrxXh-b6V4ZGAk8I,2468
22
22
  universal_mcp/tools/__init__.py,sha256=hVL-elJLwD_K87Gpw_s2_o43sQRPyRNOnxlzt0_Pfn8,72
23
23
  universal_mcp/tools/adapters.py,sha256=2HvpyFiI0zg9dp0XshnG7t6KrVqFHM7hgtmgY1bsHN0,927
24
24
  universal_mcp/tools/func_metadata.py,sha256=f_5LdDNsOu1DpXvDUeZYiJswVmwGZz6IMPtpJJ5B2-Y,7975
25
- universal_mcp/tools/tools.py,sha256=Q1MoYSjKCBnQNv-lFWwbXzrYrVaVp-69bzLLOE5GyrE,12919
25
+ universal_mcp/tools/tools.py,sha256=9YzFbX0YHdz7RrVyKKBx-eyFEnYD4HPoUVtSAftgdk4,12889
26
26
  universal_mcp/utils/__init__.py,sha256=8wi4PGWu-SrFjNJ8U7fr2iFJ1ktqlDmSKj1xYd7KSDc,41
27
- universal_mcp/utils/agentr.py,sha256=RkKDbOu5M2q-_3jSQcTqI1vQdTCsidJri5QufzyUD20,3410
28
- universal_mcp/utils/api_generator.py,sha256=bs7BTuEKWz9FNb49BofsLP29BHL3B0PRCtBaKLXoR0A,7949
27
+ universal_mcp/utils/agentr.py,sha256=3sobve7Odk8pIAZm3RHTX4Rc21rkBClcXQgXXslbSUA,3490
28
+ universal_mcp/utils/api_generator.py,sha256=puD3XaUIYWgEk8I8AbA-pAK-O-T4Xk9v5nrAZZjnH6k,7987
29
29
  universal_mcp/utils/docgen.py,sha256=yGBcBIr7dz3mNMGrCb_-JFsDf-ShmCKWWiPpuEj2SIU,21878
30
30
  universal_mcp/utils/docstring_parser.py,sha256=j7aE-LLnBOPTJI0qXayf0NlYappzxICv5E_hUPNmAlc,11459
31
31
  universal_mcp/utils/dump_app_tools.py,sha256=9bQePJ4ZKzGtcIYrBgLxbKDOZmL7ajIAHhXljT_AlyA,2041
32
32
  universal_mcp/utils/installation.py,sha256=H6woSY5AljEy_m5KgiAlHtNfe8eygOu4ZXNs5Q4H_y4,10307
33
- universal_mcp/utils/openapi.py,sha256=Mojlihskv0xO0RGGhvjjFjVMBJEoRIcE1a2h_Tt9Ir0,15998
33
+ universal_mcp/utils/openapi.py,sha256=xl3cuTkRsLAypZXpHqUPSl3h1m5QNbwOT6c1RD8mlsk,16240
34
34
  universal_mcp/utils/singleton.py,sha256=kolHnbS9yd5C7z-tzaUAD16GgI-thqJXysNi3sZM4No,733
35
- universal_mcp-0.1.13rc3.dist-info/METADATA,sha256=goT3MkIrlpp_BRyvRONlf-SNrE4k_mX_tChpl4Bep1o,12805
36
- universal_mcp-0.1.13rc3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
37
- universal_mcp-0.1.13rc3.dist-info/entry_points.txt,sha256=QlBrVKmA2jIM0q-C-3TQMNJTTWOsOFQvgedBq2rZTS8,56
38
- universal_mcp-0.1.13rc3.dist-info/RECORD,,
35
+ universal_mcp-0.1.13rc7.dist-info/METADATA,sha256=TB7HH8ZFKSojlEVNbg9nKxwEux9fLJujArgWc2SGFqw,12805
36
+ universal_mcp-0.1.13rc7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
37
+ universal_mcp-0.1.13rc7.dist-info/entry_points.txt,sha256=QlBrVKmA2jIM0q-C-3TQMNJTTWOsOFQvgedBq2rZTS8,56
38
+ universal_mcp-0.1.13rc7.dist-info/RECORD,,