mcp-proxy-adapter 6.3.1__py3-none-any.whl → 6.3.3__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 +212 -130
- mcp_proxy_adapter/version.py +1 -1
- {mcp_proxy_adapter-6.3.1.dist-info → mcp_proxy_adapter-6.3.3.dist-info}/METADATA +1 -1
- {mcp_proxy_adapter-6.3.1.dist-info → mcp_proxy_adapter-6.3.3.dist-info}/RECORD +8 -8
- {mcp_proxy_adapter-6.3.1.dist-info → mcp_proxy_adapter-6.3.3.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.3.1.dist-info → mcp_proxy_adapter-6.3.3.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.3.1.dist-info → mcp_proxy_adapter-6.3.3.dist-info}/licenses/LICENSE +0 -0
- {mcp_proxy_adapter-6.3.1.dist-info → mcp_proxy_adapter-6.3.3.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,150 @@ 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
|
+
|
349
|
+
# Handle duplicate server URL as successful registration
|
350
|
+
if error_code == "DUPLICATE_SERVER_URL":
|
351
|
+
logger.info(f"✅ Server already registered: {error_msg}")
|
352
|
+
# Return success=True for duplicate registration
|
353
|
+
return True, result
|
354
|
+
else:
|
355
|
+
logger.warning(
|
356
|
+
f"Registration failed: {error_code} - {error_msg}"
|
357
|
+
)
|
358
|
+
return success, result
|
359
|
+
else:
|
360
|
+
logger.warning(
|
361
|
+
f"Registration failed with HTTP status: {response.status}"
|
362
|
+
)
|
363
|
+
return False, result
|
304
364
|
finally:
|
305
365
|
if connector:
|
306
366
|
await connector.close()
|
307
|
-
|
308
|
-
async def _make_secure_unregistration_request(
|
367
|
+
|
368
|
+
async def _make_secure_unregistration_request(
|
369
|
+
self, data: Dict[str, Any]
|
370
|
+
) -> Tuple[bool, Dict[str, Any]]:
|
309
371
|
"""
|
310
372
|
Make secure unregistration request to proxy using security framework.
|
311
|
-
|
373
|
+
|
312
374
|
Args:
|
313
375
|
data: Unregistration data.
|
314
|
-
|
376
|
+
|
315
377
|
Returns:
|
316
378
|
Tuple of (success, result).
|
317
379
|
"""
|
318
380
|
url = urljoin(self.proxy_url, "/unregister")
|
319
|
-
|
381
|
+
|
320
382
|
# Get authentication headers
|
321
383
|
headers = self._get_auth_headers()
|
322
384
|
headers["Content-Type"] = "application/json"
|
323
|
-
|
385
|
+
|
324
386
|
# Create SSL context if needed
|
325
387
|
ssl_context = self._create_ssl_context()
|
326
|
-
|
388
|
+
|
327
389
|
# Create connector with SSL context
|
328
390
|
connector = None
|
329
391
|
if ssl_context:
|
330
392
|
connector = aiohttp.TCPConnector(ssl=ssl_context)
|
331
|
-
|
393
|
+
|
332
394
|
try:
|
333
395
|
async with aiohttp.ClientSession(connector=connector) as session:
|
334
396
|
async with session.post(
|
335
397
|
url,
|
336
398
|
json=data,
|
337
399
|
headers=headers,
|
338
|
-
timeout=aiohttp.ClientTimeout(total=self.timeout)
|
400
|
+
timeout=aiohttp.ClientTimeout(total=self.timeout),
|
339
401
|
) as response:
|
340
402
|
result = await response.json()
|
341
|
-
|
403
|
+
|
342
404
|
# Validate response headers if security framework available
|
343
405
|
if self.client_security:
|
344
|
-
self.client_security.validate_server_response(
|
345
|
-
|
346
|
-
|
406
|
+
self.client_security.validate_server_response(
|
407
|
+
dict(response.headers)
|
408
|
+
)
|
409
|
+
|
410
|
+
# Check both HTTP status and JSON success field
|
411
|
+
if response.status == 200:
|
412
|
+
success = result.get("success", False)
|
413
|
+
if not success:
|
414
|
+
error_info = result.get("error", {})
|
415
|
+
error_msg = error_info.get("message", "Unknown error")
|
416
|
+
error_code = error_info.get("code", "UNKNOWN_ERROR")
|
417
|
+
logger.warning(
|
418
|
+
f"Unregistration failed: {error_code} - {error_msg}"
|
419
|
+
)
|
420
|
+
return success, result
|
421
|
+
else:
|
422
|
+
logger.warning(
|
423
|
+
f"Unregistration failed with HTTP status: {response.status}"
|
424
|
+
)
|
425
|
+
return False, result
|
347
426
|
finally:
|
348
427
|
if connector:
|
349
428
|
await connector.close()
|
350
|
-
|
429
|
+
|
351
430
|
async def _start_heartbeat(self) -> None:
|
352
431
|
"""Start heartbeat task for keeping registration alive."""
|
353
432
|
if self.heartbeat_task and not self.heartbeat_task.done():
|
354
433
|
return
|
355
|
-
|
434
|
+
|
356
435
|
self.heartbeat_task = asyncio.create_task(self._heartbeat_loop())
|
357
436
|
logger.info("Heartbeat task started")
|
358
|
-
|
437
|
+
|
359
438
|
async def _stop_heartbeat(self) -> None:
|
360
439
|
"""Stop heartbeat task."""
|
361
440
|
if self.heartbeat_task and not self.heartbeat_task.done():
|
@@ -365,65 +444,67 @@ class ProxyRegistrationManager:
|
|
365
444
|
except asyncio.CancelledError:
|
366
445
|
pass
|
367
446
|
logger.info("Heartbeat task stopped")
|
368
|
-
|
447
|
+
|
369
448
|
async def _heartbeat_loop(self) -> None:
|
370
449
|
"""Heartbeat loop to keep registration alive."""
|
371
450
|
while self.registered:
|
372
451
|
try:
|
373
452
|
await asyncio.sleep(self.heartbeat_interval)
|
374
|
-
|
453
|
+
|
375
454
|
if not self.registered:
|
376
455
|
break
|
377
|
-
|
456
|
+
|
378
457
|
# Send heartbeat
|
379
458
|
success = await self._send_heartbeat()
|
380
459
|
if not success:
|
381
460
|
logger.warning("Heartbeat failed, attempting to re-register")
|
382
461
|
await self.register_server()
|
383
|
-
|
462
|
+
|
384
463
|
except asyncio.CancelledError:
|
385
464
|
break
|
386
465
|
except Exception as e:
|
387
466
|
logger.error(f"Heartbeat error: {e}")
|
388
|
-
|
467
|
+
|
389
468
|
async def _send_heartbeat(self) -> bool:
|
390
469
|
"""Send heartbeat to proxy server."""
|
391
470
|
if not self.server_key:
|
392
471
|
return False
|
393
|
-
|
472
|
+
|
394
473
|
heartbeat_data = {
|
395
474
|
"server_id": self.server_id,
|
396
475
|
"server_key": self.server_key,
|
397
|
-
"timestamp": int(time.time())
|
476
|
+
"timestamp": int(time.time()),
|
398
477
|
}
|
399
|
-
|
478
|
+
|
400
479
|
url = urljoin(self.proxy_url, "/heartbeat")
|
401
|
-
|
480
|
+
|
402
481
|
# Get authentication headers
|
403
482
|
headers = self._get_auth_headers()
|
404
483
|
headers["Content-Type"] = "application/json"
|
405
|
-
|
484
|
+
|
406
485
|
# Create SSL context if needed
|
407
486
|
ssl_context = self._create_ssl_context()
|
408
|
-
|
487
|
+
|
409
488
|
# Create connector with SSL context
|
410
489
|
connector = None
|
411
490
|
if ssl_context:
|
412
491
|
connector = aiohttp.TCPConnector(ssl=ssl_context)
|
413
|
-
|
492
|
+
|
414
493
|
try:
|
415
494
|
async with aiohttp.ClientSession(connector=connector) as session:
|
416
495
|
async with session.post(
|
417
496
|
url,
|
418
497
|
json=heartbeat_data,
|
419
498
|
headers=headers,
|
420
|
-
timeout=aiohttp.ClientTimeout(total=self.timeout)
|
499
|
+
timeout=aiohttp.ClientTimeout(total=self.timeout),
|
421
500
|
) as response:
|
422
501
|
if response.status == 200:
|
423
502
|
logger.debug("Heartbeat sent successfully")
|
424
503
|
return True
|
425
504
|
else:
|
426
|
-
logger.warning(
|
505
|
+
logger.warning(
|
506
|
+
f"Heartbeat failed with status: {response.status}"
|
507
|
+
)
|
427
508
|
return False
|
428
509
|
except Exception as e:
|
429
510
|
logger.error(f"Heartbeat error: {e}")
|
@@ -431,11 +512,11 @@ class ProxyRegistrationManager:
|
|
431
512
|
finally:
|
432
513
|
if connector:
|
433
514
|
await connector.close()
|
434
|
-
|
515
|
+
|
435
516
|
def get_registration_status(self) -> Dict[str, Any]:
|
436
517
|
"""
|
437
518
|
Get current registration status.
|
438
|
-
|
519
|
+
|
439
520
|
Returns:
|
440
521
|
Dictionary with registration status information.
|
441
522
|
"""
|
@@ -446,21 +527,22 @@ class ProxyRegistrationManager:
|
|
446
527
|
"server_url": self.server_url,
|
447
528
|
"proxy_url": self.proxy_url,
|
448
529
|
"server_id": self.server_id,
|
449
|
-
"heartbeat_active": self.heartbeat_task is not None
|
530
|
+
"heartbeat_active": self.heartbeat_task is not None
|
531
|
+
and not self.heartbeat_task.done(),
|
450
532
|
}
|
451
|
-
|
533
|
+
|
452
534
|
# Add security information if available
|
453
535
|
if self.client_security:
|
454
536
|
status["security_enabled"] = True
|
455
537
|
status["ssl_enabled"] = self.client_security.is_ssl_enabled()
|
456
538
|
status["auth_methods"] = self.client_security.get_supported_auth_methods()
|
457
|
-
|
539
|
+
|
458
540
|
cert_info = self.client_security.get_client_certificate_info()
|
459
541
|
if cert_info:
|
460
542
|
status["client_certificate"] = cert_info
|
461
543
|
else:
|
462
544
|
status["security_enabled"] = False
|
463
|
-
|
545
|
+
|
464
546
|
return status
|
465
547
|
|
466
548
|
|
@@ -471,7 +553,7 @@ proxy_registration_manager: Optional[ProxyRegistrationManager] = None
|
|
471
553
|
def initialize_proxy_registration(config: Dict[str, Any]) -> None:
|
472
554
|
"""
|
473
555
|
Initialize global proxy registration manager.
|
474
|
-
|
556
|
+
|
475
557
|
Args:
|
476
558
|
config: Application configuration
|
477
559
|
"""
|
@@ -482,17 +564,17 @@ def initialize_proxy_registration(config: Dict[str, Any]) -> None:
|
|
482
564
|
async def register_with_proxy(server_url: str) -> bool:
|
483
565
|
"""
|
484
566
|
Register the server with the proxy.
|
485
|
-
|
567
|
+
|
486
568
|
Args:
|
487
569
|
server_url: The URL where this server is accessible.
|
488
|
-
|
570
|
+
|
489
571
|
Returns:
|
490
572
|
True if registration was successful, False otherwise.
|
491
573
|
"""
|
492
574
|
if not proxy_registration_manager:
|
493
575
|
logger.error("Proxy registration manager not initialized")
|
494
576
|
return False
|
495
|
-
|
577
|
+
|
496
578
|
proxy_registration_manager.set_server_url(server_url)
|
497
579
|
return await proxy_registration_manager.register_server()
|
498
580
|
|
@@ -500,25 +582,25 @@ async def register_with_proxy(server_url: str) -> bool:
|
|
500
582
|
async def unregister_from_proxy() -> bool:
|
501
583
|
"""
|
502
584
|
Unregister the server from the proxy.
|
503
|
-
|
585
|
+
|
504
586
|
Returns:
|
505
587
|
True if unregistration was successful, False otherwise.
|
506
588
|
"""
|
507
589
|
if not proxy_registration_manager:
|
508
590
|
logger.error("Proxy registration manager not initialized")
|
509
591
|
return False
|
510
|
-
|
592
|
+
|
511
593
|
return await proxy_registration_manager.unregister_server()
|
512
594
|
|
513
595
|
|
514
596
|
def get_proxy_registration_status() -> Dict[str, Any]:
|
515
597
|
"""
|
516
598
|
Get current proxy registration status.
|
517
|
-
|
599
|
+
|
518
600
|
Returns:
|
519
601
|
Dictionary with registration status information.
|
520
602
|
"""
|
521
603
|
if not proxy_registration_manager:
|
522
604
|
return {"error": "Proxy registration manager not initialized"}
|
523
|
-
|
524
|
-
return proxy_registration_manager.get_registration_status()
|
605
|
+
|
606
|
+
return proxy_registration_manager.get_registration_status()
|
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.3
|
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=DQyL1W8pzzedZ1q9hB9HK-KI0eMp9cNmMrHYGTm8qPs,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=T9MyzVz3flMbJOvBpPBbAbvFpwnOWkYJ5cnfpQ3-uoc,21731
|
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
|
@@ -135,9 +135,9 @@ mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py,sha256=J0
|
|
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
137
|
mcp_proxy_adapter/utils/config_generator.py,sha256=HuNqUgV-aoJ6A1LfX_EGJAVsj-tbaOcV2H6jw6TjfmY,46648
|
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.
|
138
|
+
mcp_proxy_adapter-6.3.3.dist-info/licenses/LICENSE,sha256=6KdtUcTwmTRbJrAmYjVn7e6S-V42ubeDJ-AiVEzZ510,1075
|
139
|
+
mcp_proxy_adapter-6.3.3.dist-info/METADATA,sha256=nrlkEFLOyzg7MOA4fcfp-4zgSnfET5JfgXJOW8EW1qU,22347
|
140
|
+
mcp_proxy_adapter-6.3.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
141
|
+
mcp_proxy_adapter-6.3.3.dist-info/entry_points.txt,sha256=J3eV6ID0lt_VSp4lIdIgBFTqLCThgObNNxRCbyfiMHw,70
|
142
|
+
mcp_proxy_adapter-6.3.3.dist-info/top_level.txt,sha256=JZT7vPLBYrtroX-ij68JBhJYbjDdghcV-DFySRy-Nnw,18
|
143
|
+
mcp_proxy_adapter-6.3.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|