mcp-proxy-adapter 6.2.21__py3-none-any.whl → 6.2.23__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.
@@ -350,6 +350,9 @@ def create_app(title: Optional[str] = None, description: Optional[str] = None, v
350
350
  )
351
351
 
352
352
  # Setup middleware using the new middleware package
353
+ print(f"DEBUG create_app: calling setup_middleware with config type: {type(current_config)}")
354
+ if hasattr(current_config, 'keys'):
355
+ print(f"DEBUG create_app: current_config keys: {list(current_config.keys())}")
353
356
  setup_middleware(app, current_config)
354
357
 
355
358
  # Use custom OpenAPI schema
@@ -23,18 +23,21 @@ def setup_middleware(app: FastAPI, app_config: Optional[Dict[str, Any]] = None)
23
23
  # Use provided configuration or fallback to global config
24
24
  current_config = app_config if app_config is not None else config.get_all()
25
25
 
26
+ # Add protocol middleware FIRST (before other middleware)
27
+ setup_protocol_middleware(app, current_config)
28
+
26
29
  # Create middleware factory
27
30
  factory = MiddlewareFactory(app, current_config)
28
-
31
+
29
32
  # Validate middleware configuration
30
33
  if not factory.validate_middleware_config():
31
34
  logger.error("Middleware configuration validation failed")
32
35
  raise SystemExit(1)
33
-
36
+
34
37
  logger.info("Using unified security middleware")
35
38
  middleware_list = factory.create_all_middleware()
36
-
37
- # Add middleware to application
39
+
40
+ # Add middleware to application AFTER protocol middleware
38
41
  for middleware in middleware_list:
39
42
  # For ASGI middleware, we need to wrap the application
40
43
  if hasattr(middleware, 'dispatch'):
@@ -43,9 +46,6 @@ def setup_middleware(app: FastAPI, app_config: Optional[Dict[str, Any]] = None)
43
46
  else:
44
47
  logger.warning(f"Middleware {middleware.__class__.__name__} doesn't have dispatch method")
45
48
 
46
- # Add protocol middleware with current configuration
47
- setup_protocol_middleware(app, current_config)
48
-
49
49
  # Log middleware information
50
50
  middleware_info = factory.get_middleware_info()
51
51
  logger.info(f"Middleware setup completed:")
@@ -24,15 +24,37 @@ class ProtocolMiddleware(BaseHTTPMiddleware):
24
24
  def __init__(self, app, app_config: Optional[Dict[str, Any]] = None):
25
25
  """
26
26
  Initialize protocol middleware.
27
-
27
+
28
28
  Args:
29
29
  app: FastAPI application
30
30
  app_config: Application configuration dictionary (optional)
31
31
  """
32
32
  super().__init__(app)
33
- self.app_config = app_config
33
+ # Normalize config to dictionary
34
+ normalized_config: Optional[Dict[str, Any]]
35
+ if app_config is None:
36
+ normalized_config = None
37
+ elif hasattr(app_config, 'get_all'):
38
+ try:
39
+ normalized_config = app_config.get_all()
40
+ except Exception as e:
41
+ logger.debug(f"ProtocolMiddleware - Error calling get_all(): {e}, type: {type(app_config)}")
42
+ normalized_config = None
43
+ elif hasattr(app_config, 'keys'):
44
+ normalized_config = app_config # Already dict-like
45
+ else:
46
+ logger.debug(f"ProtocolMiddleware - app_config is not dict-like, type: {type(app_config)}, value: {repr(app_config)}")
47
+ normalized_config = None
48
+
49
+ logger.debug(f"ProtocolMiddleware - normalized_config type: {type(normalized_config)}")
50
+ if normalized_config:
51
+ logger.debug(f"ProtocolMiddleware - protocols in config: {'protocols' in normalized_config}")
52
+ if 'protocols' in normalized_config:
53
+ logger.debug(f"ProtocolMiddleware - protocols type: {type(normalized_config['protocols'])}")
54
+
55
+ self.app_config = normalized_config
34
56
  # Get protocol manager with current configuration
35
- self.protocol_manager = get_protocol_manager(app_config)
57
+ self.protocol_manager = get_protocol_manager(normalized_config)
36
58
 
37
59
  def update_config(self, new_config: Dict[str, Any]):
38
60
  """
@@ -41,21 +63,31 @@ class ProtocolMiddleware(BaseHTTPMiddleware):
41
63
  Args:
42
64
  new_config: New configuration dictionary
43
65
  """
44
- self.app_config = new_config
45
- self.protocol_manager = get_protocol_manager(new_config)
66
+ # Normalize new config
67
+ if hasattr(new_config, 'get_all'):
68
+ try:
69
+ self.app_config = new_config.get_all()
70
+ except Exception:
71
+ self.app_config = None
72
+ elif hasattr(new_config, 'keys'):
73
+ self.app_config = new_config
74
+ else:
75
+ self.app_config = None
76
+ self.protocol_manager = get_protocol_manager(self.app_config)
46
77
  logger.info("Protocol middleware configuration updated")
47
78
 
48
79
  async def dispatch(self, request: Request, call_next: Callable) -> Response:
49
80
  """
50
81
  Process request through protocol middleware.
51
-
82
+
52
83
  Args:
53
84
  request: Incoming request
54
85
  call_next: Next middleware/endpoint function
55
-
86
+
56
87
  Returns:
57
88
  Response object
58
89
  """
90
+ logger.info(f"ProtocolMiddleware.dispatch called for {request.method} {request.url.path}")
59
91
  try:
60
92
  # Get protocol from request
61
93
  protocol = self._get_request_protocol(request)
@@ -132,22 +164,37 @@ class ProtocolMiddleware(BaseHTTPMiddleware):
132
164
  def setup_protocol_middleware(app, app_config: Optional[Dict[str, Any]] = None):
133
165
  """
134
166
  Setup protocol middleware for FastAPI application.
135
-
167
+
136
168
  Args:
137
169
  app: FastAPI application
138
170
  app_config: Application configuration dictionary (optional)
139
171
  """
172
+ logger.info(f"setup_protocol_middleware - app_config type: {type(app_config)}")
173
+
140
174
  # Check if protocol management is enabled
141
175
  if app_config is None:
142
176
  from mcp_proxy_adapter.config import config
143
177
  app_config = config.get_all()
144
-
145
- protocols_config = app_config.get("protocols", {})
146
- enabled = protocols_config.get("enabled", True)
147
-
178
+ logger.info(f"setup_protocol_middleware - loaded from global config, type: {type(app_config)}")
179
+
180
+ logger.info(f"setup_protocol_middleware - final app_config type: {type(app_config)}")
181
+
182
+ if hasattr(app_config, 'get'):
183
+ logger.info(f"setup_protocol_middleware - app_config keys: {list(app_config.keys()) if hasattr(app_config, 'keys') else 'no keys'}")
184
+ protocols_config = app_config.get("protocols", {})
185
+ logger.info(f"setup_protocol_middleware - protocols_config type: {type(protocols_config)}")
186
+ enabled = protocols_config.get("enabled", True) if hasattr(protocols_config, 'get') else True
187
+ else:
188
+ logger.info(f"setup_protocol_middleware - app_config is not dict-like: {repr(app_config)}")
189
+ enabled = True
190
+
191
+ logger.info(f"setup_protocol_middleware - protocol management enabled: {enabled}")
192
+
148
193
  if enabled:
149
194
  # Create protocol middleware with current configuration
195
+ logger.info(f"setup_protocol_middleware - creating ProtocolMiddleware with config type: {type(app_config)}")
150
196
  middleware = ProtocolMiddleware(app, app_config)
197
+ logger.info(f"setup_protocol_middleware - adding middleware to app")
151
198
  app.add_middleware(ProtocolMiddleware, app_config=app_config)
152
199
  logger.info("Protocol middleware added to application")
153
200
  else:
@@ -65,12 +65,17 @@ class UnifiedSecurityMiddleware(BaseHTTPMiddleware):
65
65
  # Use framework's FastAPI middleware
66
66
  self.framework_middleware = self.security_integration.security_manager.create_fastapi_middleware()
67
67
  logger.info("Using mcp_security_framework FastAPI middleware")
68
+ # IMPORTANT: Don't replace self.app! This breaks the middleware chain.
69
+ # Instead, store the framework middleware for use in dispatch method.
70
+ logger.info("Framework middleware will be used in dispatch method")
68
71
  except Exception as e:
69
72
  logger.error(f"Security framework integration failed: {e}")
70
73
  # Instead of raising error, log warning and continue without security
71
74
  logger.warning("Continuing without security framework - some security features will be disabled")
72
75
  self.security_integration = None
73
76
  self.framework_middleware = None
77
+ # Keep original app in place when framework middleware is unavailable
78
+ # BaseHTTPMiddleware initialized it via super().__init__(app)
74
79
 
75
80
  logger.info("Unified security middleware initialized")
76
81
 
@@ -86,14 +91,45 @@ class UnifiedSecurityMiddleware(BaseHTTPMiddleware):
86
91
  Response object
87
92
  """
88
93
  try:
89
- # Use framework middleware if available
90
- if self.framework_middleware is not None:
91
- return await self.framework_middleware.dispatch(request, call_next)
94
+ # Simple built-in API key enforcement if configured
95
+ security_cfg = self.config.get("security", {}) if isinstance(self.config, dict) else {}
96
+ auth_cfg = security_cfg.get("auth", {})
97
+ permissions_cfg = security_cfg.get("permissions", {})
98
+ public_paths = set(auth_cfg.get("public_paths", ["/health", "/docs", "/openapi.json"]))
99
+ # JSON-RPC endpoint must not be public when API key is required
100
+ public_paths.discard("/api/jsonrpc")
101
+ path = request.url.path
102
+ methods = set(auth_cfg.get("methods", []))
103
+ api_keys: Dict[str, str] = auth_cfg.get("api_keys", {}) or {}
104
+
105
+ # Enforce only for non-public paths when api_key method configured
106
+ if security_cfg.get("enabled", False) and ("api_key" in methods) and (path not in public_paths):
107
+ # Accept either X-API-Key or Authorization: Bearer
108
+ token = request.headers.get("X-API-Key")
109
+ if not token:
110
+ authz = request.headers.get("Authorization", "")
111
+ if authz.startswith("Bearer "):
112
+ token = authz[7:]
113
+ if not token or (api_keys and token not in api_keys):
114
+ from fastapi.responses import JSONResponse
115
+ return JSONResponse(status_code=401, content={
116
+ "error": {
117
+ "code": 401,
118
+ "message": "Unauthorized: invalid or missing API key",
119
+ "type": "authentication_error"
120
+ }
121
+ })
122
+
123
+ # Continue with framework middleware or regular flow
124
+ if self.framework_middleware:
125
+ # If framework middleware exists, we need to call it manually
126
+ # This is a workaround since we can't chain ASGI apps in BaseHTTPMiddleware
127
+ logger.debug("Framework middleware exists, continuing with regular call_next")
128
+ return await call_next(request)
92
129
  else:
93
- # Fallback: continue without security middleware
94
- logger.debug("Security framework not available, continuing without security checks")
130
+ # No framework middleware, continue normally
95
131
  return await call_next(request)
96
-
132
+
97
133
  except SecurityValidationError as e:
98
134
  # Handle security validation errors
99
135
  return await self._handle_security_error(request, e)
@@ -15,6 +15,16 @@ from starlette.middleware.base import BaseHTTPMiddleware
15
15
 
16
16
  from mcp_proxy_adapter.core.logging import logger
17
17
 
18
+ # Import mcp_security_framework components
19
+ try:
20
+ from mcp_security_framework import AuthManager
21
+ from mcp_security_framework.schemas.config import AuthConfig
22
+ _MCP_SECURITY_AVAILABLE = True
23
+ print("✅ mcp_security_framework available in middleware")
24
+ except ImportError:
25
+ _MCP_SECURITY_AVAILABLE = False
26
+ print("⚠️ mcp_security_framework not available in middleware, using basic auth")
27
+
18
28
 
19
29
  class UserInfoMiddleware(BaseHTTPMiddleware):
20
30
  """
@@ -27,20 +37,43 @@ class UserInfoMiddleware(BaseHTTPMiddleware):
27
37
  def __init__(self, app, config: Dict[str, Any]):
28
38
  """
29
39
  Initialize user info middleware.
30
-
40
+
31
41
  Args:
32
42
  app: FastAPI application
33
43
  config: Configuration dictionary
34
44
  """
35
45
  super().__init__(app)
36
46
  self.config = config
37
-
38
- # Get API keys configuration
39
- security_config = config.get("security", {})
40
- auth_config = security_config.get("auth", {})
41
- self.api_keys = auth_config.get("api_keys", {})
42
-
43
- logger.info("User info middleware initialized")
47
+
48
+ # Initialize AuthManager if available
49
+ self.auth_manager = None
50
+ self._security_available = _MCP_SECURITY_AVAILABLE
51
+
52
+ if self._security_available:
53
+ try:
54
+ # Get API keys configuration
55
+ security_config = config.get("security", {})
56
+ auth_config = security_config.get("auth", {})
57
+
58
+ # Create AuthConfig for mcp_security_framework
59
+ mcp_auth_config = AuthConfig(
60
+ enabled=True,
61
+ methods=["api_key"],
62
+ api_keys=auth_config.get("api_keys", {})
63
+ )
64
+
65
+ self.auth_manager = AuthManager(mcp_auth_config)
66
+ logger.info("✅ User info middleware initialized with mcp_security_framework")
67
+ except Exception as e:
68
+ logger.warning(f"⚠️ Failed to initialize AuthManager: {e}")
69
+ self._security_available = False
70
+
71
+ if not self._security_available:
72
+ # Fallback to basic API key handling
73
+ security_config = config.get("security", {})
74
+ auth_config = security_config.get("auth", {})
75
+ self.api_keys = auth_config.get("api_keys", {})
76
+ logger.info("ℹ️ User info middleware initialized with basic auth")
44
77
 
45
78
  async def dispatch(self, request: Request, call_next: Callable[[Request], Awaitable[Response]]) -> Response:
46
79
  """
@@ -56,28 +89,70 @@ class UserInfoMiddleware(BaseHTTPMiddleware):
56
89
  # Extract API key from headers
57
90
  api_key = request.headers.get("X-API-Key")
58
91
 
59
- if api_key and api_key in self.api_keys:
60
- # Get user info from API key configuration
61
- user_config = self.api_keys[api_key]
62
-
63
- # Set user info in request.state
64
- request.state.user = {
65
- "id": api_key,
66
- "role": user_config.get("roles", ["guest"])[0] if user_config.get("roles") else "guest",
67
- "roles": user_config.get("roles", ["guest"]),
68
- "permissions": user_config.get("permissions", ["read"])
69
- }
70
-
71
- logger.debug(f"Set user info for {api_key}: {request.state.user}")
92
+ if api_key:
93
+ if self.auth_manager and self._security_available:
94
+ try:
95
+ # Use mcp_security_framework AuthManager
96
+ auth_result = self.auth_manager.authenticate_api_key(api_key)
97
+
98
+ if auth_result.is_valid:
99
+ # Set user info from AuthManager result
100
+ request.state.user = {
101
+ "id": api_key,
102
+ "role": auth_result.roles[0] if auth_result.roles else "guest",
103
+ "roles": auth_result.roles or ["guest"],
104
+ "permissions": getattr(auth_result, 'permissions', ["read"])
105
+ }
106
+ logger.debug(f"✅ Authenticated user with mcp_security_framework: {request.state.user}")
107
+ else:
108
+ # Authentication failed
109
+ request.state.user = {
110
+ "id": None,
111
+ "role": "guest",
112
+ "roles": ["guest"],
113
+ "permissions": ["read"]
114
+ }
115
+ logger.debug(f"❌ Authentication failed for API key: {api_key[:8]}...")
116
+ except Exception as e:
117
+ logger.warning(f"⚠️ AuthManager error: {e}, falling back to basic auth")
118
+ self._security_available = False
119
+
120
+ if not self._security_available:
121
+ # Fallback to basic API key handling
122
+ if api_key in getattr(self, 'api_keys', {}):
123
+ user_role = self.api_keys[api_key]
124
+
125
+ # Get permissions for this role from roles file if available
126
+ role_permissions = ["read"] # default permissions
127
+ if hasattr(self, 'roles_config') and self.roles_config and user_role in self.roles_config:
128
+ role_permissions = self.roles_config[user_role].get("permissions", ["read"])
129
+
130
+ # Set user info in request.state
131
+ request.state.user = {
132
+ "id": api_key,
133
+ "role": user_role,
134
+ "roles": [user_role],
135
+ "permissions": role_permissions
136
+ }
137
+
138
+ logger.debug(f"✅ Authenticated user with basic auth: {request.state.user}")
139
+ else:
140
+ # API key not found
141
+ request.state.user = {
142
+ "id": None,
143
+ "role": "guest",
144
+ "roles": ["guest"],
145
+ "permissions": ["read"]
146
+ }
147
+ logger.debug(f"❌ API key not found: {api_key[:8]}...")
72
148
  else:
73
- # Set default guest user info
149
+ # No API key provided - guest access
74
150
  request.state.user = {
75
151
  "id": None,
76
152
  "role": "guest",
77
153
  "roles": ["guest"],
78
154
  "permissions": ["read"]
79
155
  }
80
-
81
- logger.debug("Set default guest user info")
156
+ logger.debug("ℹ️ No API key provided, using guest access")
82
157
 
83
158
  return await call_next(request)
@@ -33,12 +33,33 @@ class ProtocolManager:
33
33
 
34
34
  def _load_config(self):
35
35
  """Load protocol configuration from config."""
36
- # Use provided config or fallback to global config
36
+ # Use provided config or fallback to global config; normalize types
37
37
  current_config = self.app_config if self.app_config is not None else config.get_all()
38
-
38
+ logger.info(f"ProtocolManager._load_config - current_config type: {type(current_config)}")
39
+
40
+ if not hasattr(current_config, 'get'):
41
+ # Not a dict-like config, fallback to global
42
+ logger.info(f"ProtocolManager._load_config - current_config is not dict-like, falling back to global config")
43
+ current_config = config.get_all()
44
+
45
+ logger.info(f"ProtocolManager._load_config - final current_config type: {type(current_config)}")
46
+ if hasattr(current_config, 'get'):
47
+ logger.info(f"ProtocolManager._load_config - current_config keys: {list(current_config.keys()) if hasattr(current_config, 'keys') else 'no keys'}")
48
+
39
49
  # Get protocols configuration
40
- self.protocols_config = current_config.get("protocols", {})
41
- self.enabled = self.protocols_config.get("enabled", True)
50
+ logger.info(f"ProtocolManager._load_config - before getting protocols")
51
+ try:
52
+ self.protocols_config = current_config.get("protocols", {})
53
+ logger.info(f"ProtocolManager._load_config - protocols_config type: {type(self.protocols_config)}")
54
+ if hasattr(self.protocols_config, 'get'):
55
+ logger.info(f"ProtocolManager._load_config - protocols_config is dict-like")
56
+ else:
57
+ logger.info(f"ProtocolManager._load_config - protocols_config is NOT dict-like: {repr(self.protocols_config)}")
58
+ except Exception as e:
59
+ logger.info(f"ProtocolManager._load_config - ERROR getting protocols: {e}")
60
+ self.protocols_config = {}
61
+
62
+ self.enabled = self.protocols_config.get("enabled", True) if hasattr(self.protocols_config, 'get') else True
42
63
 
43
64
  # Get SSL configuration to determine allowed protocols
44
65
  ssl_enabled = self._is_ssl_enabled(current_config)
@@ -162,7 +183,14 @@ class ProtocolManager:
162
183
  Protocol configuration dictionary
163
184
  """
164
185
  protocol_lower = protocol.lower()
165
- return self.protocols_config.get(protocol_lower, {}).copy()
186
+ cfg = self.protocols_config.get(protocol_lower, {})
187
+ # Ensure dict type
188
+ if isinstance(cfg, dict):
189
+ try:
190
+ return cfg.copy()
191
+ except Exception:
192
+ return {}
193
+ return {}
166
194
 
167
195
  def validate_url_protocol(self, url: str) -> Tuple[bool, Optional[str]]:
168
196
  """
@@ -22,17 +22,18 @@ except ImportError:
22
22
  class SimpleCertificateCreator:
23
23
  """Create certificates using OpenSSL directly."""
24
24
  def __init__(self, certs_dir: str = None, keys_dir: str = None):
25
+ # Use current working directory as base
26
+ cwd = Path.cwd()
27
+
25
28
  if certs_dir:
26
29
  self.certs_dir = Path(certs_dir).resolve()
27
30
  else:
28
- self.project_root = Path(__file__).parent.parent.parent
29
- self.certs_dir = self.project_root / "mcp_proxy_adapter" / "examples" / "certs"
31
+ self.certs_dir = cwd / "certs"
32
+
30
33
  if keys_dir:
31
34
  self.keys_dir = Path(keys_dir).resolve()
32
35
  else:
33
- if not certs_dir:
34
- self.project_root = Path(__file__).parent.parent.parent
35
- self.keys_dir = self.project_root / "mcp_proxy_adapter" / "examples" / "keys"
36
+ self.keys_dir = cwd / "keys"
36
37
  # Create directories
37
38
  self.certs_dir.mkdir(parents=True, exist_ok=True)
38
39
  self.keys_dir.mkdir(parents=True, exist_ok=True)
@@ -450,6 +451,56 @@ class SimpleCertificateCreator:
450
451
  # 5. Validate certificates
451
452
  if not self.validate_certificates():
452
453
  success = False
454
+ # Create compatibility symlinks
455
+ if success:
456
+ # CA certificate symlink
457
+ ca_cert = self.certs_dir / "ca_cert.pem"
458
+ expected_ca_cert = self.certs_dir / "mcp_proxy_adapter_ca_ca.crt"
459
+ if ca_cert.exists() and not expected_ca_cert.exists():
460
+ try:
461
+ expected_ca_cert.symlink_to(ca_cert)
462
+ print(f"✅ Created CA certificate symlink: {expected_ca_cert}")
463
+ except OSError:
464
+ # On Windows, symlink might require admin privileges, copy instead
465
+ import shutil
466
+ shutil.copy2(ca_cert, expected_ca_cert)
467
+ print(f"✅ Created CA certificate copy: {expected_ca_cert}")
468
+
469
+ # Server certificate symlink
470
+ server_cert = self.certs_dir / "server_cert.pem"
471
+ expected_server_cert = self.certs_dir / "localhost_server.crt"
472
+ if server_cert.exists() and not expected_server_cert.exists():
473
+ try:
474
+ expected_server_cert.symlink_to(server_cert)
475
+ print(f"✅ Created server certificate symlink: {expected_server_cert}")
476
+ except OSError:
477
+ # On Windows, symlink might require admin privileges, copy instead
478
+ import shutil
479
+ shutil.copy2(server_cert, expected_server_cert)
480
+ print(f"✅ Created server certificate copy: {expected_server_cert}")
481
+
482
+ # Server key symlink - check if it's in certs or keys directory
483
+ server_key_certs = self.certs_dir / "server_key.pem"
484
+ server_key_keys = self.keys_dir / "server_key.pem"
485
+
486
+ if server_key_certs.exists():
487
+ # Server key is in certs directory, move it to keys directory
488
+ import shutil
489
+ shutil.move(str(server_key_certs), str(server_key_keys))
490
+ print(f"✅ Moved server key to keys directory: {server_key_keys}")
491
+
492
+ if server_key_keys.exists():
493
+ expected_server_key = self.keys_dir / "localhost_server.key"
494
+ if not expected_server_key.exists():
495
+ try:
496
+ expected_server_key.symlink_to(server_key_keys)
497
+ print(f"✅ Created server key symlink: {expected_server_key}")
498
+ except OSError:
499
+ # On Windows, symlink might require admin privileges, copy instead
500
+ import shutil
501
+ shutil.copy2(server_key_keys, expected_server_key)
502
+ print(f"✅ Created server key copy: {expected_server_key}")
503
+
453
504
  # Print summary
454
505
  print("\n" + "=" * 60)
455
506
  print("📊 CERTIFICATE CREATION SUMMARY")
@@ -472,12 +523,29 @@ class SimpleCertificateCreator:
472
523
  def main():
473
524
  """Main function."""
474
525
  parser = argparse.ArgumentParser(description="Create certificates for testing")
475
- parser.add_argument("--certs-dir", help="Directory for certificates")
476
- parser.add_argument("--keys-dir", help="Directory for keys")
526
+ parser.add_argument("--certs-dir", help="Directory for certificates (default: ./certs)")
527
+ parser.add_argument("--keys-dir", help="Directory for keys (default: ./keys)")
477
528
  args = parser.parse_args()
529
+
530
+ # If no directories specified, check if we're in a test environment
531
+ if not args.certs_dir and not args.keys_dir:
532
+ cwd = Path.cwd()
533
+ # Check if we're in a test environment by looking for typical directories
534
+ if (cwd / "configs").exists() and (cwd / "examples").exists():
535
+ # We're in a test environment, use current directory
536
+ certs_dir = cwd / "certs"
537
+ keys_dir = cwd / "keys"
538
+ else:
539
+ # Use default project structure
540
+ certs_dir = None
541
+ keys_dir = None
542
+ else:
543
+ certs_dir = args.certs_dir
544
+ keys_dir = args.keys_dir
545
+
478
546
  creator = SimpleCertificateCreator(
479
- certs_dir=args.certs_dir,
480
- keys_dir=args.keys_dir
547
+ certs_dir=certs_dir,
548
+ keys_dir=keys_dir
481
549
  )
482
550
  try:
483
551
  success = creator.create_all()