mcp-dbutils 0.8.0__py3-none-any.whl → 0.10.0__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.
mcp_dbutils/config.py CHANGED
@@ -7,14 +7,14 @@ from dataclasses import dataclass, field
7
7
  from typing import Optional, Dict, Any, Literal
8
8
  from pathlib import Path
9
9
 
10
- # Supported database types
11
- DBType = Literal['sqlite', 'postgres']
10
+ # Supported connection types
11
+ ConnectionType = Literal['sqlite', 'postgres']
12
12
 
13
- class DatabaseConfig(ABC):
14
- """Base class for database configuration"""
13
+ class ConnectionConfig(ABC):
14
+ """Base class for connection configuration"""
15
15
 
16
16
  debug: bool = False
17
- type: DBType # Database type
17
+ type: ConnectionType # Connection type
18
18
 
19
19
  @abstractmethod
20
20
  def get_connection_params(self) -> Dict[str, Any]:
@@ -39,19 +39,19 @@ class DatabaseConfig(ABC):
39
39
  with open(yaml_path, 'r', encoding='utf-8') as f:
40
40
  config = yaml.safe_load(f)
41
41
 
42
- if not config or 'databases' not in config:
43
- raise ValueError("Configuration file must contain 'databases' section")
42
+ if not config or 'connections' not in config:
43
+ raise ValueError("Configuration file must contain 'connections' section")
44
44
 
45
45
  # Validate type field in each database configuration
46
- databases = config['databases']
47
- for db_name, db_config in databases.items():
46
+ connections = config['connections']
47
+ for conn_name, db_config in connections.items():
48
48
  if 'type' not in db_config:
49
- raise ValueError(f"Database configuration {db_name} missing required 'type' field")
49
+ raise ValueError(f"Database configuration {conn_name} missing required 'type' field")
50
50
  db_type = db_config['type']
51
51
  if db_type not in ('sqlite', 'postgres'):
52
- raise ValueError(f"Invalid type value in database configuration {db_name}: {db_type}")
52
+ raise ValueError(f"Invalid type value in database configuration {conn_name}: {db_type}")
53
53
 
54
- return databases
54
+ return connections
55
55
 
56
56
  @classmethod
57
57
  def get_debug_mode(cls) -> bool:
@@ -1,6 +1,6 @@
1
1
  """PostgreSQL module"""
2
2
 
3
- from .handler import PostgresHandler
4
- from .config import PostgresConfig
3
+ from .handler import PostgreSQLHandler
4
+ from .config import PostgreSQLConfig
5
5
 
6
- __all__ = ['PostgresHandler', 'PostgresConfig']
6
+ __all__ = ['PostgreSQLHandler', 'PostgreSQLConfig']
@@ -1,28 +1,35 @@
1
1
  """PostgreSQL configuration module"""
2
2
  from dataclasses import dataclass
3
3
  from typing import Optional, Dict, Any, Literal
4
- from urllib.parse import urlparse
5
- from ..config import DatabaseConfig
4
+ from urllib.parse import urlparse, parse_qs
5
+ from ..config import ConnectionConfig
6
6
 
7
- def parse_jdbc_url(jdbc_url: str) -> Dict[str, str]:
8
- """Parse JDBC URL into connection parameters
7
+ @dataclass
8
+ class SSLConfig:
9
+ """SSL configuration for PostgreSQL connection"""
10
+ mode: Literal['disable', 'require', 'verify-ca', 'verify-full'] = 'disable'
11
+ cert: Optional[str] = None
12
+ key: Optional[str] = None
13
+ root: Optional[str] = None
14
+
15
+ def parse_url(url: str) -> Dict[str, Any]:
16
+ """Parse PostgreSQL URL into connection parameters
9
17
 
10
18
  Args:
11
- jdbc_url: JDBC URL (e.g. jdbc:postgresql://host:port/dbname)
19
+ url: URL (e.g. postgresql://host:port/dbname?sslmode=verify-full)
12
20
 
13
21
  Returns:
14
- Dictionary of connection parameters
22
+ Dictionary of connection parameters including SSL settings
15
23
  """
16
- if not jdbc_url.startswith('jdbc:postgresql://'):
17
- raise ValueError("Invalid PostgreSQL JDBC URL format")
24
+ if not url.startswith('postgresql://'):
25
+ raise ValueError("Invalid PostgreSQL URL format")
18
26
 
19
- # Remove jdbc: prefix and ensure no credentials in URL
20
- url = jdbc_url[5:]
21
27
  if '@' in url:
22
- raise ValueError("JDBC URL should not contain credentials. Please provide username and password separately.")
28
+ raise ValueError("URL should not contain credentials. Please provide username and password separately.")
23
29
 
24
- # Parse URL
30
+ # Parse URL and query parameters
25
31
  parsed = urlparse(url)
32
+ query_params = parse_qs(parsed.query)
26
33
 
27
34
  params = {
28
35
  'host': parsed.hostname or 'localhost',
@@ -31,12 +38,30 @@ def parse_jdbc_url(jdbc_url: str) -> Dict[str, str]:
31
38
  }
32
39
 
33
40
  if not params['dbname']:
34
- raise ValueError("Database name must be specified in URL")
41
+ raise ValueError("PostgreSQL database name must be specified in URL")
42
+
43
+ # Parse SSL parameters if present
44
+ ssl_params = {}
45
+ if 'sslmode' in query_params:
46
+ mode = query_params['sslmode'][0]
47
+ if mode not in ['disable', 'require', 'verify-ca', 'verify-full']:
48
+ raise ValueError(f"Invalid sslmode: {mode}")
49
+ ssl_params['mode'] = mode
50
+
51
+ if 'sslcert' in query_params:
52
+ ssl_params['cert'] = query_params['sslcert'][0]
53
+ if 'sslkey' in query_params:
54
+ ssl_params['key'] = query_params['sslkey'][0]
55
+ if 'sslrootcert' in query_params:
56
+ ssl_params['root'] = query_params['sslrootcert'][0]
57
+
58
+ if ssl_params:
59
+ params['ssl'] = SSLConfig(**ssl_params)
35
60
 
36
61
  return params
37
62
 
38
63
  @dataclass
39
- class PostgresConfig(DatabaseConfig):
64
+ class PostgreSQLConfig(ConnectionConfig):
40
65
  dbname: str
41
66
  user: str
42
67
  password: str
@@ -44,39 +69,41 @@ class PostgresConfig(DatabaseConfig):
44
69
  port: str = '5432'
45
70
  local_host: Optional[str] = None
46
71
  type: Literal['postgres'] = 'postgres'
72
+ url: Optional[str] = None
73
+ ssl: Optional[SSLConfig] = None
47
74
 
48
75
  @classmethod
49
- def from_yaml(cls, yaml_path: str, db_name: str, local_host: Optional[str] = None) -> 'PostgresConfig':
76
+ def from_yaml(cls, yaml_path: str, db_name: str, local_host: Optional[str] = None) -> 'PostgreSQLConfig':
50
77
  """Create configuration from YAML file
51
78
 
52
79
  Args:
53
80
  yaml_path: Path to YAML configuration file
54
- db_name: Database configuration name to use
81
+ db_name: Connection configuration name to use
55
82
  local_host: Optional local host address
56
83
  """
57
84
  configs = cls.load_yaml_config(yaml_path)
58
85
  if not db_name:
59
- raise ValueError("Database name must be specified")
86
+ raise ValueError("Connection name must be specified")
60
87
  if db_name not in configs:
61
88
  available_dbs = list(configs.keys())
62
- raise ValueError(f"Database configuration not found: {db_name}. Available configurations: {available_dbs}")
89
+ raise ValueError(f"Connection configuration not found: {db_name}. Available configurations: {available_dbs}")
63
90
 
64
91
  db_config = configs[db_name]
65
92
  if 'type' not in db_config:
66
- raise ValueError("Database configuration must include 'type' field")
93
+ raise ValueError("Connection configuration must include 'type' field")
67
94
  if db_config['type'] != 'postgres':
68
95
  raise ValueError(f"Configuration is not PostgreSQL type: {db_config['type']}")
69
96
 
70
97
  # Check required credentials
71
98
  if not db_config.get('user'):
72
- raise ValueError("User must be specified in database configuration")
99
+ raise ValueError("User must be specified in connection configuration")
73
100
  if not db_config.get('password'):
74
- raise ValueError("Password must be specified in database configuration")
101
+ raise ValueError("Password must be specified in connection configuration")
75
102
 
76
103
  # Get connection parameters
77
- if 'jdbc_url' in db_config:
78
- # Parse JDBC URL for connection parameters
79
- params = parse_jdbc_url(db_config['jdbc_url'])
104
+ if 'url' in db_config:
105
+ # Parse URL for connection parameters
106
+ params = parse_url(db_config['url'])
80
107
  config = cls(
81
108
  dbname=params['dbname'],
82
109
  user=db_config['user'],
@@ -84,14 +111,34 @@ class PostgresConfig(DatabaseConfig):
84
111
  host=params['host'],
85
112
  port=params['port'],
86
113
  local_host=local_host,
114
+ url=db_config['url'],
115
+ ssl=params.get('ssl')
87
116
  )
88
117
  else:
89
118
  if not db_config.get('dbname'):
90
- raise ValueError("Database name must be specified in configuration")
119
+ raise ValueError("PostgreSQL database name must be specified in configuration")
91
120
  if not db_config.get('host'):
92
- raise ValueError("Host must be specified in configuration")
121
+ raise ValueError("Host must be specified in connection configuration")
93
122
  if not db_config.get('port'):
94
- raise ValueError("Port must be specified in configuration")
123
+ raise ValueError("Port must be specified in connection configuration")
124
+
125
+ # Parse SSL configuration if present
126
+ ssl_config = None
127
+ if 'ssl' in db_config:
128
+ ssl_params = db_config['ssl']
129
+ if not isinstance(ssl_params, dict):
130
+ raise ValueError("SSL configuration must be a dictionary")
131
+
132
+ if ssl_params.get('mode') not in [None, 'disable', 'require', 'verify-ca', 'verify-full']:
133
+ raise ValueError(f"Invalid sslmode: {ssl_params.get('mode')}")
134
+
135
+ ssl_config = SSLConfig(
136
+ mode=ssl_params.get('mode', 'disable'),
137
+ cert=ssl_params.get('cert'),
138
+ key=ssl_params.get('key'),
139
+ root=ssl_params.get('root')
140
+ )
141
+
95
142
  config = cls(
96
143
  dbname=db_config['dbname'],
97
144
  user=db_config['user'],
@@ -99,25 +146,26 @@ class PostgresConfig(DatabaseConfig):
99
146
  host=db_config['host'],
100
147
  port=str(db_config['port']),
101
148
  local_host=local_host,
149
+ ssl=ssl_config
102
150
  )
103
151
  config.debug = cls.get_debug_mode()
104
152
  return config
105
153
 
106
154
  @classmethod
107
- def from_jdbc_url(cls, jdbc_url: str, user: str, password: str,
108
- local_host: Optional[str] = None) -> 'PostgresConfig':
109
- """Create configuration from JDBC URL and credentials
155
+ def from_url(cls, url: str, user: str, password: str,
156
+ local_host: Optional[str] = None) -> 'PostgreSQLConfig':
157
+ """Create configuration from URL and credentials
110
158
 
111
159
  Args:
112
- jdbc_url: JDBC URL (jdbc:postgresql://host:port/dbname)
113
- user: Username for database connection
114
- password: Password for database connection
160
+ url: URL (postgresql://host:port/dbname)
161
+ user: Username for connection
162
+ password: Password for connection
115
163
  local_host: Optional local host address
116
164
 
117
165
  Raises:
118
166
  ValueError: If URL format is invalid or required parameters are missing
119
167
  """
120
- params = parse_jdbc_url(jdbc_url)
168
+ params = parse_url(url)
121
169
 
122
170
  config = cls(
123
171
  dbname=params['dbname'],
@@ -126,6 +174,8 @@ class PostgresConfig(DatabaseConfig):
126
174
  host=params['host'],
127
175
  port=params['port'],
128
176
  local_host=local_host,
177
+ url=url,
178
+ ssl=params.get('ssl')
129
179
  )
130
180
  config.debug = cls.get_debug_mode()
131
181
  return config
@@ -139,6 +189,17 @@ class PostgresConfig(DatabaseConfig):
139
189
  'host': self.local_host or self.host,
140
190
  'port': self.port
141
191
  }
192
+
193
+ # Add SSL parameters if configured
194
+ if self.ssl:
195
+ params['sslmode'] = self.ssl.mode
196
+ if self.ssl.cert:
197
+ params['sslcert'] = self.ssl.cert
198
+ if self.ssl.key:
199
+ params['sslkey'] = self.ssl.key
200
+ if self.ssl.root:
201
+ params['sslrootcert'] = self.ssl.root
202
+
142
203
  return {k: v for k, v in params.items() if v}
143
204
 
144
205
  def get_masked_connection_info(self) -> Dict[str, Any]: