universal-mcp 0.1.15rc7__py3-none-any.whl → 0.1.17__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.
@@ -2,7 +2,7 @@ import importlib
2
2
  import os
3
3
  import subprocess
4
4
  import sys
5
- from importlib.metadata import version
5
+ from pathlib import Path
6
6
 
7
7
  from loguru import logger
8
8
 
@@ -18,13 +18,13 @@ from universal_mcp.utils.common import (
18
18
  get_default_repository_path,
19
19
  )
20
20
 
21
- UNIVERSAL_MCP_HOME = os.path.join(os.path.expanduser("~"), ".universal-mcp", "packages")
21
+ UNIVERSAL_MCP_HOME = Path.home() / ".universal-mcp" / "packages"
22
22
 
23
- if not os.path.exists(UNIVERSAL_MCP_HOME):
24
- os.makedirs(UNIVERSAL_MCP_HOME)
23
+ if not UNIVERSAL_MCP_HOME.exists():
24
+ UNIVERSAL_MCP_HOME.mkdir(parents=True, exist_ok=True)
25
25
 
26
26
  # set python path to include the universal-mcp home directory
27
- sys.path.append(UNIVERSAL_MCP_HOME)
27
+ sys.path.append(str(UNIVERSAL_MCP_HOME))
28
28
 
29
29
 
30
30
  # Name are in the format of "app-name", eg, google-calendar
@@ -35,30 +35,34 @@ def _install_or_upgrade_package(package_name: str, repository_path: str):
35
35
  """
36
36
  Helper to install a package via pip from the universal-mcp GitHub repository.
37
37
  """
38
- try:
39
- current_version = version(package_name)
40
- logger.info(f"Current version of {package_name} is {current_version}")
41
- except ImportError:
42
- current_version = None
43
- if current_version is not None:
44
- return
38
+
39
+ uv_path = os.getenv("UV_PATH")
40
+ uv_executable = str(Path(uv_path) / "uv") if uv_path else "uv"
41
+ logger.info(f"Using uv executable: {uv_executable}")
45
42
  cmd = [
46
- "uv",
43
+ uv_executable,
47
44
  "pip",
48
45
  "install",
49
46
  "--upgrade",
50
47
  repository_path,
51
48
  "--target",
52
- UNIVERSAL_MCP_HOME,
49
+ str(UNIVERSAL_MCP_HOME),
53
50
  ]
54
51
  logger.debug(f"Installing package '{package_name}' with command: {' '.join(cmd)}")
55
52
  try:
56
- subprocess.check_call(cmd)
53
+ result = subprocess.run(cmd, capture_output=True, text=True)
54
+ if result.stdout:
55
+ logger.info(f"Command stdout: {result.stdout}")
56
+ if result.stderr:
57
+ logger.info(f"Command stderr: {result.stderr}")
58
+ result.check_returncode()
57
59
  except subprocess.CalledProcessError as e:
58
60
  logger.error(f"Installation failed for '{package_name}': {e}")
59
- raise ModuleNotFoundError(
60
- f"Installation failed for package '{package_name}'"
61
- ) from e
61
+ if e.stdout:
62
+ logger.error(f"Command stdout: {e.stdout}")
63
+ if e.stderr:
64
+ logger.error(f"Command stderr: {e.stderr}")
65
+ raise ModuleNotFoundError(f"Installation failed for package '{package_name}'") from e
62
66
  else:
63
67
  logger.debug(f"Package {package_name} installed successfully")
64
68
 
@@ -72,9 +76,7 @@ def app_from_slug(slug: str):
72
76
  module_path = get_default_module_path(slug)
73
77
  package_name = get_default_package_name(slug)
74
78
  repository_path = get_default_repository_path(slug)
75
- logger.debug(
76
- f"Resolving app for slug '{slug}' → module '{module_path}', class '{class_name}'"
77
- )
79
+ logger.debug(f"Resolving app for slug '{slug}' → module '{module_path}', class '{class_name}'")
78
80
  try:
79
81
  _install_or_upgrade_package(package_name, repository_path)
80
82
  module = importlib.import_module(module_path)
@@ -82,13 +84,9 @@ def app_from_slug(slug: str):
82
84
  logger.debug(f"Loaded class '{class_}' from module '{module_path}'")
83
85
  return class_
84
86
  except ModuleNotFoundError as e:
85
- raise ModuleNotFoundError(
86
- f"Package '{module_path}' not found locally. Please install it first."
87
- ) from e
87
+ raise ModuleNotFoundError(f"Package '{module_path}' not found locally. Please install it first.") from e
88
88
  except AttributeError as e:
89
- raise AttributeError(
90
- f"Class '{class_name}' not found in module '{module_path}'"
91
- ) from e
89
+ raise AttributeError(f"Class '{class_name}' not found in module '{module_path}'") from e
92
90
  except Exception as e:
93
91
  raise Exception(f"Error importing module '{module_path}': {e}") from e
94
92
 
@@ -82,11 +82,9 @@ class APIApplication(BaseApplication):
82
82
  super().__init__(name, **kwargs)
83
83
  self.default_timeout: int = 180
84
84
  self.integration: Integration | None = integration
85
- logger.debug(
86
- f"Initializing APIApplication '{name}' with integration: {integration}"
87
- )
85
+ logger.debug(f"Initializing APIApplication '{name}' with integration: {integration}")
88
86
  self._client: httpx.Client | None = client
89
- self.base_url: str = "" # Initialize, but subclasses should set this
87
+ self.base_url: str = ""
90
88
 
91
89
  def _get_headers(self) -> dict[str, str]:
92
90
  """
@@ -112,11 +110,7 @@ class APIApplication(BaseApplication):
112
110
  return headers
113
111
 
114
112
  # Check if api key is provided
115
- api_key = (
116
- credentials.get("api_key")
117
- or credentials.get("API_KEY")
118
- or credentials.get("apiKey")
119
- )
113
+ api_key = credentials.get("api_key") or credentials.get("API_KEY") or credentials.get("apiKey")
120
114
  if api_key:
121
115
  logger.debug("Using API key from credentials")
122
116
  return {
@@ -148,17 +142,11 @@ class APIApplication(BaseApplication):
148
142
  """
149
143
  if not self._client:
150
144
  headers = self._get_headers()
151
- if not self.base_url:
152
- logger.warning(f"APIApplication '{self.name}' base_url is not set.")
153
- self._client = httpx.Client(
154
- headers=headers, timeout=self.default_timeout
155
- )
156
- else:
157
- self._client = httpx.Client(
158
- base_url=self.base_url,
159
- headers=headers,
160
- timeout=self.default_timeout,
161
- )
145
+ self._client = httpx.Client(
146
+ base_url=self.base_url,
147
+ headers=headers,
148
+ timeout=self.default_timeout,
149
+ )
162
150
  return self._client
163
151
 
164
152
  def _get(self, url: str, params: dict[str, Any] | None = None) -> httpx.Response:
@@ -181,9 +169,7 @@ class APIApplication(BaseApplication):
181
169
  logger.debug(f"GET request successful with status code: {response.status_code}")
182
170
  return response
183
171
 
184
- def _post(
185
- self, url: str, data: dict[str, Any], params: dict[str, Any] | None = None
186
- ) -> httpx.Response:
172
+ def _post(self, url: str, data: dict[str, Any], params: dict[str, Any] | None = None) -> httpx.Response:
187
173
  """
188
174
  Make a POST request to the specified URL.
189
175
 
@@ -198,9 +184,7 @@ class APIApplication(BaseApplication):
198
184
  Raises:
199
185
  httpx.HTTPError: If the request fails
200
186
  """
201
- logger.debug(
202
- f"Making POST request to {url} with params: {params} and data: {data}"
203
- )
187
+ logger.debug(f"Making POST request to {url} with params: {params} and data: {data}")
204
188
  response = httpx.post(
205
189
  url,
206
190
  headers=self._get_headers(),
@@ -208,14 +192,10 @@ class APIApplication(BaseApplication):
208
192
  params=params,
209
193
  )
210
194
  response.raise_for_status()
211
- logger.debug(
212
- f"POST request successful with status code: {response.status_code}"
213
- )
195
+ logger.debug(f"POST request successful with status code: {response.status_code}")
214
196
  return response
215
197
 
216
- def _put(
217
- self, url: str, data: dict[str, Any], params: dict[str, Any] | None = None
218
- ) -> httpx.Response:
198
+ def _put(self, url: str, data: dict[str, Any], params: dict[str, Any] | None = None) -> httpx.Response:
219
199
  """
220
200
  Make a PUT request to the specified URL.
221
201
 
@@ -230,9 +210,7 @@ class APIApplication(BaseApplication):
230
210
  Raises:
231
211
  httpx.HTTPError: If the request fails
232
212
  """
233
- logger.debug(
234
- f"Making PUT request to {url} with params: {params} and data: {data}"
235
- )
213
+ logger.debug(f"Making PUT request to {url} with params: {params} and data: {data}")
236
214
  response = self.client.put(
237
215
  url,
238
216
  json=data,
@@ -259,14 +237,10 @@ class APIApplication(BaseApplication):
259
237
  logger.debug(f"Making DELETE request to {url} with params: {params}")
260
238
  response = self.client.delete(url, params=params, timeout=self.default_timeout)
261
239
  response.raise_for_status()
262
- logger.debug(
263
- f"DELETE request successful with status code: {response.status_code}"
264
- )
240
+ logger.debug(f"DELETE request successful with status code: {response.status_code}")
265
241
  return response
266
242
 
267
- def _patch(
268
- self, url: str, data: dict[str, Any], params: dict[str, Any] | None = None
269
- ) -> httpx.Response:
243
+ def _patch(self, url: str, data: dict[str, Any], params: dict[str, Any] | None = None) -> httpx.Response:
270
244
  """
271
245
  Make a PATCH request to the specified URL.
272
246
 
@@ -281,18 +255,14 @@ class APIApplication(BaseApplication):
281
255
  Raises:
282
256
  httpx.HTTPError: If the request fails
283
257
  """
284
- logger.debug(
285
- f"Making PATCH request to {url} with params: {params} and data: {data}"
286
- )
258
+ logger.debug(f"Making PATCH request to {url} with params: {params} and data: {data}")
287
259
  response = self.client.patch(
288
260
  url,
289
261
  json=data,
290
262
  params=params,
291
263
  )
292
264
  response.raise_for_status()
293
- logger.debug(
294
- f"PATCH request successful with status code: {response.status_code}"
295
- )
265
+ logger.debug(f"PATCH request successful with status code: {response.status_code}")
296
266
  return response
297
267
 
298
268
 
@@ -357,11 +327,7 @@ class GraphQLApplication(BaseApplication):
357
327
  return headers
358
328
 
359
329
  # Check if api key is provided
360
- api_key = (
361
- credentials.get("api_key")
362
- or credentials.get("API_KEY")
363
- or credentials.get("apiKey")
364
- )
330
+ api_key = credentials.get("api_key") or credentials.get("API_KEY") or credentials.get("apiKey")
365
331
  if api_key:
366
332
  logger.debug("Using API key from credentials")
367
333
  return {
@@ -392,9 +358,7 @@ class GraphQLApplication(BaseApplication):
392
358
  if not self._client:
393
359
  headers = self._get_headers()
394
360
  transport = RequestsHTTPTransport(url=self.base_url, headers=headers)
395
- self._client = GraphQLClient(
396
- transport=transport, fetch_schema_from_transport=True
397
- )
361
+ self._client = GraphQLClient(transport=transport, fetch_schema_from_transport=True)
398
362
  return self._client
399
363
 
400
364
  def mutate(
universal_mcp/cli.py CHANGED
@@ -7,8 +7,7 @@ from rich.panel import Panel
7
7
 
8
8
  from universal_mcp.utils.installation import (
9
9
  get_supported_apps,
10
- install_claude,
11
- install_cursor,
10
+ install_app,
12
11
  )
13
12
 
14
13
  # Setup rich console and logging
@@ -101,9 +100,7 @@ def docgen(
101
100
 
102
101
  @app.command()
103
102
  def run(
104
- config_path: Path | None = typer.Option(
105
- None, "--config", "-c", help="Path to the config file"
106
- ),
103
+ config_path: Path | None = typer.Option(None, "--config", "-c", help="Path to the config file"),
107
104
  ):
108
105
  """Run the MCP server"""
109
106
  from universal_mcp.config import ServerConfig
@@ -112,10 +109,7 @@ def run(
112
109
 
113
110
  setup_logger()
114
111
 
115
- if config_path:
116
- config = ServerConfig.model_validate_json(config_path.read_text())
117
- else:
118
- config = ServerConfig()
112
+ config = ServerConfig.model_validate_json(config_path.read_text()) if config_path else ServerConfig()
119
113
  server = server_from_config(config)
120
114
  server.run(transport=config.transport)
121
115
 
@@ -151,14 +145,7 @@ def install(app_name: str = typer.Argument(..., help="Name of app to install")):
151
145
  type=str,
152
146
  )
153
147
  try:
154
- if app_name == "claude":
155
- console.print(f"[blue]Installing mcp server for: {app_name}[/blue]")
156
- install_claude(api_key)
157
- console.print("[green]App installed successfully[/green]")
158
- elif app_name == "cursor":
159
- console.print(f"[blue]Installing mcp server for: {app_name}[/blue]")
160
- install_cursor(api_key)
161
- console.print("[green]App installed successfully[/green]")
148
+ install_app(app_name, api_key)
162
149
  except Exception as e:
163
150
  console.print(f"[red]Error installing app: {e}[/red]")
164
151
  raise typer.Exit(1) from e
@@ -219,18 +206,12 @@ def init(
219
206
  if not output_dir.exists():
220
207
  try:
221
208
  output_dir.mkdir(parents=True, exist_ok=True)
222
- console.print(
223
- f"[green]✅ Created output directory at '{output_dir}'[/green]"
224
- )
209
+ console.print(f"[green]✅ Created output directory at '{output_dir}'[/green]")
225
210
  except Exception as e:
226
- console.print(
227
- f"[red]❌ Failed to create output directory '{output_dir}': {e}[/red]"
228
- )
211
+ console.print(f"[red]❌ Failed to create output directory '{output_dir}': {e}[/red]")
229
212
  raise typer.Exit(code=1) from e
230
213
  elif not output_dir.is_dir():
231
- console.print(
232
- f"[red]❌ Output path '{output_dir}' exists but is not a directory.[/red]"
233
- )
214
+ console.print(f"[red]❌ Output path '{output_dir}' exists but is not a directory.[/red]")
234
215
  raise typer.Exit(code=1)
235
216
 
236
217
  # Integration type
@@ -241,9 +222,7 @@ def init(
241
222
  prompt_suffix=" (api_key, oauth, agentr, none): ",
242
223
  ).lower()
243
224
  if integration_type not in ("api_key", "oauth", "agentr", "none"):
244
- console.print(
245
- "[red]❌ Integration type must be one of: api_key, oauth, agentr, none[/red]"
246
- )
225
+ console.print("[red]❌ Integration type must be one of: api_key, oauth, agentr, none[/red]")
247
226
  raise typer.Exit(code=1)
248
227
 
249
228
  console.print("[blue]🚀 Generating project using cookiecutter...[/blue]")
@@ -264,12 +243,14 @@ def init(
264
243
  project_dir = output_dir / f"{app_name}"
265
244
  console.print(f"✅ Project created at {project_dir}")
266
245
 
246
+
267
247
  @app.command()
268
248
  def preprocess(
269
249
  schema_path: Path = typer.Option(None, "--schema", "-s", help="Path to the OpenAPI schema file."),
270
250
  output_path: Path = typer.Option(None, "--output", "-o", help="Path to save the processed schema."),
271
251
  ):
272
252
  from universal_mcp.utils.openapi.preprocessor import run_preprocessing
253
+
273
254
  """Preprocess an OpenAPI schema using LLM to fill or enhance descriptions."""
274
255
  run_preprocessing(schema_path, output_path)
275
256
 
universal_mcp/config.py CHANGED
@@ -12,9 +12,7 @@ class StoreConfig(BaseModel):
12
12
  type: Literal["memory", "environment", "keyring", "agentr"] = Field(
13
13
  default="memory", description="Type of credential storage to use"
14
14
  )
15
- path: Path | None = Field(
16
- default=None, description="Path to store credentials (if applicable)"
17
- )
15
+ path: Path | None = Field(default=None, description="Path to store credentials (if applicable)")
18
16
 
19
17
 
20
18
  class IntegrationConfig(BaseModel):
@@ -24,24 +22,16 @@ class IntegrationConfig(BaseModel):
24
22
  type: Literal["api_key", "oauth", "agentr", "oauth2"] = Field(
25
23
  default="api_key", description="Type of authentication to use"
26
24
  )
27
- credentials: dict[str, Any] | None = Field(
28
- default=None, description="Integration-specific credentials"
29
- )
30
- store: StoreConfig | None = Field(
31
- default=None, description="Store configuration for credentials"
32
- )
25
+ credentials: dict[str, Any] | None = Field(default=None, description="Integration-specific credentials")
26
+ store: StoreConfig | None = Field(default=None, description="Store configuration for credentials")
33
27
 
34
28
 
35
29
  class AppConfig(BaseModel):
36
30
  """Configuration for individual applications."""
37
31
 
38
32
  name: str = Field(..., description="Name of the application")
39
- integration: IntegrationConfig | None = Field(
40
- default=None, description="Integration configuration"
41
- )
42
- actions: list[str] | None = Field(
43
- default=None, description="List of available actions"
44
- )
33
+ integration: IntegrationConfig | None = Field(default=None, description="Integration configuration")
34
+ actions: list[str] | None = Field(default=None, description="List of available actions")
45
35
 
46
36
 
47
37
  class ServerConfig(BaseSettings):
@@ -56,46 +46,24 @@ class ServerConfig(BaseSettings):
56
46
  )
57
47
 
58
48
  name: str = Field(default="Universal MCP", description="Name of the MCP server")
59
- description: str = Field(
60
- default="Universal MCP", description="Description of the MCP server"
61
- )
62
- api_key: SecretStr | None = Field(
63
- default=None, description="API key for authentication"
64
- )
65
- type: Literal["local", "agentr"] = Field(
66
- default="agentr", description="Type of server deployment"
67
- )
68
- transport: Literal["stdio", "sse", "http"] = Field(
69
- default="stdio", description="Transport protocol to use"
70
- )
71
- port: int = Field(
72
- default=8005, description="Port to run the server on (if applicable)"
73
- )
74
- host: str = Field(
75
- default="localhost", description="Host to bind the server to (if applicable)"
76
- )
77
- apps: list[AppConfig] | None = Field(
78
- default=None, description="List of configured applications"
79
- )
80
- store: StoreConfig | None = Field(
81
- default=None, description="Default store configuration"
82
- )
49
+ description: str = Field(default="Universal MCP", description="Description of the MCP server")
50
+ api_key: SecretStr | None = Field(default=None, description="API key for authentication")
51
+ type: Literal["local", "agentr"] = Field(default="agentr", description="Type of server deployment")
52
+ transport: Literal["stdio", "sse", "http"] = Field(default="stdio", description="Transport protocol to use")
53
+ port: int = Field(default=8005, description="Port to run the server on (if applicable)")
54
+ host: str = Field(default="localhost", description="Host to bind the server to (if applicable)")
55
+ apps: list[AppConfig] | None = Field(default=None, description="List of configured applications")
56
+ store: StoreConfig | None = Field(default=None, description="Default store configuration")
83
57
  debug: bool = Field(default=False, description="Enable debug mode")
84
58
  log_level: str = Field(default="INFO", description="Logging level")
85
- max_connections: int = Field(
86
- default=100, description="Maximum number of concurrent connections"
87
- )
88
- request_timeout: int = Field(
89
- default=60, description="Default request timeout in seconds"
90
- )
59
+ max_connections: int = Field(default=100, description="Maximum number of concurrent connections")
60
+ request_timeout: int = Field(default=60, description="Default request timeout in seconds")
91
61
 
92
62
  @field_validator("log_level", mode="before")
93
63
  def validate_log_level(cls, v: str) -> str:
94
64
  valid_levels = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
95
65
  if v.upper() not in valid_levels:
96
- raise ValueError(
97
- f"Invalid log level. Must be one of: {', '.join(valid_levels)}"
98
- )
66
+ raise ValueError(f"Invalid log level. Must be one of: {', '.join(valid_levels)}")
99
67
  return v.upper()
100
68
 
101
69
  @field_validator("port", mode="before")
@@ -8,9 +8,7 @@ from universal_mcp.integrations.integration import (
8
8
  from universal_mcp.stores.store import BaseStore
9
9
 
10
10
 
11
- def integration_from_config(
12
- config: IntegrationConfig, store: BaseStore | None = None, **kwargs
13
- ) -> Integration:
11
+ def integration_from_config(config: IntegrationConfig, store: BaseStore | None = None, **kwargs) -> Integration:
14
12
  if config.type == "api_key":
15
13
  return ApiKeyIntegration(config.name, store=store, **kwargs)
16
14
  elif config.type == "agentr":
universal_mcp/logger.py CHANGED
@@ -5,6 +5,21 @@ from pathlib import Path
5
5
  from loguru import logger
6
6
 
7
7
 
8
+ def get_log_file_path(app_name: str = "universal-mcp") -> Path:
9
+ """Get a standardized log file path for an application.
10
+
11
+ Args:
12
+ app_name: Name of the application.
13
+
14
+ Returns:
15
+ Path to the log file in the format: logs/{app_name}/{app_name}_{date}.log
16
+ """
17
+ date_str = datetime.now().strftime("%Y%m%d")
18
+ home = Path.home()
19
+ log_dir = home / ".universal-mcp" / "logs"
20
+ return log_dir / f"{app_name}_{date_str}.log"
21
+
22
+
8
23
  def setup_logger(
9
24
  log_file: Path | None = None,
10
25
  rotation: str = "10 MB",
@@ -35,34 +50,21 @@ def setup_logger(
35
50
  backtrace=True,
36
51
  diagnose=True,
37
52
  )
53
+ if not log_file:
54
+ log_file = get_log_file_path()
38
55
 
39
- # Add file handler if log_file is specified
40
- if log_file:
41
- # Ensure log directory exists
42
- log_file.parent.mkdir(parents=True, exist_ok=True)
43
-
44
- logger.add(
45
- sink=str(log_file),
46
- rotation=rotation,
47
- retention=retention,
48
- compression=compression,
49
- level=level,
50
- format=format,
51
- enqueue=True,
52
- backtrace=True,
53
- diagnose=True,
54
- )
55
-
56
-
57
- def get_log_file_path(app_name: str) -> Path:
58
- """Get a standardized log file path for an application.
56
+ # Ensure log directory exists
57
+ log_file.parent.mkdir(parents=True, exist_ok=True)
59
58
 
60
- Args:
61
- app_name: Name of the application.
62
-
63
- Returns:
64
- Path to the log file in the format: logs/{app_name}/{app_name}_{date}.log
65
- """
66
- date_str = datetime.now().strftime("%Y%m%d")
67
- log_dir = Path("logs") / app_name
68
- return log_dir / f"{app_name}_{date_str}.log"
59
+ logger.add(
60
+ sink=str(log_file),
61
+ rotation=rotation,
62
+ retention=retention,
63
+ compression=compression,
64
+ level=level,
65
+ format=format,
66
+ enqueue=True,
67
+ backtrace=True,
68
+ diagnose=True,
69
+ )
70
+ logger.info(f"Logging to {log_file}")
@@ -28,9 +28,7 @@ class BaseServer(FastMCP, ABC):
28
28
 
29
29
  def __init__(self, config: ServerConfig, **kwargs):
30
30
  super().__init__(config.name, config.description, port=config.port, **kwargs)
31
- logger.info(
32
- f"Initializing server: {config.name} ({config.type}) with store: {config.store}"
33
- )
31
+ logger.info(f"Initializing server: {config.name} ({config.type}) with store: {config.store}")
34
32
 
35
33
  self.config = config # Store config at base level for consistency
36
34
  self._tool_manager = ToolManager(warn_on_duplicate_tools=True)
@@ -67,19 +65,13 @@ class BaseServer(FastMCP, ABC):
67
65
  """
68
66
  if isinstance(result, str):
69
67
  return [TextContent(type="text", text=result)]
70
- elif isinstance(result, list) and all(
71
- isinstance(item, TextContent) for item in result
72
- ):
68
+ elif isinstance(result, list) and all(isinstance(item, TextContent) for item in result):
73
69
  return result
74
70
  else:
75
- logger.warning(
76
- f"Tool returned unexpected type: {type(result)}. Wrapping in TextContent."
77
- )
71
+ logger.warning(f"Tool returned unexpected type: {type(result)}. Wrapping in TextContent.")
78
72
  return [TextContent(type="text", text=str(result))]
79
73
 
80
- async def call_tool(
81
- self, name: str, arguments: dict[str, Any]
82
- ) -> list[TextContent]:
74
+ async def call_tool(self, name: str, arguments: dict[str, Any]) -> list[TextContent]:
83
75
  """Call a tool with comprehensive error handling.
84
76
 
85
77
  Args:
@@ -139,9 +131,7 @@ class LocalServer(BaseServer):
139
131
  """
140
132
  try:
141
133
  integration = (
142
- integration_from_config(app_config.integration, store=self.store)
143
- if app_config.integration
144
- else None
134
+ integration_from_config(app_config.integration, store=self.store) if app_config.integration else None
145
135
  )
146
136
  return app_from_slug(app_config.name)(integration=integration)
147
137
  except Exception as e:
@@ -200,9 +190,7 @@ class AgentRServer(BaseServer):
200
190
  """
201
191
  try:
202
192
  integration = (
203
- AgentRIntegration(
204
- name=app_config.integration.name, api_key=self.client.api_key
205
- )
193
+ AgentRIntegration(name=app_config.integration.name, api_key=self.client.api_key)
206
194
  if app_config.integration
207
195
  else None
208
196
  )
@@ -15,9 +15,7 @@ from pydantic_core import PydanticUndefined
15
15
 
16
16
 
17
17
  def _get_typed_annotation(annotation: Any, globalns: dict[str, Any]) -> Any:
18
- def try_eval_type(
19
- value: Any, globalns: dict[str, Any], localns: dict[str, Any]
20
- ) -> tuple[Any, bool]:
18
+ def try_eval_type(value: Any, globalns: dict[str, Any], localns: dict[str, Any]) -> tuple[Any, bool]:
21
19
  try:
22
20
  return eval_type_backport(value, globalns, localns), True
23
21
  except NameError:
@@ -169,9 +167,7 @@ class FuncMetadata(BaseModel):
169
167
  globalns = getattr(func, "__globals__", {})
170
168
  for param in params.values():
171
169
  if param.name.startswith("_"):
172
- raise InvalidSignature(
173
- f"Parameter {param.name} of {func.__name__} cannot start with '_'"
174
- )
170
+ raise InvalidSignature(f"Parameter {param.name} of {func.__name__} cannot start with '_'")
175
171
  if param.name in skip_names:
176
172
  continue
177
173
  annotation = param.annotation
@@ -180,11 +176,7 @@ class FuncMetadata(BaseModel):
180
176
  if annotation is None:
181
177
  annotation = Annotated[
182
178
  None,
183
- Field(
184
- default=param.default
185
- if param.default is not inspect.Parameter.empty
186
- else PydanticUndefined
187
- ),
179
+ Field(default=param.default if param.default is not inspect.Parameter.empty else PydanticUndefined),
188
180
  ]
189
181
 
190
182
  # Untyped field
@@ -198,15 +190,9 @@ class FuncMetadata(BaseModel):
198
190
 
199
191
  field_info = FieldInfo.from_annotated_attribute(
200
192
  _get_typed_annotation(annotation, globalns),
201
- param.default
202
- if param.default is not inspect.Parameter.empty
203
- else PydanticUndefined,
193
+ param.default if param.default is not inspect.Parameter.empty else PydanticUndefined,
204
194
  )
205
- if (
206
- not field_info.title
207
- and arg_description
208
- and arg_description.get(param.name)
209
- ):
195
+ if not field_info.title and arg_description and arg_description.get(param.name):
210
196
  field_info.title = arg_description.get(param.name)
211
197
  dynamic_pydantic_model_params[param.name] = (
212
198
  field_info.annotation,