mcp-mesh 0.5.4__py3-none-any.whl → 0.5.6__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.
@@ -0,0 +1,312 @@
1
+ """
2
+ Shared utilities for discovering existing FastAPI and uvicorn server instances.
3
+
4
+ This module provides utilities to discover running servers that have been started
5
+ outside the pipeline, such as by immediate uvicorn start in decorators.
6
+ """
7
+
8
+ import gc
9
+ import logging
10
+ import socket
11
+ from typing import Any, Dict, List, Optional, Tuple
12
+ import threading
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class ServerDiscoveryUtil:
18
+ """Utility class for discovering existing FastAPI apps and uvicorn servers."""
19
+
20
+ @staticmethod
21
+ def discover_fastapi_instances() -> Dict[str, Dict[str, Any]]:
22
+ """
23
+ Discover FastAPI application instances in the Python runtime.
24
+
25
+ Uses intelligent deduplication to handle standard uvicorn patterns where
26
+ the same app might be imported multiple times (e.g., "module:app" pattern).
27
+
28
+ Returns:
29
+ Dict mapping app_id -> app_info where app_info contains:
30
+ - 'instance': The FastAPI app instance
31
+ - 'title': App title from FastAPI
32
+ - 'routes': List of route information
33
+ - 'module': Module where app was found
34
+ """
35
+ fastapi_apps = {}
36
+ seen_apps = {} # For deduplication: title -> app_info
37
+
38
+ try:
39
+ # Import FastAPI here to avoid dependency if not used
40
+ from fastapi import FastAPI
41
+ except ImportError:
42
+ logger.warning("FastAPI not installed - cannot discover FastAPI apps")
43
+ return {}
44
+
45
+ # Scan garbage collector for FastAPI instances
46
+ candidate_apps = []
47
+ for obj in gc.get_objects():
48
+ if isinstance(obj, FastAPI):
49
+ candidate_apps.append(obj)
50
+
51
+ # Deduplicate apps with identical configurations
52
+ for obj in candidate_apps:
53
+ try:
54
+ title = getattr(obj, "title", "FastAPI App")
55
+ version = getattr(obj, "version", "unknown")
56
+ routes = ServerDiscoveryUtil._extract_route_info(obj)
57
+ route_count = len(routes)
58
+
59
+ # Create a signature for deduplication
60
+ app_signature = (title, version, route_count)
61
+
62
+ # Check if we've seen an identical app
63
+ if app_signature in seen_apps:
64
+ existing_app = seen_apps[app_signature]
65
+ # Compare route details to ensure they're truly identical
66
+ existing_routes = existing_app["routes"]
67
+
68
+ if ServerDiscoveryUtil._routes_are_identical(routes, existing_routes):
69
+ logger.debug(
70
+ f"Skipping duplicate FastAPI app: '{title}' (same title, version, and routes)"
71
+ )
72
+ continue # Skip this duplicate
73
+
74
+ # This is a unique app, add it
75
+ app_id = f"app_{id(obj)}"
76
+ app_info = {
77
+ "instance": obj,
78
+ "title": title,
79
+ "version": version,
80
+ "routes": routes,
81
+ "module": ServerDiscoveryUtil._get_app_module(obj),
82
+ "object_id": id(obj),
83
+ "router_routes_count": len(obj.router.routes) if hasattr(obj, 'router') else 0,
84
+ }
85
+
86
+ fastapi_apps[app_id] = app_info
87
+ seen_apps[app_signature] = app_info
88
+
89
+ logger.debug(
90
+ f"Found FastAPI app: '{title}' (module: {app_info['module']}) with "
91
+ f"{len(routes)} routes"
92
+ )
93
+
94
+ except Exception as e:
95
+ logger.warning(f"Error analyzing FastAPI app: {e}")
96
+ continue
97
+
98
+ return fastapi_apps
99
+
100
+ @staticmethod
101
+ def discover_running_servers() -> List[Dict[str, Any]]:
102
+ """
103
+ Discover running uvicorn servers by scanning threads and checking port bindings.
104
+
105
+ Returns:
106
+ List of server info dictionaries containing:
107
+ - 'type': 'uvicorn' or 'unknown'
108
+ - 'host': Server host
109
+ - 'port': Server port
110
+ - 'thread': Thread object if found
111
+ - 'app': FastAPI app if discoverable
112
+ """
113
+ running_servers = []
114
+
115
+ # Look for uvicorn server threads
116
+ for thread in threading.enumerate():
117
+ if hasattr(thread, '_target'):
118
+ # Check if thread target looks like a uvicorn server
119
+ target_name = getattr(thread._target, '__name__', '') if thread._target else ''
120
+ if 'server' in target_name.lower() or 'uvicorn' in target_name.lower():
121
+ server_info = {
122
+ 'type': 'uvicorn',
123
+ 'thread': thread,
124
+ 'target_name': target_name,
125
+ 'daemon': thread.daemon,
126
+ 'alive': thread.is_alive(),
127
+ }
128
+
129
+ # Try to extract server details from thread
130
+ server_details = ServerDiscoveryUtil._extract_server_details_from_thread(thread)
131
+ server_info.update(server_details)
132
+
133
+ running_servers.append(server_info)
134
+ logger.debug(f"Found running server thread: {target_name} (daemon={thread.daemon})")
135
+
136
+ # Also check for bound ports that might indicate running servers
137
+ bound_ports = ServerDiscoveryUtil._discover_bound_ports()
138
+ for port_info in bound_ports:
139
+ # Only add if we haven't already found this port via thread discovery
140
+ existing_ports = [s.get('port') for s in running_servers if s.get('port')]
141
+ if port_info['port'] not in existing_ports:
142
+ port_info['type'] = 'unknown'
143
+ running_servers.append(port_info)
144
+ logger.debug(f"Found bound port: {port_info['host']}:{port_info['port']}")
145
+
146
+ return running_servers
147
+
148
+ @staticmethod
149
+ def _extract_server_details_from_thread(thread) -> Dict[str, Any]:
150
+ """Extract server details from a thread if possible."""
151
+ details = {}
152
+
153
+ try:
154
+ # Try to access thread local variables or target args
155
+ if hasattr(thread, '_args') and thread._args:
156
+ # Some uvicorn servers might have args with host/port
157
+ args = thread._args
158
+ if len(args) >= 2:
159
+ # Common pattern: (app, host, port) or similar
160
+ if isinstance(args[0], str) and ':' in args[0]:
161
+ # Might be "host:port" format
162
+ try:
163
+ host, port = args[0].split(':')
164
+ details['host'] = host
165
+ details['port'] = int(port)
166
+ except (ValueError, IndexError):
167
+ pass
168
+
169
+ # Try to find FastAPI app in thread target or args
170
+ if hasattr(thread, '_target') and thread._target:
171
+ # Check if target has app attribute or if it's in closure
172
+ target = thread._target
173
+ if hasattr(target, '__closure__') and target.__closure__:
174
+ for cell in target.__closure__:
175
+ try:
176
+ cell_contents = cell.cell_contents
177
+ from fastapi import FastAPI
178
+ if isinstance(cell_contents, FastAPI):
179
+ details['app'] = cell_contents
180
+ details['app_title'] = getattr(cell_contents, 'title', 'Unknown')
181
+ break
182
+ except (ImportError, AttributeError):
183
+ continue
184
+
185
+ except Exception as e:
186
+ logger.debug(f"Could not extract server details from thread: {e}")
187
+
188
+ return details
189
+
190
+ @staticmethod
191
+ def _discover_bound_ports() -> List[Dict[str, Any]]:
192
+ """Discover ports that are currently bound by this process."""
193
+ bound_ports = []
194
+
195
+ try:
196
+ # Common port ranges for web servers
197
+ common_ports = [8000, 8080, 8090, 9090, 9091, 3000, 3001, 4000, 5000]
198
+
199
+ for port in common_ports:
200
+ for host in ['127.0.0.1', '0.0.0.0', 'localhost']:
201
+ try:
202
+ # Try to connect to see if port is bound
203
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
204
+ sock.settimeout(0.1) # Very short timeout
205
+ result = sock.connect_ex((host, port))
206
+ sock.close()
207
+
208
+ if result == 0: # Connection successful = port is bound
209
+ bound_ports.append({
210
+ 'host': host,
211
+ 'port': port,
212
+ 'status': 'bound'
213
+ })
214
+ break # Don't check other hosts for same port
215
+ except Exception:
216
+ continue
217
+
218
+ except Exception as e:
219
+ logger.debug(f"Error discovering bound ports: {e}")
220
+
221
+ return bound_ports
222
+
223
+ @staticmethod
224
+ def find_server_on_port(target_port: int, target_host: str = None) -> Optional[Dict[str, Any]]:
225
+ """
226
+ Find if there's already a server running on the specified port.
227
+
228
+ Args:
229
+ target_port: Port to check
230
+ target_host: Host to check (optional)
231
+
232
+ Returns:
233
+ Server info dict if found, None otherwise
234
+ """
235
+ running_servers = ServerDiscoveryUtil.discover_running_servers()
236
+
237
+ for server in running_servers:
238
+ server_port = server.get('port')
239
+ server_host = server.get('host')
240
+
241
+ # Check port match
242
+ if server_port == target_port:
243
+ # If target_host is specified, check host match too
244
+ if target_host is None or server_host == target_host or server_host in ['0.0.0.0', '127.0.0.1']:
245
+ logger.info(f"🔍 DISCOVERY: Found existing server on {server_host}:{server_port}")
246
+ return server
247
+
248
+ logger.debug(f"🔍 DISCOVERY: No existing server found on port {target_port}")
249
+ return None
250
+
251
+ @staticmethod
252
+ def _routes_are_identical(routes1: List[Dict[str, Any]], routes2: List[Dict[str, Any]]) -> bool:
253
+ """Compare two route lists to see if they're identical."""
254
+ if len(routes1) != len(routes2):
255
+ return False
256
+
257
+ # Create comparable signatures for each route
258
+ def route_signature(route):
259
+ return (
260
+ tuple(sorted(route.get('methods', []))), # Sort methods for consistent comparison
261
+ route.get('path', ''),
262
+ route.get('endpoint_name', '')
263
+ )
264
+
265
+ # Sort routes by signature for consistent comparison
266
+ sig1 = sorted([route_signature(r) for r in routes1])
267
+ sig2 = sorted([route_signature(r) for r in routes2])
268
+
269
+ return sig1 == sig2
270
+
271
+ @staticmethod
272
+ def _extract_route_info(app) -> List[Dict[str, Any]]:
273
+ """Extract route information from FastAPI app without modifying it."""
274
+ routes = []
275
+
276
+ try:
277
+ for route in app.router.routes:
278
+ if hasattr(route, 'endpoint') and hasattr(route, 'path'):
279
+ route_info = {
280
+ "path": route.path,
281
+ "methods": list(route.methods) if hasattr(route, 'methods') else [],
282
+ "endpoint": route.endpoint,
283
+ "endpoint_name": getattr(route.endpoint, '__name__', 'unknown'),
284
+ "has_mesh_route": hasattr(route.endpoint, '_mesh_route_metadata'),
285
+ }
286
+ routes.append(route_info)
287
+
288
+ except Exception as e:
289
+ logger.warning(f"Error extracting route info: {e}")
290
+
291
+ return routes
292
+
293
+ @staticmethod
294
+ def _get_app_module(app) -> Optional[str]:
295
+ """Try to determine which module the FastAPI app belongs to."""
296
+ try:
297
+ # Try to get module from the app's stack frame when it was created
298
+ # This is best-effort - may not always work
299
+ import inspect
300
+
301
+ frame = inspect.currentframe()
302
+ while frame:
303
+ frame_globals = frame.f_globals
304
+ for name, obj in frame_globals.items():
305
+ if obj is app:
306
+ return frame_globals.get('__name__', 'unknown')
307
+ frame = frame.f_back
308
+
309
+ except Exception:
310
+ pass
311
+
312
+ return None
@@ -0,0 +1,217 @@
1
+ """
2
+ Simple shutdown coordination for MCP Mesh agents.
3
+
4
+ Provides clean shutdown via FastAPI lifespan events and basic signal handling.
5
+ """
6
+
7
+ import asyncio
8
+ import logging
9
+ import signal
10
+ from contextlib import asynccontextmanager
11
+ from typing import Any, Optional
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class SimpleShutdownCoordinator:
17
+ """Lightweight shutdown coordination using FastAPI lifespan."""
18
+
19
+ def __init__(self):
20
+ self._shutdown_requested = False
21
+ self._registry_url: Optional[str] = None
22
+ self._agent_id: Optional[str] = None
23
+ self._shutdown_complete = False # Flag to prevent race conditions
24
+
25
+ def set_shutdown_context(self, registry_url: str, agent_id: str) -> None:
26
+ """Set context for shutdown cleanup."""
27
+ self._registry_url = registry_url
28
+ self._agent_id = agent_id
29
+ logger.debug(
30
+ f"🔧 Shutdown context set: agent_id={agent_id}, registry_url={registry_url}"
31
+ )
32
+
33
+ def install_signal_handlers(self) -> None:
34
+ """Install minimal signal handlers as backup."""
35
+
36
+ def shutdown_signal_handler(signum, frame):
37
+ # Avoid logging in signal handler to prevent reentrant call issues
38
+ self._shutdown_requested = True
39
+
40
+ signal.signal(signal.SIGINT, shutdown_signal_handler)
41
+ signal.signal(signal.SIGTERM, shutdown_signal_handler)
42
+ logger.debug("📡 Signal handlers installed")
43
+
44
+ def is_shutdown_requested(self) -> bool:
45
+ """Check if shutdown was requested via signal."""
46
+ return self._shutdown_requested
47
+
48
+ def is_shutdown_complete(self) -> bool:
49
+ """Check if shutdown cleanup is complete."""
50
+ return self._shutdown_complete
51
+
52
+ def mark_shutdown_complete(self) -> None:
53
+ """Mark shutdown cleanup as complete to prevent further operations."""
54
+ self._shutdown_complete = True
55
+ logger.debug("🏁 Shutdown marked as complete")
56
+
57
+ async def perform_registry_cleanup(self) -> None:
58
+ """Perform registry cleanup by calling DELETE /agents/{agent_id}."""
59
+ # Try to get the actual agent_id from DecoratorRegistry if available
60
+ actual_agent_id = self._agent_id
61
+ try:
62
+ from _mcp_mesh.engine.decorator_registry import DecoratorRegistry
63
+
64
+ agent_config = DecoratorRegistry.get_resolved_agent_config()
65
+ if agent_config and "agent_id" in agent_config:
66
+ resolved_agent_id = agent_config["agent_id"]
67
+ if resolved_agent_id and resolved_agent_id != "unknown":
68
+ actual_agent_id = resolved_agent_id
69
+ logger.debug(
70
+ f"🔧 Using resolved agent_id from DecoratorRegistry: {actual_agent_id}"
71
+ )
72
+ except Exception as e:
73
+ logger.debug(f"Could not get agent_id from DecoratorRegistry: {e}")
74
+
75
+ if (
76
+ not self._registry_url
77
+ or not actual_agent_id
78
+ or actual_agent_id == "unknown"
79
+ ):
80
+ logger.warning(
81
+ f"⚠️ Missing registry URL or agent ID for cleanup: registry_url={self._registry_url}, agent_id={actual_agent_id}"
82
+ )
83
+ return
84
+
85
+ try:
86
+ from _mcp_mesh.generated.mcp_mesh_registry_client.api_client import (
87
+ ApiClient,
88
+ )
89
+ from _mcp_mesh.generated.mcp_mesh_registry_client.configuration import (
90
+ Configuration,
91
+ )
92
+ from _mcp_mesh.shared.registry_client_wrapper import RegistryClientWrapper
93
+
94
+ config = Configuration(host=self._registry_url)
95
+ api_client = ApiClient(configuration=config)
96
+ registry_wrapper = RegistryClientWrapper(api_client)
97
+
98
+ success = await registry_wrapper.unregister_agent(actual_agent_id)
99
+ if success:
100
+ logger.info(f"✅ Agent '{actual_agent_id}' unregistered from registry")
101
+ self.mark_shutdown_complete()
102
+ else:
103
+ logger.warning(f"⚠️ Failed to unregister agent '{actual_agent_id}'")
104
+ self.mark_shutdown_complete() # Mark complete even on failure to prevent loops
105
+
106
+ except Exception as e:
107
+ logger.error(f"❌ Registry cleanup error: {e}")
108
+ self.mark_shutdown_complete() # Mark complete even on error to prevent loops
109
+
110
+ def create_shutdown_lifespan(self, original_lifespan=None):
111
+ """Create lifespan function that includes registry cleanup."""
112
+
113
+ @asynccontextmanager
114
+ async def shutdown_lifespan(app):
115
+ # Startup phase
116
+ if original_lifespan:
117
+ # If user had a lifespan, run their startup code
118
+ async with original_lifespan(app):
119
+ yield
120
+ else:
121
+ yield
122
+
123
+ # Shutdown phase
124
+ logger.info("🔄 FastAPI shutdown initiated, performing registry cleanup...")
125
+ await self.perform_registry_cleanup()
126
+ logger.info("🏁 Registry cleanup completed")
127
+
128
+ return shutdown_lifespan
129
+
130
+ def inject_shutdown_lifespan(self, app, registry_url: str, agent_id: str) -> None:
131
+ """Inject shutdown lifespan into FastAPI app."""
132
+ self.set_shutdown_context(registry_url, agent_id)
133
+
134
+ # Store original lifespan if it exists
135
+ original_lifespan = getattr(app, "router", {}).get("lifespan", None)
136
+
137
+ # Replace with our shutdown-aware lifespan
138
+ new_lifespan = self.create_shutdown_lifespan(original_lifespan)
139
+ app.router.lifespan = new_lifespan
140
+
141
+ logger.info(f"🔌 Shutdown lifespan injected for agent '{agent_id}'")
142
+
143
+
144
+ # Global instance
145
+ _simple_shutdown_coordinator = SimpleShutdownCoordinator()
146
+
147
+
148
+ def inject_shutdown_lifespan(app, registry_url: str, agent_id: str) -> None:
149
+ """Inject shutdown lifespan into FastAPI app (module-level function)."""
150
+ _simple_shutdown_coordinator.inject_shutdown_lifespan(app, registry_url, agent_id)
151
+
152
+
153
+ def install_signal_handlers() -> None:
154
+ """Install signal handlers (module-level function)."""
155
+ _simple_shutdown_coordinator.install_signal_handlers()
156
+
157
+
158
+ def should_stop_heartbeat() -> bool:
159
+ """Check if heartbeat should stop due to shutdown."""
160
+ return _simple_shutdown_coordinator.is_shutdown_complete()
161
+
162
+
163
+ def start_blocking_loop_with_shutdown_support(thread) -> None:
164
+ """
165
+ Keep main thread alive while uvicorn in the thread handles requests.
166
+
167
+ Install signal handlers in main thread for proper registry cleanup since
168
+ signals to threads can be unreliable for FastAPI lifespan shutdown.
169
+ """
170
+ logger.info("🔒 MAIN THREAD: Installing signal handlers for registry cleanup")
171
+
172
+ # Install signal handlers for proper registry cleanup
173
+ _simple_shutdown_coordinator.install_signal_handlers()
174
+
175
+ logger.info(
176
+ "🔒 MAIN THREAD: Waiting for uvicorn thread - signals handled by main thread"
177
+ )
178
+
179
+ try:
180
+ # Wait for thread while handling signals in main thread
181
+ while thread.is_alive():
182
+ thread.join(timeout=1.0)
183
+
184
+ # Check if shutdown was requested via signal
185
+ if _simple_shutdown_coordinator.is_shutdown_requested():
186
+ logger.info(
187
+ "🔄 MAIN THREAD: Shutdown requested, performing registry cleanup..."
188
+ )
189
+
190
+ # Perform registry cleanup in main thread
191
+ import asyncio
192
+
193
+ try:
194
+ # Run cleanup in main thread
195
+ asyncio.run(_simple_shutdown_coordinator.perform_registry_cleanup())
196
+ except Exception as e:
197
+ logger.error(f"❌ Registry cleanup error: {e}")
198
+
199
+ logger.info("🏁 MAIN THREAD: Registry cleanup completed, exiting")
200
+ break
201
+
202
+ except KeyboardInterrupt:
203
+ logger.info(
204
+ "🔄 MAIN THREAD: KeyboardInterrupt received, performing registry cleanup..."
205
+ )
206
+
207
+ # Perform registry cleanup on Ctrl+C
208
+ import asyncio
209
+
210
+ try:
211
+ asyncio.run(_simple_shutdown_coordinator.perform_registry_cleanup())
212
+ except Exception as e:
213
+ logger.error(f"❌ Registry cleanup error: {e}")
214
+
215
+ logger.info("🏁 MAIN THREAD: Registry cleanup completed")
216
+
217
+ logger.info("🏁 MAIN THREAD: Uvicorn thread completed")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-mesh
3
- Version: 0.5.4
3
+ Version: 0.5.6
4
4
  Summary: Kubernetes-native platform for distributed MCP applications
5
5
  Project-URL: Homepage, https://github.com/dhyansraj/mcp-mesh
6
6
  Project-URL: Documentation, https://github.com/dhyansraj/mcp-mesh/tree/main/docs
@@ -1,17 +1,16 @@
1
- _mcp_mesh/__init__.py,sha256=oKxTjy-zjkUN12K-L9eYnWb7ZKGieCTyRvS2s-STPoc,2646
1
+ _mcp_mesh/__init__.py,sha256=uw4Yj9HoBfp-_CyH8izKGYKrrCI-dF9uwMLMNgkIMN0,2719
2
2
  _mcp_mesh/engine/__init__.py,sha256=2ennzbo7yJcpkXO9BqN69TruLjJfmJY4Y5VEsG644K4,3630
3
3
  _mcp_mesh/engine/async_mcp_client.py,sha256=UcbQjxtgVfeRw6DHTZhAzN1gkcKlTg-lUPEePRPQWAU,6306
4
- _mcp_mesh/engine/decorator_registry.py,sha256=Dy3de-CQa3T4-BRV5Xx6Ks3s6gvgL-lcxCYLsfrTdX0,19085
4
+ _mcp_mesh/engine/decorator_registry.py,sha256=0-GI7DILpbB6GRLIYhZu0DcdHEA62d89jDYxkK1ywiM,22422
5
5
  _mcp_mesh/engine/dependency_injector.py,sha256=B1iRxtIXq_z4LnJ6NTs0ZPdEIlijrMJv5KbDpQbq9iw,23752
6
6
  _mcp_mesh/engine/full_mcp_proxy.py,sha256=PlRv7GSKqn5riOCqeCVulVdtq3z1Ug76mOkwMsOFHXw,25297
7
7
  _mcp_mesh/engine/http_wrapper.py,sha256=PeYR7HkJVX7Hb0W-uYSMO-Y14VqCyrPlWzA6vnwf1Tw,21640
8
- _mcp_mesh/engine/mcp_client_proxy.py,sha256=dNkAIOeOG0U8_tGgaI06nAML6HtGvozlz6MvlNQqQF4,17231
8
+ _mcp_mesh/engine/mcp_client_proxy.py,sha256=eJStwy_VQJexYYD8bOh_m4Ld3Bb8Ae_dt8N1CC41qBc,17625
9
9
  _mcp_mesh/engine/self_dependency_proxy.py,sha256=OkKt0-B_ADnJlWtHiHItoZCBZ7Su0iz2unEPFfXvrs4,3302
10
10
  _mcp_mesh/engine/session_aware_client.py,sha256=mc9eh-aCvUvfllORiXTf_X8_jPqV-32QdWKlr8tHLkU,10600
11
11
  _mcp_mesh/engine/session_manager.py,sha256=MCr0_fXBaUjXM51WU5EhDkiGvBdfzYQFVNb9DCXXL0A,10418
12
12
  _mcp_mesh/engine/signature_analyzer.py,sha256=AxBuFq8_TCT2afw_XDF4AjWPqdG6ST5jfbw2BDdp9Mo,7482
13
- _mcp_mesh/engine/threading_utils.py,sha256=TAot2lLxSO_0QeAO8c0XBvZeJ6Ho6ljKtR0WINV7o7o,8244
14
- _mcp_mesh/engine/unified_mcp_proxy.py,sha256=r1nbJ_adRAbblDK8f-4AffxszJqkqtwvEwRKw-tczPA,36095
13
+ _mcp_mesh/engine/unified_mcp_proxy.py,sha256=33xm-jABTYUwr_uIB6mx4yNlNJrsvstCHJOMTdcm-pg,36365
15
14
  _mcp_mesh/generated/.openapi-generator-ignore,sha256=-d-Y-RVAZRrHw36jO0b79oDXpfA8rZdBGPCG4Vs_rUs,227
16
15
  _mcp_mesh/generated/.openapi-generator/FILES,sha256=BXFXHe4FTV5GFUTNjMhmvOrVcYVlHJc5O-3UXyp8OCY,2169
17
16
  _mcp_mesh/generated/.openapi-generator/VERSION,sha256=nMm490YXJUW3_vAdeAsg7E3yRgUqVwk5-50PuaFonM8,7
@@ -69,30 +68,31 @@ _mcp_mesh/pipeline/api_heartbeat/api_registry_connection.py,sha256=6N0JdXdnLkaXa
69
68
  _mcp_mesh/pipeline/api_startup/__init__.py,sha256=eivolkSKot2bJTWP2BV8-RKRT1Zm7SGQYuEUiTxusOQ,577
70
69
  _mcp_mesh/pipeline/api_startup/api_pipeline.py,sha256=w4m7LP7qnpLxYYWy4moa_8inAWFT0jWy9i7G9WGWOCM,2546
71
70
  _mcp_mesh/pipeline/api_startup/api_server_setup.py,sha256=Qy0wbXyIWIQYA7CjiGVZwn0nWCKK85ZzFTRI2JDA9Aw,15099
72
- _mcp_mesh/pipeline/api_startup/fastapi_discovery.py,sha256=BOo7_cBwABNpk_3Y-Lh_3ygwAOQ_254dwblPyO_vLIw,11991
71
+ _mcp_mesh/pipeline/api_startup/fastapi_discovery.py,sha256=MV3hvDXvX7r1Mrn6LAReu9W3hKt-5-jDhPpPYwZXnco,5770
73
72
  _mcp_mesh/pipeline/api_startup/middleware_integration.py,sha256=ybImXZlmIR6yA-wYg5Zy_ZMFF9YgToLkk4jnBeZJ7WY,6267
74
73
  _mcp_mesh/pipeline/api_startup/route_collection.py,sha256=UjA-F5_RbGVU5TfDT19Np5_x2PtYkNn2mGFyivDsk24,2031
75
74
  _mcp_mesh/pipeline/api_startup/route_integration.py,sha256=aMT7p7cwK8N3tZBRqeGQF8upc7tU-Exj6Dz0a4cSBhU,13441
76
75
  _mcp_mesh/pipeline/mcp_heartbeat/__init__.py,sha256=nRNjZ3VD_9bPLQuJ6Nc02gE7KSLcMP7TMquB0hP6hHs,844
77
76
  _mcp_mesh/pipeline/mcp_heartbeat/dependency_resolution.py,sha256=nHjKSM4Yyn05wnfw1dI4VGroHZy8lRkDihnZfFHeF8U,16217
78
77
  _mcp_mesh/pipeline/mcp_heartbeat/fast_heartbeat_check.py,sha256=QTzYdL81WERkOaBVOgNbFQh1ddTn70urNtyIgtFTudA,4465
79
- _mcp_mesh/pipeline/mcp_heartbeat/heartbeat_orchestrator.py,sha256=56IK5p8xlppJ5i6oMMh12fin3XwrOTcppTuVO2CnlV8,11292
78
+ _mcp_mesh/pipeline/mcp_heartbeat/heartbeat_orchestrator.py,sha256=uB9o298X7GbOaKUw4ij_fUAOCpK0n2brx_oWqWGTXFY,11296
80
79
  _mcp_mesh/pipeline/mcp_heartbeat/heartbeat_pipeline.py,sha256=Jb7EVJO13trUVO3aCSgzGqAtoc4vie5kDrYLZtOkiXg,11601
81
80
  _mcp_mesh/pipeline/mcp_heartbeat/heartbeat_send.py,sha256=ydVx-Vb_RgW1WPCboHVdZfEwNbgDngFV6Y9elZIBrAw,3602
82
- _mcp_mesh/pipeline/mcp_heartbeat/lifespan_integration.py,sha256=yCt731vgniSfxuClIcMvlMtDrubTu_9crgwiOwOYoBQ,2510
81
+ _mcp_mesh/pipeline/mcp_heartbeat/lifespan_integration.py,sha256=4XPPlaJ6rz-FkDO3bxzVxHmVF-aq1CCaTW4vIBXrB0c,3016
83
82
  _mcp_mesh/pipeline/mcp_heartbeat/registry_connection.py,sha256=4abbOKN3echwX82PV0RvxF6cJZUu0pMgisOpILZ_ZzY,2875
84
- _mcp_mesh/pipeline/mcp_startup/__init__.py,sha256=_Y25DgT9eYJ_7Qe7x1Z7y4VFUIaFEmCBS9YD9QIlo7k,1142
83
+ _mcp_mesh/pipeline/mcp_startup/__init__.py,sha256=gS0xNmVx66bkLUMw64olMsN40ZLPH3ymwlLixZ4NuTs,1239
85
84
  _mcp_mesh/pipeline/mcp_startup/configuration.py,sha256=6LRLIxrqFMU76qrBb6GjGknUlKPZZ9iqOlxE7F9ZhLs,2808
86
85
  _mcp_mesh/pipeline/mcp_startup/decorator_collection.py,sha256=RHC6MHtfP9aP0hZ-IJjISZu72e0Pml3LU0qr7dc284w,2294
87
- _mcp_mesh/pipeline/mcp_startup/fastapiserver_setup.py,sha256=pQIrEc70qeWP732S9LF5A2qWq0CZ5wK-bxls7AjX_2o,33346
86
+ _mcp_mesh/pipeline/mcp_startup/fastapiserver_setup.py,sha256=ktCn1IB8J3Iz7T0iUJF_ytwwO_RRbJN3dNQ6qZLY6iI,40229
88
87
  _mcp_mesh/pipeline/mcp_startup/fastmcpserver_discovery.py,sha256=ktsE9EZYdyZbCtCKB6HVdzGFMQ0E9n0-7I55LRO99sE,10270
89
88
  _mcp_mesh/pipeline/mcp_startup/heartbeat_loop.py,sha256=v85B0ynomvYu87eIvLe-aSZ7-Iwov2VtM4Fg3PkmrZs,3865
90
89
  _mcp_mesh/pipeline/mcp_startup/heartbeat_preparation.py,sha256=v3Fl0PvW5s7Ib_Cy7WtXA7gDvsFGiz54a-IlQRTcLPg,10410
91
- _mcp_mesh/pipeline/mcp_startup/startup_orchestrator.py,sha256=W9o_d0xTOAqK5i8Xd7BJgiUJou1uPZwsygslM3I9HYo,25149
92
- _mcp_mesh/pipeline/mcp_startup/startup_pipeline.py,sha256=E-kmmfkdtRMhpEAF-_qFLAdaY3sLo165SUHjlRXwqC4,2069
90
+ _mcp_mesh/pipeline/mcp_startup/server_discovery.py,sha256=i3t12Dd2nEg3nbifyMGvm2SYr3WYiYJbicBakS3ZeuM,8007
91
+ _mcp_mesh/pipeline/mcp_startup/startup_orchestrator.py,sha256=KldSG3xfGNm0iexnCMPkDsi3nuIVXBwneDBuoT5gJO4,26756
92
+ _mcp_mesh/pipeline/mcp_startup/startup_pipeline.py,sha256=Y_VeZcvyT3y9phWtGD7cX92NzKZIzF2J6kRJUO8q-9U,2291
93
93
  _mcp_mesh/pipeline/shared/__init__.py,sha256=s9xmdf6LkoetrVRGd7Zp3NUxcJCW6YZ_yNKzUBcnYys,352
94
94
  _mcp_mesh/pipeline/shared/base_step.py,sha256=kyPbNUX79NyGrE_0Q-e-Aek7m1J0TW036njWfv0iZ0I,1080
95
- _mcp_mesh/pipeline/shared/mesh_pipeline.py,sha256=wTODlMb7ByhzsT3DnM0lzY3gS9fKLDUxL0yDEozCVeo,6494
95
+ _mcp_mesh/pipeline/shared/mesh_pipeline.py,sha256=UlQBrPWqbruFiUdVYgFKgPOpp_sMVsO97nZsWX90k9U,6498
96
96
  _mcp_mesh/pipeline/shared/pipeline_types.py,sha256=iKSgzCoYu3bpIwLViw6lE7T7irEzOm7gpie29lyy7SQ,1504
97
97
  _mcp_mesh/pipeline/shared/registry_connection.py,sha256=jmlgULixRM1NMNyMc-8SJN3RpCtV9hUf76vn9nciAN4,2904
98
98
  _mcp_mesh/shared/__init__.py,sha256=L0detgEWjnc_XfxA_5vIBcaGTuLcT6AARKkUpUBIaH4,1116
@@ -104,6 +104,8 @@ _mcp_mesh/shared/fastapi_middleware_manager.py,sha256=_h10dSL9mgQstpJW_ZM2cpkU6y
104
104
  _mcp_mesh/shared/host_resolver.py,sha256=ycs6gXnI1zJX5KiqiLJPX5GkHX8r4j8NMHQOlG2J2X8,2964
105
105
  _mcp_mesh/shared/logging_config.py,sha256=n9AqShZ5BZgyrkoTlvux6ECRVpM9dUYvmGB0NPMl-Ak,2477
106
106
  _mcp_mesh/shared/registry_client_wrapper.py,sha256=d8yL-MiCrQr_WYdRFStOd531qaLv9kZjh0zJAmCJ-Cc,16976
107
+ _mcp_mesh/shared/server_discovery.py,sha256=W5nsN-GvEVFD-7XkbMTxh-9FUIEiyWOxP3GYr8GNi3E,13142
108
+ _mcp_mesh/shared/simple_shutdown.py,sha256=jnF1rTR2yR619LZnEjNlu-ZdKlB3PovxKqG0VZ3HDgE,8319
107
109
  _mcp_mesh/shared/sse_parser.py,sha256=OEPnfL9xL3rsjQrbyvfUO82WljPSDeO6Z61uUwN1NAo,8035
108
110
  _mcp_mesh/shared/support_types.py,sha256=k-ICF_UwDkHxQ1D5LwFZrp-UrNb4E5dzw02CRuLW9iI,7264
109
111
  _mcp_mesh/tracing/agent_context_helper.py,sha256=BIJ3Kc4Znd6emMAu97aUhSoxSIza3qYUmObLgc9ONiA,4765
@@ -114,9 +116,9 @@ _mcp_mesh/tracing/redis_metadata_publisher.py,sha256=F78E34qnI3D0tOmbHUTBsLbDst2
114
116
  _mcp_mesh/tracing/trace_context_helper.py,sha256=6tEkwjWFqMBe45zBlhacktmIpzJWTF950ph3bwL3cNc,5994
115
117
  _mcp_mesh/tracing/utils.py,sha256=t9lJuTH7CeuzAiiAaD0WxsJMFJPdzZFR0w6-vyR9f2E,3849
116
118
  mesh/__init__.py,sha256=l5RSMV8Kx0h7cvku8YkZPbTHjEPWciGT0bcEB2O_eNU,3242
117
- mesh/decorators.py,sha256=Ncpov2Up8EW_3n6_uMvKN613BqVWkdfWesd3SoufGDs,25673
119
+ mesh/decorators.py,sha256=oBWoRE-FA3qUGygAUtk3-eAYBckwTGfTzvXOgCag4ys,36678
118
120
  mesh/types.py,sha256=g37DXAzya-xGPa1_WKlW3T3_VqyTn8ZVepIDSrhBTkc,10815
119
- mcp_mesh-0.5.4.dist-info/METADATA,sha256=rAqRHewsG0BBTs2tMiwg1khm3XIPIHb3-X8YgzC5BRE,4879
120
- mcp_mesh-0.5.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
121
- mcp_mesh-0.5.4.dist-info/licenses/LICENSE,sha256=_EBQHRQThv9FPOLc5eFOUdeeRO0mYwChC7cx60dM1tM,1078
122
- mcp_mesh-0.5.4.dist-info/RECORD,,
121
+ mcp_mesh-0.5.6.dist-info/METADATA,sha256=Mh8nwL5wjm53WMFPOqcVHCRmcvbPa4sgMmX7bQV-yiw,4879
122
+ mcp_mesh-0.5.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
123
+ mcp_mesh-0.5.6.dist-info/licenses/LICENSE,sha256=_EBQHRQThv9FPOLc5eFOUdeeRO0mYwChC7cx60dM1tM,1078
124
+ mcp_mesh-0.5.6.dist-info/RECORD,,