mcp-proxy-adapter 6.3.28__py3-none-any.whl → 6.3.30__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.
@@ -65,16 +65,49 @@ def create_lifespan(config_path: Optional[str] = None):
65
65
  initialize_proxy_registration,
66
66
  )
67
67
 
68
- # Initialize proxy registration manager WITH CURRENT CONFIG before reload_system
69
- # so that registration inside reload_system can work
68
+ # Initialize proxy registration manager early
70
69
  try:
71
70
  initialize_proxy_registration(config.get_all())
72
71
  except Exception as e:
73
72
  logger.error(f"Failed to initialize proxy registration: {e}")
74
73
 
75
- # Initialize system using unified logic
76
- # This will load config, register custom commands, and discover auto-commands
77
- # Only reload config if not already loaded from the same path
74
+ # Compute server_url EARLY and inject into registration manager so
75
+ # that reload_system (which may perform registration) uses the correct
76
+ # externally reachable address.
77
+ server_config = config.get("server", {})
78
+ server_host = server_config.get("host", "0.0.0.0")
79
+ server_port = server_config.get("port", 8000)
80
+
81
+ reg_cfg = config.get("registration", {})
82
+ public_host = reg_cfg.get("public_host")
83
+ public_port = reg_cfg.get("public_port")
84
+
85
+ security_config = config.get("security", {})
86
+ ssl_config = security_config.get("ssl", {})
87
+ if not ssl_config.get("enabled", False):
88
+ ssl_config = config.get("ssl", {})
89
+ protocol = "https" if ssl_config.get("enabled", False) else "http"
90
+
91
+ import os
92
+ docker_host_addr = os.getenv("DOCKER_HOST_ADDR", "172.17.0.1")
93
+ target_host = public_host or (docker_host_addr if server_host == "0.0.0.0" else server_host)
94
+ target_port = public_port or server_port
95
+ early_server_url = f"{protocol}://{target_host}:{target_port}"
96
+ try:
97
+ from mcp_proxy_adapter.core.proxy_registration import (
98
+ proxy_registration_manager,
99
+ )
100
+
101
+ if proxy_registration_manager is not None:
102
+ proxy_registration_manager.set_server_url(early_server_url)
103
+ logger.info(
104
+ "🔍 Early injection of server_url for registration: %s",
105
+ early_server_url,
106
+ )
107
+ except Exception as e:
108
+ logger.error(f"Failed to inject early server_url: {e}")
109
+
110
+ # Initialize system using unified logic (may perform registration)
78
111
  if config_path:
79
112
  init_result = await registry.reload_system(config_path=config_path)
80
113
  else:
@@ -91,30 +124,59 @@ def create_lifespan(config_path: Optional[str] = None):
91
124
  except Exception as e:
92
125
  logger.error(f"Failed to initialize proxy registration: {e}")
93
126
 
94
- # Register with proxy if enabled (run slightly delayed to ensure server is accepting connections)
95
- server_config = config.get("server", {})
96
- server_host = server_config.get("host", "0.0.0.0")
97
- server_port = server_config.get("port", 8000)
98
-
99
- # Determine server URL based on SSL configuration
100
- # Try security framework SSL config first
101
- security_config = config.get("security", {})
102
- ssl_config = security_config.get("ssl", {})
127
+ # Recompute registration URL AFTER config reload using final config
128
+ try:
129
+ final_config = config.get_all()
130
+ server_config = final_config.get("server", {})
131
+ server_host = server_config.get("host", "0.0.0.0")
132
+ server_port = server_config.get("port", 8000)
103
133
 
104
- # Fallback to legacy SSL config
105
- if not ssl_config.get("enabled", False):
106
- ssl_config = config.get("ssl", {})
134
+ reg_cfg = final_config.get("registration", final_config.get("proxy_registration", {}))
135
+ public_host = reg_cfg.get("public_host")
136
+ public_port = reg_cfg.get("public_port")
107
137
 
108
- if ssl_config.get("enabled", False):
109
- protocol = "https"
110
- else:
111
- protocol = "http"
138
+ security_config = final_config.get("security", {})
139
+ ssl_cfg = security_config.get("ssl", {})
140
+ if not ssl_cfg.get("enabled", False):
141
+ ssl_cfg = final_config.get("ssl", {})
142
+ protocol = "https" if ssl_cfg.get("enabled", False) else "http"
112
143
 
113
- # Use localhost for external access if host is 0.0.0.0
114
- if server_host == "0.0.0.0":
115
- server_host = "localhost"
144
+ import os
145
+ docker_host_addr = os.getenv("DOCKER_HOST_ADDR", "172.17.0.1")
146
+ resolved_host = public_host or (docker_host_addr if server_host == "0.0.0.0" else server_host)
147
+ resolved_port = public_port or server_port
148
+ server_url = f"{protocol}://{resolved_host}:{resolved_port}"
149
+
150
+ logger.info(
151
+ "🔍 Registration URL selection: server_host=%s, server_port=%s, public_host=%s, public_port=%s, protocol=%s",
152
+ server_host,
153
+ server_port,
154
+ public_host,
155
+ public_port,
156
+ protocol,
157
+ )
158
+ try:
159
+ print(
160
+ "🔍 Registration URL selection (print):",
161
+ {
162
+ "server_host": server_host,
163
+ "server_port": server_port,
164
+ "public_host": public_host,
165
+ "public_port": public_port,
166
+ "protocol": protocol,
167
+ },
168
+ )
169
+ except Exception:
170
+ pass
116
171
 
117
- server_url = f"{protocol}://{server_host}:{server_port}"
172
+ logger.info("🔍 Registration server_url resolved to: %s", server_url)
173
+ try:
174
+ print("🔍 Registration server_url resolved to (print):", server_url)
175
+ except Exception:
176
+ pass
177
+ except Exception as e:
178
+ logger.error(f"Failed to recompute registration URL: {e}")
179
+ server_url = early_server_url
118
180
 
119
181
  # Attempt proxy registration in background with small delay
120
182
  async def _delayed_register():
@@ -256,7 +318,26 @@ def create_app(
256
318
  else:
257
319
  print("🔍 Debug: create_app received no app_config, using global config")
258
320
 
259
- # Security check: Validate all authentication configurations before startup
321
+ # Security check: Validate configuration strictly at startup (fail-fast)
322
+ try:
323
+ from mcp_proxy_adapter.core.config_validator import ConfigValidator
324
+
325
+ try:
326
+ _validator = ConfigValidator()
327
+ except TypeError:
328
+ _validator = ConfigValidator(current_config)
329
+ _validation = _validator.validate(current_config)
330
+ if not _validation.is_valid:
331
+ logger.critical("CRITICAL CONFIG ERROR: Invalid configuration at startup:")
332
+ for _e in _validation.errors:
333
+ logger.critical(f" - {_e}")
334
+ raise SystemExit(1)
335
+ for _w in _validation.warnings:
336
+ logger.warning(f"Config warning: {_w}")
337
+ except Exception as _ex:
338
+ logger.error(f"Failed to run startup configuration validation: {_ex}")
339
+
340
+ # Security check: Validate all authentication configurations before startup (legacy checks kept for compatibility)
260
341
  security_errors = []
261
342
 
262
343
  print(f"🔍 Debug: current_config keys: {list(current_config.keys())}")
@@ -426,11 +507,11 @@ def create_app(
426
507
  # Add request logging middleware for debugging
427
508
  @app.middleware("http")
428
509
  async def debug_request_middleware(request: Request, call_next):
429
- logger.debug(f"🔍 FastAPI Request START: {request.method} {request.url.path}")
430
- logger.debug(f"🔍 FastAPI Request Headers: {dict(request.headers)}")
510
+ logger.info(f"🔍 FastAPI Request START: {request.method} {request.url.path}")
511
+ logger.info(f"🔍 FastAPI Request Headers: {dict(request.headers)}")
431
512
  try:
432
513
  response = await call_next(request)
433
- logger.debug(f"🔍 FastAPI Request COMPLETED: {response.status_code}")
514
+ logger.info(f"🔍 FastAPI Request COMPLETED: {response.status_code}")
434
515
  return response
435
516
  except Exception as e:
436
517
  logger.error(f"🔍 FastAPI Request ERROR: {e}", exc_info=True)
@@ -440,14 +521,14 @@ def create_app(
440
521
  setup_middleware(app, current_config)
441
522
 
442
523
  # Add request logging middleware
443
- @app.middleware("http")
444
- async def log_requests(request: Request, call_next):
445
- logger.info(f"🔍 REQUEST LOG: {request.method} {request.url.path}")
446
- logger.info(f"🔍 REQUEST LOG: Headers: {dict(request.headers)}")
447
- logger.info(f"🔍 REQUEST LOG: Client: {request.client}")
448
- response = await call_next(request)
449
- logger.info(f"🔍 RESPONSE LOG: Status: {response.status_code}")
450
- return response
524
+ # @app.middleware("http")
525
+ # async def log_requests(request: Request, call_next):
526
+ # logger.info(f"🔍 REQUEST LOG: {request.method} {request.url.path}")
527
+ # logger.info(f"🔍 REQUEST LOG: Headers: {dict(request.headers)}")
528
+ # logger.info(f"🔍 REQUEST LOG: Client: {request.client}")
529
+ # response = await call_next(request)
530
+ # logger.info(f"🔍 RESPONSE LOG: Status: {response.status_code}")
531
+ # return response
451
532
 
452
533
  # Use custom OpenAPI schema
453
534
  app.openapi = lambda: custom_openapi_with_fallback(app)
@@ -674,7 +674,8 @@ class CommandRegistry:
674
674
  f"🔄 Starting system reload with config: {config_path or 'default'}"
675
675
  )
676
676
 
677
- # Step 1: Load configuration
677
+ # Step 1: Load configuration (preserve previous config for soft-fail)
678
+ previous_config = config.get_all()
678
679
  try:
679
680
  if config_path:
680
681
  config.load_from_file(config_path)
@@ -688,6 +689,28 @@ class CommandRegistry:
688
689
  logger.error(f"❌ Failed to load configuration: {e}")
689
690
  config_reloaded = False
690
691
 
692
+ # Step 1.1: Validate configuration (soft-fail on reload)
693
+ try:
694
+ from mcp_proxy_adapter.core.config_validator import ConfigValidator
695
+
696
+ validator = ConfigValidator()
697
+ validation = validator.validate(config.get_all())
698
+ if not validation.is_valid:
699
+ logger.error("⚠️ Configuration validation failed during reload:")
700
+ for err in validation.errors:
701
+ logger.error(f" - {err}")
702
+ # Do NOT exit on reload; restore previous configuration
703
+ try:
704
+ config.config_data = previous_config
705
+ config_reloaded = False
706
+ logger.error("ℹ️ Restored previous configuration due to validation errors")
707
+ except Exception as restore_ex:
708
+ logger.error(f"❌ Failed to restore previous configuration: {restore_ex}")
709
+ for warn in validation.warnings:
710
+ logger.warning(f"Config warning: {warn}")
711
+ except Exception as e:
712
+ logger.error(f"❌ Failed to validate configuration: {e}")
713
+
691
714
  # Step 2: Initialize logging with configuration
692
715
  try:
693
716
  from mcp_proxy_adapter.core.logging import setup_logging