mcp-proxy-adapter 6.3.28__py3-none-any.whl → 6.3.29__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_proxy_adapter/api/app.py +118 -37
- mcp_proxy_adapter/commands/command_registry.py +24 -1
- mcp_proxy_adapter/core/config_validator.py +172 -234
- mcp_proxy_adapter/core/proxy_client.py +163 -640
- mcp_proxy_adapter/core/proxy_registration.py +143 -41
- mcp_proxy_adapter/main.py +57 -22
- {mcp_proxy_adapter-6.3.28.dist-info → mcp_proxy_adapter-6.3.29.dist-info}/METADATA +1 -1
- {mcp_proxy_adapter-6.3.28.dist-info → mcp_proxy_adapter-6.3.29.dist-info}/RECORD +11 -11
- {mcp_proxy_adapter-6.3.28.dist-info → mcp_proxy_adapter-6.3.29.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.3.28.dist-info → mcp_proxy_adapter-6.3.29.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.3.28.dist-info → mcp_proxy_adapter-6.3.29.dist-info}/top_level.txt +0 -0
mcp_proxy_adapter/api/app.py
CHANGED
@@ -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
|
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
|
-
#
|
76
|
-
#
|
77
|
-
#
|
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
|
-
#
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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
|
-
|
105
|
-
|
106
|
-
|
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
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
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
|
-
|
114
|
-
|
115
|
-
|
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
|
-
|
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
|
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.
|
430
|
-
logger.
|
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.
|
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
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
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
|