mcp-proxy-adapter 6.3.0__py3-none-any.whl → 6.3.2__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/core/proxy_registration.py +205 -130
- mcp_proxy_adapter/utils/config_generator.py +3 -0
- mcp_proxy_adapter/version.py +1 -1
- {mcp_proxy_adapter-6.3.0.dist-info → mcp_proxy_adapter-6.3.2.dist-info}/METADATA +1 -1
- {mcp_proxy_adapter-6.3.0.dist-info → mcp_proxy_adapter-6.3.2.dist-info}/RECORD +9 -9
- {mcp_proxy_adapter-6.3.0.dist-info → mcp_proxy_adapter-6.3.2.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.3.0.dist-info → mcp_proxy_adapter-6.3.2.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.3.0.dist-info → mcp_proxy_adapter-6.3.2.dist-info}/licenses/LICENSE +0 -0
- {mcp_proxy_adapter-6.3.0.dist-info → mcp_proxy_adapter-6.3.2.dist-info}/top_level.txt +0 -0
@@ -10,15 +10,12 @@ email: vasilyvz@gmail.com
|
|
10
10
|
"""
|
11
11
|
|
12
12
|
import asyncio
|
13
|
-
import json
|
14
13
|
import time
|
15
14
|
import ssl
|
16
15
|
from typing import Dict, Any, Optional, Tuple
|
17
16
|
from urllib.parse import urljoin
|
18
17
|
|
19
18
|
import aiohttp
|
20
|
-
import requests
|
21
|
-
from requests.exceptions import RequestException
|
22
19
|
|
23
20
|
from mcp_proxy_adapter.core.logging import logger
|
24
21
|
from mcp_proxy_adapter.core.client_security import create_client_security_manager
|
@@ -26,88 +23,113 @@ from mcp_proxy_adapter.core.client_security import create_client_security_manage
|
|
26
23
|
|
27
24
|
class ProxyRegistrationError(Exception):
|
28
25
|
"""Exception raised when proxy registration fails."""
|
26
|
+
|
29
27
|
pass
|
30
28
|
|
31
29
|
|
32
30
|
class ProxyRegistrationManager:
|
33
31
|
"""
|
34
32
|
Manager for proxy registration functionality with security framework integration.
|
35
|
-
|
33
|
+
|
36
34
|
Handles automatic registration and unregistration of the server
|
37
35
|
with the MCP proxy server using secure authentication methods.
|
38
36
|
"""
|
39
|
-
|
37
|
+
|
40
38
|
def __init__(self, config: Dict[str, Any]):
|
41
39
|
"""
|
42
40
|
Initialize the proxy registration manager.
|
43
|
-
|
41
|
+
|
44
42
|
Args:
|
45
43
|
config: Application configuration
|
46
44
|
"""
|
47
45
|
self.config = config
|
48
46
|
# Try both registration and proxy_registration for backward compatibility
|
49
|
-
self.registration_config = config.get(
|
50
|
-
|
47
|
+
self.registration_config = config.get(
|
48
|
+
"registration", config.get("proxy_registration", {})
|
49
|
+
)
|
50
|
+
|
51
51
|
# Basic registration settings
|
52
|
-
self.proxy_url = self.registration_config.get(
|
53
|
-
|
54
|
-
|
55
|
-
self.
|
56
|
-
|
57
|
-
|
52
|
+
self.proxy_url = self.registration_config.get(
|
53
|
+
"proxy_url", "https://proxy-registry.example.com"
|
54
|
+
)
|
55
|
+
self.server_id = self.registration_config.get(
|
56
|
+
"server_id",
|
57
|
+
self.registration_config.get("proxy_info", {}).get(
|
58
|
+
"name", "mcp_proxy_adapter"
|
59
|
+
),
|
60
|
+
)
|
61
|
+
self.server_name = self.registration_config.get(
|
62
|
+
"server_name",
|
63
|
+
self.registration_config.get("proxy_info", {}).get(
|
64
|
+
"name", "MCP Proxy Adapter"
|
65
|
+
),
|
66
|
+
)
|
67
|
+
self.description = self.registration_config.get(
|
68
|
+
"description",
|
69
|
+
self.registration_config.get("proxy_info", {}).get(
|
70
|
+
"description", "JSON-RPC API for interacting with MCP Proxy"
|
71
|
+
),
|
72
|
+
)
|
73
|
+
self.version = self.registration_config.get(
|
74
|
+
"version",
|
75
|
+
self.registration_config.get("proxy_info", {}).get("version", "1.0.0"),
|
76
|
+
)
|
77
|
+
|
58
78
|
# Heartbeat settings
|
59
79
|
heartbeat_config = self.registration_config.get("heartbeat", {})
|
60
80
|
self.timeout = heartbeat_config.get("timeout", 30)
|
61
81
|
self.retry_attempts = heartbeat_config.get("retry_attempts", 3)
|
62
82
|
self.retry_delay = heartbeat_config.get("retry_delay", 60)
|
63
83
|
self.heartbeat_interval = heartbeat_config.get("interval", 300)
|
64
|
-
|
84
|
+
|
65
85
|
# Auto registration settings
|
66
86
|
self.auto_register = self.registration_config.get("enabled", False)
|
67
87
|
self.auto_unregister = True # Always unregister on shutdown
|
68
|
-
|
88
|
+
|
69
89
|
# Initialize client security manager
|
70
90
|
self.client_security = create_client_security_manager(config)
|
71
|
-
|
91
|
+
|
72
92
|
# Registration state
|
73
93
|
self.registered = False
|
74
94
|
self.server_key: Optional[str] = None
|
75
95
|
self.server_url: Optional[str] = None
|
76
96
|
self.heartbeat_task: Optional[asyncio.Task] = None
|
77
|
-
|
78
|
-
logger.info(
|
79
|
-
|
97
|
+
|
98
|
+
logger.info(
|
99
|
+
"Proxy registration manager initialized with security framework integration"
|
100
|
+
)
|
101
|
+
|
80
102
|
def is_enabled(self) -> bool:
|
81
103
|
"""
|
82
104
|
Check if proxy registration is enabled.
|
83
|
-
|
105
|
+
|
84
106
|
Returns:
|
85
107
|
True if registration is enabled, False otherwise.
|
86
108
|
"""
|
87
109
|
return self.registration_config.get("enabled", False)
|
88
|
-
|
110
|
+
|
89
111
|
def set_server_url(self, server_url: str) -> None:
|
90
112
|
"""
|
91
113
|
Set the server URL for registration.
|
92
|
-
|
114
|
+
|
93
115
|
Args:
|
94
116
|
server_url: The URL where this server is accessible.
|
95
117
|
"""
|
96
118
|
self.server_url = server_url
|
97
119
|
logger.info(f"Proxy registration server URL set to: {server_url}")
|
98
|
-
|
120
|
+
|
99
121
|
def _get_auth_headers(self) -> Dict[str, str]:
|
100
122
|
"""
|
101
123
|
Get authentication headers for registration requests.
|
102
|
-
|
124
|
+
|
103
125
|
Returns:
|
104
126
|
Dictionary of authentication headers
|
105
127
|
"""
|
106
128
|
if not self.client_security:
|
107
129
|
return {"Content-Type": "application/json"}
|
108
|
-
|
130
|
+
|
109
131
|
auth_method = self.registration_config.get("auth_method", "certificate")
|
110
|
-
|
132
|
+
|
111
133
|
if auth_method == "certificate":
|
112
134
|
return self.client_security.get_client_auth_headers("certificate")
|
113
135
|
elif auth_method == "token":
|
@@ -117,46 +139,48 @@ class ProxyRegistrationManager:
|
|
117
139
|
elif auth_method == "api_key":
|
118
140
|
api_key_config = self.registration_config.get("api_key", {})
|
119
141
|
api_key = api_key_config.get("key")
|
120
|
-
return self.client_security.get_client_auth_headers(
|
142
|
+
return self.client_security.get_client_auth_headers(
|
143
|
+
"api_key", api_key=api_key
|
144
|
+
)
|
121
145
|
else:
|
122
146
|
return {"Content-Type": "application/json"}
|
123
|
-
|
147
|
+
|
124
148
|
def _create_ssl_context(self) -> Optional[ssl.SSLContext]:
|
125
149
|
"""
|
126
150
|
Create SSL context for secure connections.
|
127
|
-
|
151
|
+
|
128
152
|
Returns:
|
129
153
|
SSL context or None if SSL not needed
|
130
154
|
"""
|
131
155
|
if not self.client_security:
|
132
156
|
return None
|
133
|
-
|
157
|
+
|
134
158
|
try:
|
135
159
|
# Check if SSL is enabled for registration
|
136
160
|
cert_config = self.registration_config.get("certificate", {})
|
137
161
|
if cert_config.get("enabled", False):
|
138
162
|
return self.client_security.create_client_ssl_context()
|
139
|
-
|
163
|
+
|
140
164
|
return None
|
141
165
|
except Exception as e:
|
142
166
|
logger.error(f"Failed to create SSL context: {e}")
|
143
167
|
return None
|
144
|
-
|
168
|
+
|
145
169
|
async def register_server(self) -> bool:
|
146
170
|
"""
|
147
171
|
Register the server with the proxy using secure authentication.
|
148
|
-
|
172
|
+
|
149
173
|
Returns:
|
150
174
|
True if registration was successful, False otherwise.
|
151
175
|
"""
|
152
176
|
if not self.is_enabled():
|
153
177
|
logger.info("Proxy registration is disabled in configuration")
|
154
178
|
return False
|
155
|
-
|
179
|
+
|
156
180
|
if not self.server_url:
|
157
181
|
logger.error("Server URL not set, cannot register with proxy")
|
158
182
|
return False
|
159
|
-
|
183
|
+
|
160
184
|
# Prepare registration data with proxy info
|
161
185
|
proxy_info = self.registration_config.get("proxy_info", {})
|
162
186
|
registration_data = {
|
@@ -166,90 +190,100 @@ class ProxyRegistrationManager:
|
|
166
190
|
"description": self.description,
|
167
191
|
"version": self.version,
|
168
192
|
"capabilities": proxy_info.get("capabilities", ["jsonrpc", "rest"]),
|
169
|
-
"endpoints": proxy_info.get(
|
170
|
-
"
|
171
|
-
"rest": "/cmd",
|
172
|
-
|
173
|
-
})
|
193
|
+
"endpoints": proxy_info.get(
|
194
|
+
"endpoints",
|
195
|
+
{"jsonrpc": "/api/jsonrpc", "rest": "/cmd", "health": "/health"},
|
196
|
+
),
|
174
197
|
}
|
175
|
-
|
198
|
+
|
176
199
|
logger.info(f"Attempting to register server with proxy at {self.proxy_url}")
|
177
200
|
logger.debug(f"Registration data: {registration_data}")
|
178
|
-
|
201
|
+
|
179
202
|
for attempt in range(self.retry_attempts):
|
180
203
|
try:
|
181
|
-
success, result = await self._make_secure_registration_request(
|
182
|
-
|
204
|
+
success, result = await self._make_secure_registration_request(
|
205
|
+
registration_data
|
206
|
+
)
|
207
|
+
|
183
208
|
if success:
|
184
209
|
self.registered = True
|
185
210
|
self.server_key = result.get("server_key")
|
186
|
-
logger.info(
|
187
|
-
|
211
|
+
logger.info(
|
212
|
+
f"✅ Successfully registered with proxy. Server key: {self.server_key}"
|
213
|
+
)
|
214
|
+
|
188
215
|
# Start heartbeat if enabled
|
189
|
-
if self.registration_config.get("heartbeat", {}).get(
|
216
|
+
if self.registration_config.get("heartbeat", {}).get(
|
217
|
+
"enabled", True
|
218
|
+
):
|
190
219
|
await self._start_heartbeat()
|
191
|
-
|
220
|
+
|
192
221
|
return True
|
193
222
|
else:
|
194
223
|
error_msg = result.get("error", {}).get("message", "Unknown error")
|
195
|
-
logger.warning(
|
196
|
-
|
224
|
+
logger.warning(
|
225
|
+
f"❌ Registration attempt {attempt + 1} failed: {error_msg}"
|
226
|
+
)
|
227
|
+
|
197
228
|
if attempt < self.retry_attempts - 1:
|
198
229
|
logger.info(f"Retrying in {self.retry_delay} seconds...")
|
199
230
|
await asyncio.sleep(self.retry_delay)
|
200
|
-
|
231
|
+
|
201
232
|
except Exception as e:
|
202
|
-
logger.error(
|
203
|
-
|
233
|
+
logger.error(
|
234
|
+
f"❌ Registration attempt {attempt + 1} failed with exception: {e}"
|
235
|
+
)
|
236
|
+
|
204
237
|
if attempt < self.retry_attempts - 1:
|
205
238
|
logger.info(f"Retrying in {self.retry_delay} seconds...")
|
206
239
|
await asyncio.sleep(self.retry_delay)
|
207
|
-
|
208
|
-
logger.error(
|
240
|
+
|
241
|
+
logger.error(
|
242
|
+
f"❌ Failed to register with proxy after {self.retry_attempts} attempts"
|
243
|
+
)
|
209
244
|
return False
|
210
|
-
|
245
|
+
|
211
246
|
async def unregister_server(self) -> bool:
|
212
247
|
"""
|
213
248
|
Unregister the server from the proxy.
|
214
|
-
|
249
|
+
|
215
250
|
Returns:
|
216
251
|
True if unregistration was successful, False otherwise.
|
217
252
|
"""
|
218
253
|
if not self.is_enabled():
|
219
254
|
logger.info("Proxy registration is disabled, skipping unregistration")
|
220
255
|
return True
|
221
|
-
|
256
|
+
|
222
257
|
if not self.registered or not self.server_key:
|
223
258
|
logger.info("Server not registered with proxy, skipping unregistration")
|
224
259
|
return True
|
225
|
-
|
260
|
+
|
226
261
|
# Stop heartbeat
|
227
262
|
await self._stop_heartbeat()
|
228
|
-
|
263
|
+
|
229
264
|
# Extract copy_number from server_key (format: server_id_copy_number)
|
230
265
|
try:
|
231
266
|
copy_number = int(self.server_key.split("_")[-1])
|
232
267
|
except (ValueError, IndexError):
|
233
268
|
copy_number = 1
|
234
|
-
|
235
|
-
unregistration_data = {
|
236
|
-
|
237
|
-
"copy_number": copy_number
|
238
|
-
}
|
239
|
-
|
269
|
+
|
270
|
+
unregistration_data = {"server_id": self.server_id, "copy_number": copy_number}
|
271
|
+
|
240
272
|
logger.info(f"Attempting to unregister server from proxy at {self.proxy_url}")
|
241
273
|
logger.debug(f"Unregistration data: {unregistration_data}")
|
242
|
-
|
274
|
+
|
243
275
|
try:
|
244
|
-
success, result = await self._make_secure_unregistration_request(
|
245
|
-
|
276
|
+
success, result = await self._make_secure_unregistration_request(
|
277
|
+
unregistration_data
|
278
|
+
)
|
279
|
+
|
246
280
|
if success:
|
247
281
|
unregistered = result.get("unregistered", False)
|
248
282
|
if unregistered:
|
249
283
|
logger.info("✅ Successfully unregistered from proxy")
|
250
284
|
else:
|
251
285
|
logger.warning("⚠️ Server was not found in proxy registry")
|
252
|
-
|
286
|
+
|
253
287
|
self.registered = False
|
254
288
|
self.server_key = None
|
255
289
|
return True
|
@@ -257,105 +291,143 @@ class ProxyRegistrationManager:
|
|
257
291
|
error_msg = result.get("error", {}).get("message", "Unknown error")
|
258
292
|
logger.error(f"❌ Failed to unregister from proxy: {error_msg}")
|
259
293
|
return False
|
260
|
-
|
294
|
+
|
261
295
|
except Exception as e:
|
262
296
|
logger.error(f"❌ Unregistration failed with exception: {e}")
|
263
297
|
return False
|
264
|
-
|
265
|
-
async def _make_secure_registration_request(
|
298
|
+
|
299
|
+
async def _make_secure_registration_request(
|
300
|
+
self, data: Dict[str, Any]
|
301
|
+
) -> Tuple[bool, Dict[str, Any]]:
|
266
302
|
"""
|
267
303
|
Make secure registration request to proxy using security framework.
|
268
|
-
|
304
|
+
|
269
305
|
Args:
|
270
306
|
data: Registration data.
|
271
|
-
|
307
|
+
|
272
308
|
Returns:
|
273
309
|
Tuple of (success, result).
|
274
310
|
"""
|
275
311
|
url = urljoin(self.proxy_url, "/register")
|
276
|
-
|
312
|
+
|
277
313
|
# Get authentication headers
|
278
314
|
headers = self._get_auth_headers()
|
279
315
|
headers["Content-Type"] = "application/json"
|
280
|
-
|
316
|
+
|
281
317
|
# Create SSL context if needed
|
282
318
|
ssl_context = self._create_ssl_context()
|
283
|
-
|
319
|
+
|
284
320
|
# Create connector with SSL context
|
285
321
|
connector = None
|
286
322
|
if ssl_context:
|
287
323
|
connector = aiohttp.TCPConnector(ssl=ssl_context)
|
288
|
-
|
324
|
+
|
289
325
|
try:
|
290
326
|
async with aiohttp.ClientSession(connector=connector) as session:
|
291
327
|
async with session.post(
|
292
328
|
url,
|
293
329
|
json=data,
|
294
330
|
headers=headers,
|
295
|
-
timeout=aiohttp.ClientTimeout(total=self.timeout)
|
331
|
+
timeout=aiohttp.ClientTimeout(total=self.timeout),
|
296
332
|
) as response:
|
297
333
|
result = await response.json()
|
298
|
-
|
334
|
+
|
299
335
|
# Validate response headers if security framework available
|
300
336
|
if self.client_security:
|
301
|
-
self.client_security.validate_server_response(
|
302
|
-
|
303
|
-
|
337
|
+
self.client_security.validate_server_response(
|
338
|
+
dict(response.headers)
|
339
|
+
)
|
340
|
+
|
341
|
+
# Check both HTTP status and JSON success field
|
342
|
+
if response.status == 200:
|
343
|
+
success = result.get("success", False)
|
344
|
+
if not success:
|
345
|
+
error_info = result.get("error", {})
|
346
|
+
error_msg = error_info.get("message", "Unknown error")
|
347
|
+
error_code = error_info.get("code", "UNKNOWN_ERROR")
|
348
|
+
logger.warning(
|
349
|
+
f"Registration failed: {error_code} - {error_msg}"
|
350
|
+
)
|
351
|
+
return success, result
|
352
|
+
else:
|
353
|
+
logger.warning(
|
354
|
+
f"Registration failed with HTTP status: {response.status}"
|
355
|
+
)
|
356
|
+
return False, result
|
304
357
|
finally:
|
305
358
|
if connector:
|
306
359
|
await connector.close()
|
307
|
-
|
308
|
-
async def _make_secure_unregistration_request(
|
360
|
+
|
361
|
+
async def _make_secure_unregistration_request(
|
362
|
+
self, data: Dict[str, Any]
|
363
|
+
) -> Tuple[bool, Dict[str, Any]]:
|
309
364
|
"""
|
310
365
|
Make secure unregistration request to proxy using security framework.
|
311
|
-
|
366
|
+
|
312
367
|
Args:
|
313
368
|
data: Unregistration data.
|
314
|
-
|
369
|
+
|
315
370
|
Returns:
|
316
371
|
Tuple of (success, result).
|
317
372
|
"""
|
318
373
|
url = urljoin(self.proxy_url, "/unregister")
|
319
|
-
|
374
|
+
|
320
375
|
# Get authentication headers
|
321
376
|
headers = self._get_auth_headers()
|
322
377
|
headers["Content-Type"] = "application/json"
|
323
|
-
|
378
|
+
|
324
379
|
# Create SSL context if needed
|
325
380
|
ssl_context = self._create_ssl_context()
|
326
|
-
|
381
|
+
|
327
382
|
# Create connector with SSL context
|
328
383
|
connector = None
|
329
384
|
if ssl_context:
|
330
385
|
connector = aiohttp.TCPConnector(ssl=ssl_context)
|
331
|
-
|
386
|
+
|
332
387
|
try:
|
333
388
|
async with aiohttp.ClientSession(connector=connector) as session:
|
334
389
|
async with session.post(
|
335
390
|
url,
|
336
391
|
json=data,
|
337
392
|
headers=headers,
|
338
|
-
timeout=aiohttp.ClientTimeout(total=self.timeout)
|
393
|
+
timeout=aiohttp.ClientTimeout(total=self.timeout),
|
339
394
|
) as response:
|
340
395
|
result = await response.json()
|
341
|
-
|
396
|
+
|
342
397
|
# Validate response headers if security framework available
|
343
398
|
if self.client_security:
|
344
|
-
self.client_security.validate_server_response(
|
345
|
-
|
346
|
-
|
399
|
+
self.client_security.validate_server_response(
|
400
|
+
dict(response.headers)
|
401
|
+
)
|
402
|
+
|
403
|
+
# Check both HTTP status and JSON success field
|
404
|
+
if response.status == 200:
|
405
|
+
success = result.get("success", False)
|
406
|
+
if not success:
|
407
|
+
error_info = result.get("error", {})
|
408
|
+
error_msg = error_info.get("message", "Unknown error")
|
409
|
+
error_code = error_info.get("code", "UNKNOWN_ERROR")
|
410
|
+
logger.warning(
|
411
|
+
f"Unregistration failed: {error_code} - {error_msg}"
|
412
|
+
)
|
413
|
+
return success, result
|
414
|
+
else:
|
415
|
+
logger.warning(
|
416
|
+
f"Unregistration failed with HTTP status: {response.status}"
|
417
|
+
)
|
418
|
+
return False, result
|
347
419
|
finally:
|
348
420
|
if connector:
|
349
421
|
await connector.close()
|
350
|
-
|
422
|
+
|
351
423
|
async def _start_heartbeat(self) -> None:
|
352
424
|
"""Start heartbeat task for keeping registration alive."""
|
353
425
|
if self.heartbeat_task and not self.heartbeat_task.done():
|
354
426
|
return
|
355
|
-
|
427
|
+
|
356
428
|
self.heartbeat_task = asyncio.create_task(self._heartbeat_loop())
|
357
429
|
logger.info("Heartbeat task started")
|
358
|
-
|
430
|
+
|
359
431
|
async def _stop_heartbeat(self) -> None:
|
360
432
|
"""Stop heartbeat task."""
|
361
433
|
if self.heartbeat_task and not self.heartbeat_task.done():
|
@@ -365,65 +437,67 @@ class ProxyRegistrationManager:
|
|
365
437
|
except asyncio.CancelledError:
|
366
438
|
pass
|
367
439
|
logger.info("Heartbeat task stopped")
|
368
|
-
|
440
|
+
|
369
441
|
async def _heartbeat_loop(self) -> None:
|
370
442
|
"""Heartbeat loop to keep registration alive."""
|
371
443
|
while self.registered:
|
372
444
|
try:
|
373
445
|
await asyncio.sleep(self.heartbeat_interval)
|
374
|
-
|
446
|
+
|
375
447
|
if not self.registered:
|
376
448
|
break
|
377
|
-
|
449
|
+
|
378
450
|
# Send heartbeat
|
379
451
|
success = await self._send_heartbeat()
|
380
452
|
if not success:
|
381
453
|
logger.warning("Heartbeat failed, attempting to re-register")
|
382
454
|
await self.register_server()
|
383
|
-
|
455
|
+
|
384
456
|
except asyncio.CancelledError:
|
385
457
|
break
|
386
458
|
except Exception as e:
|
387
459
|
logger.error(f"Heartbeat error: {e}")
|
388
|
-
|
460
|
+
|
389
461
|
async def _send_heartbeat(self) -> bool:
|
390
462
|
"""Send heartbeat to proxy server."""
|
391
463
|
if not self.server_key:
|
392
464
|
return False
|
393
|
-
|
465
|
+
|
394
466
|
heartbeat_data = {
|
395
467
|
"server_id": self.server_id,
|
396
468
|
"server_key": self.server_key,
|
397
|
-
"timestamp": int(time.time())
|
469
|
+
"timestamp": int(time.time()),
|
398
470
|
}
|
399
|
-
|
471
|
+
|
400
472
|
url = urljoin(self.proxy_url, "/heartbeat")
|
401
|
-
|
473
|
+
|
402
474
|
# Get authentication headers
|
403
475
|
headers = self._get_auth_headers()
|
404
476
|
headers["Content-Type"] = "application/json"
|
405
|
-
|
477
|
+
|
406
478
|
# Create SSL context if needed
|
407
479
|
ssl_context = self._create_ssl_context()
|
408
|
-
|
480
|
+
|
409
481
|
# Create connector with SSL context
|
410
482
|
connector = None
|
411
483
|
if ssl_context:
|
412
484
|
connector = aiohttp.TCPConnector(ssl=ssl_context)
|
413
|
-
|
485
|
+
|
414
486
|
try:
|
415
487
|
async with aiohttp.ClientSession(connector=connector) as session:
|
416
488
|
async with session.post(
|
417
489
|
url,
|
418
490
|
json=heartbeat_data,
|
419
491
|
headers=headers,
|
420
|
-
timeout=aiohttp.ClientTimeout(total=self.timeout)
|
492
|
+
timeout=aiohttp.ClientTimeout(total=self.timeout),
|
421
493
|
) as response:
|
422
494
|
if response.status == 200:
|
423
495
|
logger.debug("Heartbeat sent successfully")
|
424
496
|
return True
|
425
497
|
else:
|
426
|
-
logger.warning(
|
498
|
+
logger.warning(
|
499
|
+
f"Heartbeat failed with status: {response.status}"
|
500
|
+
)
|
427
501
|
return False
|
428
502
|
except Exception as e:
|
429
503
|
logger.error(f"Heartbeat error: {e}")
|
@@ -431,11 +505,11 @@ class ProxyRegistrationManager:
|
|
431
505
|
finally:
|
432
506
|
if connector:
|
433
507
|
await connector.close()
|
434
|
-
|
508
|
+
|
435
509
|
def get_registration_status(self) -> Dict[str, Any]:
|
436
510
|
"""
|
437
511
|
Get current registration status.
|
438
|
-
|
512
|
+
|
439
513
|
Returns:
|
440
514
|
Dictionary with registration status information.
|
441
515
|
"""
|
@@ -446,21 +520,22 @@ class ProxyRegistrationManager:
|
|
446
520
|
"server_url": self.server_url,
|
447
521
|
"proxy_url": self.proxy_url,
|
448
522
|
"server_id": self.server_id,
|
449
|
-
"heartbeat_active": self.heartbeat_task is not None
|
523
|
+
"heartbeat_active": self.heartbeat_task is not None
|
524
|
+
and not self.heartbeat_task.done(),
|
450
525
|
}
|
451
|
-
|
526
|
+
|
452
527
|
# Add security information if available
|
453
528
|
if self.client_security:
|
454
529
|
status["security_enabled"] = True
|
455
530
|
status["ssl_enabled"] = self.client_security.is_ssl_enabled()
|
456
531
|
status["auth_methods"] = self.client_security.get_supported_auth_methods()
|
457
|
-
|
532
|
+
|
458
533
|
cert_info = self.client_security.get_client_certificate_info()
|
459
534
|
if cert_info:
|
460
535
|
status["client_certificate"] = cert_info
|
461
536
|
else:
|
462
537
|
status["security_enabled"] = False
|
463
|
-
|
538
|
+
|
464
539
|
return status
|
465
540
|
|
466
541
|
|
@@ -471,7 +546,7 @@ proxy_registration_manager: Optional[ProxyRegistrationManager] = None
|
|
471
546
|
def initialize_proxy_registration(config: Dict[str, Any]) -> None:
|
472
547
|
"""
|
473
548
|
Initialize global proxy registration manager.
|
474
|
-
|
549
|
+
|
475
550
|
Args:
|
476
551
|
config: Application configuration
|
477
552
|
"""
|
@@ -482,17 +557,17 @@ def initialize_proxy_registration(config: Dict[str, Any]) -> None:
|
|
482
557
|
async def register_with_proxy(server_url: str) -> bool:
|
483
558
|
"""
|
484
559
|
Register the server with the proxy.
|
485
|
-
|
560
|
+
|
486
561
|
Args:
|
487
562
|
server_url: The URL where this server is accessible.
|
488
|
-
|
563
|
+
|
489
564
|
Returns:
|
490
565
|
True if registration was successful, False otherwise.
|
491
566
|
"""
|
492
567
|
if not proxy_registration_manager:
|
493
568
|
logger.error("Proxy registration manager not initialized")
|
494
569
|
return False
|
495
|
-
|
570
|
+
|
496
571
|
proxy_registration_manager.set_server_url(server_url)
|
497
572
|
return await proxy_registration_manager.register_server()
|
498
573
|
|
@@ -500,25 +575,25 @@ async def register_with_proxy(server_url: str) -> bool:
|
|
500
575
|
async def unregister_from_proxy() -> bool:
|
501
576
|
"""
|
502
577
|
Unregister the server from the proxy.
|
503
|
-
|
578
|
+
|
504
579
|
Returns:
|
505
580
|
True if unregistration was successful, False otherwise.
|
506
581
|
"""
|
507
582
|
if not proxy_registration_manager:
|
508
583
|
logger.error("Proxy registration manager not initialized")
|
509
584
|
return False
|
510
|
-
|
585
|
+
|
511
586
|
return await proxy_registration_manager.unregister_server()
|
512
587
|
|
513
588
|
|
514
589
|
def get_proxy_registration_status() -> Dict[str, Any]:
|
515
590
|
"""
|
516
591
|
Get current proxy registration status.
|
517
|
-
|
592
|
+
|
518
593
|
Returns:
|
519
594
|
Dictionary with registration status information.
|
520
595
|
"""
|
521
596
|
if not proxy_registration_manager:
|
522
597
|
return {"error": "Proxy registration manager not initialized"}
|
523
|
-
|
524
|
-
return proxy_registration_manager.get_registration_status()
|
598
|
+
|
599
|
+
return proxy_registration_manager.get_registration_status()
|
@@ -10,6 +10,7 @@ email: vasilyvz@gmail.com
|
|
10
10
|
|
11
11
|
import json
|
12
12
|
import logging
|
13
|
+
import uuid
|
13
14
|
from pathlib import Path
|
14
15
|
from typing import Dict, Any, Optional
|
15
16
|
|
@@ -32,6 +33,7 @@ class ConfigGenerator:
|
|
32
33
|
def _get_template_config(self) -> Dict[str, Any]:
|
33
34
|
"""Get template configuration with all available options."""
|
34
35
|
return {
|
36
|
+
"uuid": str(uuid.uuid4()),
|
35
37
|
"server": {
|
36
38
|
"host": "0.0.0.0",
|
37
39
|
"port": 8000,
|
@@ -878,6 +880,7 @@ class ConfigGenerator:
|
|
878
880
|
def _get_comments_for_type(self, config_type: str) -> Dict[str, str]:
|
879
881
|
"""Get comments for configuration sections."""
|
880
882
|
base_comments = {
|
883
|
+
"uuid": "Unique service identifier (UUID4) - REQUIRED for service identification",
|
881
884
|
"server": "Server configuration for FastAPI application",
|
882
885
|
"ssl": "SSL/TLS configuration for secure connections",
|
883
886
|
"security": "Security framework configuration (mcp_security_framework)",
|
mcp_proxy_adapter/version.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: mcp-proxy-adapter
|
3
|
-
Version: 6.3.
|
3
|
+
Version: 6.3.2
|
4
4
|
Summary: Powerful JSON-RPC microservices framework with built-in security, authentication, and proxy registration
|
5
5
|
Home-page: https://github.com/maverikod/mcp-proxy-adapter
|
6
6
|
Author: Vasiliy Zdanovskiy
|
@@ -4,7 +4,7 @@ mcp_proxy_adapter/config.py,sha256=_VpJrmdK6NS27ABgoEABvMcgUHxCjd1D1H-HUzx3-hY,2
|
|
4
4
|
mcp_proxy_adapter/custom_openapi.py,sha256=jYUrCy8C1mShh3sjKj-JkzSMLAvxDLTvtzSJFj5HUNg,15023
|
5
5
|
mcp_proxy_adapter/main.py,sha256=Q5WxLVbQ5y9cdpcriFvIuILaxFyPyyxyxQ4OeU3xaYY,3193
|
6
6
|
mcp_proxy_adapter/openapi.py,sha256=36vOEbJjGnVZR6hUhl6mHCD29HYOEFKo2bL0JdGSm-4,13952
|
7
|
-
mcp_proxy_adapter/version.py,sha256=
|
7
|
+
mcp_proxy_adapter/version.py,sha256=A0-RO2zxRJWT7MAmVnv81okgNGwsizc4BPyzwLzaT-w,75
|
8
8
|
mcp_proxy_adapter/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
9
|
mcp_proxy_adapter/api/app.py,sha256=khl4kaI4mJ6dNbfAK7hR97Ek-eWC9NBeuXHr6GVbLoU,28911
|
10
10
|
mcp_proxy_adapter/api/handlers.py,sha256=DcZT7MVBV33q-0EJ0iFqxE0VgBkFt6d_SqoRkntwyvc,8477
|
@@ -69,7 +69,7 @@ mcp_proxy_adapter/core/mtls_asgi.py,sha256=X2lAj3wk3L85amRCp_-10sqvZa5wJf_diXhwr
|
|
69
69
|
mcp_proxy_adapter/core/mtls_asgi_app.py,sha256=VeolP08TTaqYU5fGeaZexj6EBWBDugoVrEGXzJW4PuM,6406
|
70
70
|
mcp_proxy_adapter/core/protocol_manager.py,sha256=ISFRXjUuK4Q3uMbVB8-O_ozQSsDEH0PQA_HAKGeUrrw,14763
|
71
71
|
mcp_proxy_adapter/core/proxy_client.py,sha256=n44T5iBS29y6E2lQLGKpOQxPYVrxIl98OAs6mMuhEsM,22916
|
72
|
-
mcp_proxy_adapter/core/proxy_registration.py,sha256=
|
72
|
+
mcp_proxy_adapter/core/proxy_registration.py,sha256=CCxzKwkGMLjooX6aTdeBCvad4T29nnhGBQ90FcG38f0,21278
|
73
73
|
mcp_proxy_adapter/core/role_utils.py,sha256=wMoTVz3gF5fM7jozNMwsEwPkp1tui26M-t_KH1Oz8gs,12880
|
74
74
|
mcp_proxy_adapter/core/security_adapter.py,sha256=wZ3OH1WzhUdpN8N8CrGJSFFVNi474DqdazIqQ1T8PN4,13343
|
75
75
|
mcp_proxy_adapter/core/security_factory.py,sha256=4r7qvBq30XfosGD_b1ZHyNVLN8rOQ3NAKuaCOCEK8jA,8262
|
@@ -134,10 +134,10 @@ mcp_proxy_adapter/examples/scripts/create_certificates_simple.py,sha256=xkIvUYl6
|
|
134
134
|
mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py,sha256=J0qHm_BMY8RYqfuwf7V7xKsHcsRJx8E7x-8JxmW5sPw,15988
|
135
135
|
mcp_proxy_adapter/schemas/base_schema.json,sha256=v9G9cGMd4dRhCZsOQ_FMqOi5VFyVbI6Cf3fyIvOT9dc,2881
|
136
136
|
mcp_proxy_adapter/schemas/openapi_schema.json,sha256=C3yLkwmDsvnLW9B5gnKKdBGl4zxkeU-rEmjTrNVsQU0,8405
|
137
|
-
mcp_proxy_adapter/utils/config_generator.py,sha256=
|
138
|
-
mcp_proxy_adapter-6.3.
|
139
|
-
mcp_proxy_adapter-6.3.
|
140
|
-
mcp_proxy_adapter-6.3.
|
141
|
-
mcp_proxy_adapter-6.3.
|
142
|
-
mcp_proxy_adapter-6.3.
|
143
|
-
mcp_proxy_adapter-6.3.
|
137
|
+
mcp_proxy_adapter/utils/config_generator.py,sha256=HuNqUgV-aoJ6A1LfX_EGJAVsj-tbaOcV2H6jw6TjfmY,46648
|
138
|
+
mcp_proxy_adapter-6.3.2.dist-info/licenses/LICENSE,sha256=6KdtUcTwmTRbJrAmYjVn7e6S-V42ubeDJ-AiVEzZ510,1075
|
139
|
+
mcp_proxy_adapter-6.3.2.dist-info/METADATA,sha256=x487vHH_rG-nAcWPh8pfh6ZHteSfTpF_gu3H3BZMRxk,22347
|
140
|
+
mcp_proxy_adapter-6.3.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
141
|
+
mcp_proxy_adapter-6.3.2.dist-info/entry_points.txt,sha256=J3eV6ID0lt_VSp4lIdIgBFTqLCThgObNNxRCbyfiMHw,70
|
142
|
+
mcp_proxy_adapter-6.3.2.dist-info/top_level.txt,sha256=JZT7vPLBYrtroX-ij68JBhJYbjDdghcV-DFySRy-Nnw,18
|
143
|
+
mcp_proxy_adapter-6.3.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|