mcp-mesh 0.7.21__py3-none-any.whl → 0.8.0b1__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.
Files changed (121) hide show
  1. _mcp_mesh/__init__.py +1 -1
  2. _mcp_mesh/engine/dependency_injector.py +4 -6
  3. _mcp_mesh/engine/http_wrapper.py +69 -10
  4. _mcp_mesh/engine/mesh_llm_agent.py +4 -7
  5. _mcp_mesh/engine/mesh_llm_agent_injector.py +2 -1
  6. _mcp_mesh/engine/provider_handlers/__init__.py +14 -1
  7. _mcp_mesh/engine/provider_handlers/base_provider_handler.py +114 -8
  8. _mcp_mesh/engine/provider_handlers/claude_handler.py +15 -57
  9. _mcp_mesh/engine/provider_handlers/gemini_handler.py +181 -0
  10. _mcp_mesh/engine/provider_handlers/openai_handler.py +8 -63
  11. _mcp_mesh/engine/provider_handlers/provider_handler_registry.py +16 -10
  12. _mcp_mesh/engine/response_parser.py +61 -15
  13. _mcp_mesh/engine/unified_mcp_proxy.py +18 -34
  14. _mcp_mesh/pipeline/__init__.py +9 -20
  15. _mcp_mesh/pipeline/api_heartbeat/__init__.py +12 -7
  16. _mcp_mesh/pipeline/api_heartbeat/api_lifespan_integration.py +23 -49
  17. _mcp_mesh/pipeline/api_heartbeat/rust_api_heartbeat.py +425 -0
  18. _mcp_mesh/pipeline/api_startup/api_pipeline.py +7 -9
  19. _mcp_mesh/pipeline/api_startup/api_server_setup.py +91 -70
  20. _mcp_mesh/pipeline/api_startup/fastapi_discovery.py +22 -23
  21. _mcp_mesh/pipeline/api_startup/middleware_integration.py +32 -24
  22. _mcp_mesh/pipeline/api_startup/route_collection.py +2 -4
  23. _mcp_mesh/pipeline/mcp_heartbeat/__init__.py +5 -17
  24. _mcp_mesh/pipeline/mcp_heartbeat/rust_heartbeat.py +695 -0
  25. _mcp_mesh/pipeline/mcp_startup/__init__.py +2 -5
  26. _mcp_mesh/pipeline/mcp_startup/configuration.py +1 -1
  27. _mcp_mesh/pipeline/mcp_startup/fastapiserver_setup.py +5 -6
  28. _mcp_mesh/pipeline/mcp_startup/heartbeat_loop.py +6 -7
  29. _mcp_mesh/pipeline/mcp_startup/startup_orchestrator.py +21 -9
  30. _mcp_mesh/pipeline/mcp_startup/startup_pipeline.py +3 -8
  31. _mcp_mesh/pipeline/shared/mesh_pipeline.py +0 -2
  32. _mcp_mesh/reload.py +1 -3
  33. _mcp_mesh/shared/__init__.py +2 -8
  34. _mcp_mesh/shared/config_resolver.py +124 -80
  35. _mcp_mesh/shared/defaults.py +89 -14
  36. _mcp_mesh/shared/fastapi_middleware_manager.py +149 -91
  37. _mcp_mesh/shared/host_resolver.py +8 -46
  38. _mcp_mesh/shared/server_discovery.py +115 -86
  39. _mcp_mesh/shared/simple_shutdown.py +44 -86
  40. _mcp_mesh/tracing/execution_tracer.py +2 -6
  41. _mcp_mesh/tracing/redis_metadata_publisher.py +24 -79
  42. _mcp_mesh/tracing/trace_context_helper.py +3 -13
  43. _mcp_mesh/tracing/utils.py +29 -15
  44. _mcp_mesh/utils/fastmcp_schema_extractor.py +2 -1
  45. {mcp_mesh-0.7.21.dist-info → mcp_mesh-0.8.0b1.dist-info}/METADATA +2 -1
  46. mcp_mesh-0.8.0b1.dist-info/RECORD +85 -0
  47. mesh/__init__.py +2 -1
  48. mesh/decorators.py +89 -5
  49. _mcp_mesh/generated/.openapi-generator/FILES +0 -50
  50. _mcp_mesh/generated/.openapi-generator/VERSION +0 -1
  51. _mcp_mesh/generated/.openapi-generator-ignore +0 -15
  52. _mcp_mesh/generated/mcp_mesh_registry_client/__init__.py +0 -90
  53. _mcp_mesh/generated/mcp_mesh_registry_client/api/__init__.py +0 -6
  54. _mcp_mesh/generated/mcp_mesh_registry_client/api/agents_api.py +0 -1088
  55. _mcp_mesh/generated/mcp_mesh_registry_client/api/health_api.py +0 -764
  56. _mcp_mesh/generated/mcp_mesh_registry_client/api/tracing_api.py +0 -303
  57. _mcp_mesh/generated/mcp_mesh_registry_client/api_client.py +0 -798
  58. _mcp_mesh/generated/mcp_mesh_registry_client/api_response.py +0 -21
  59. _mcp_mesh/generated/mcp_mesh_registry_client/configuration.py +0 -577
  60. _mcp_mesh/generated/mcp_mesh_registry_client/exceptions.py +0 -217
  61. _mcp_mesh/generated/mcp_mesh_registry_client/models/__init__.py +0 -55
  62. _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_info.py +0 -158
  63. _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_metadata.py +0 -126
  64. _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_metadata_dependencies_inner.py +0 -139
  65. _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_metadata_dependencies_inner_one_of.py +0 -92
  66. _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_registration.py +0 -103
  67. _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_registration_metadata.py +0 -136
  68. _mcp_mesh/generated/mcp_mesh_registry_client/models/agents_list_response.py +0 -100
  69. _mcp_mesh/generated/mcp_mesh_registry_client/models/capability_info.py +0 -107
  70. _mcp_mesh/generated/mcp_mesh_registry_client/models/decorator_agent_metadata.py +0 -112
  71. _mcp_mesh/generated/mcp_mesh_registry_client/models/decorator_agent_request.py +0 -103
  72. _mcp_mesh/generated/mcp_mesh_registry_client/models/decorator_info.py +0 -105
  73. _mcp_mesh/generated/mcp_mesh_registry_client/models/dependency_info.py +0 -103
  74. _mcp_mesh/generated/mcp_mesh_registry_client/models/dependency_resolution_info.py +0 -106
  75. _mcp_mesh/generated/mcp_mesh_registry_client/models/error_response.py +0 -91
  76. _mcp_mesh/generated/mcp_mesh_registry_client/models/health_response.py +0 -103
  77. _mcp_mesh/generated/mcp_mesh_registry_client/models/heartbeat_request.py +0 -101
  78. _mcp_mesh/generated/mcp_mesh_registry_client/models/heartbeat_request_metadata.py +0 -111
  79. _mcp_mesh/generated/mcp_mesh_registry_client/models/heartbeat_response.py +0 -117
  80. _mcp_mesh/generated/mcp_mesh_registry_client/models/llm_provider.py +0 -93
  81. _mcp_mesh/generated/mcp_mesh_registry_client/models/llm_provider_resolution_info.py +0 -106
  82. _mcp_mesh/generated/mcp_mesh_registry_client/models/llm_tool_filter.py +0 -109
  83. _mcp_mesh/generated/mcp_mesh_registry_client/models/llm_tool_filter_filter_inner.py +0 -139
  84. _mcp_mesh/generated/mcp_mesh_registry_client/models/llm_tool_filter_filter_inner_one_of.py +0 -91
  85. _mcp_mesh/generated/mcp_mesh_registry_client/models/llm_tool_info.py +0 -101
  86. _mcp_mesh/generated/mcp_mesh_registry_client/models/llm_tool_resolution_info.py +0 -120
  87. _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_agent_register_metadata.py +0 -112
  88. _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_agent_registration.py +0 -129
  89. _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_registration_response.py +0 -153
  90. _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_registration_response_dependencies_resolved_value_inner.py +0 -101
  91. _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_tool_dependency_registration.py +0 -93
  92. _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_tool_register_metadata.py +0 -107
  93. _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_tool_registration.py +0 -117
  94. _mcp_mesh/generated/mcp_mesh_registry_client/models/registration_response.py +0 -119
  95. _mcp_mesh/generated/mcp_mesh_registry_client/models/resolved_llm_provider.py +0 -110
  96. _mcp_mesh/generated/mcp_mesh_registry_client/models/rich_dependency.py +0 -93
  97. _mcp_mesh/generated/mcp_mesh_registry_client/models/root_response.py +0 -92
  98. _mcp_mesh/generated/mcp_mesh_registry_client/models/standardized_dependency.py +0 -93
  99. _mcp_mesh/generated/mcp_mesh_registry_client/models/trace_event.py +0 -106
  100. _mcp_mesh/generated/mcp_mesh_registry_client/py.typed +0 -0
  101. _mcp_mesh/generated/mcp_mesh_registry_client/rest.py +0 -259
  102. _mcp_mesh/pipeline/api_heartbeat/api_dependency_resolution.py +0 -418
  103. _mcp_mesh/pipeline/api_heartbeat/api_fast_heartbeat_check.py +0 -117
  104. _mcp_mesh/pipeline/api_heartbeat/api_health_check.py +0 -140
  105. _mcp_mesh/pipeline/api_heartbeat/api_heartbeat_orchestrator.py +0 -243
  106. _mcp_mesh/pipeline/api_heartbeat/api_heartbeat_pipeline.py +0 -311
  107. _mcp_mesh/pipeline/api_heartbeat/api_heartbeat_send.py +0 -386
  108. _mcp_mesh/pipeline/api_heartbeat/api_registry_connection.py +0 -104
  109. _mcp_mesh/pipeline/mcp_heartbeat/dependency_resolution.py +0 -396
  110. _mcp_mesh/pipeline/mcp_heartbeat/fast_heartbeat_check.py +0 -116
  111. _mcp_mesh/pipeline/mcp_heartbeat/heartbeat_orchestrator.py +0 -311
  112. _mcp_mesh/pipeline/mcp_heartbeat/heartbeat_pipeline.py +0 -282
  113. _mcp_mesh/pipeline/mcp_heartbeat/heartbeat_send.py +0 -98
  114. _mcp_mesh/pipeline/mcp_heartbeat/lifespan_integration.py +0 -84
  115. _mcp_mesh/pipeline/mcp_heartbeat/llm_tools_resolution.py +0 -264
  116. _mcp_mesh/pipeline/mcp_heartbeat/registry_connection.py +0 -79
  117. _mcp_mesh/pipeline/shared/registry_connection.py +0 -80
  118. _mcp_mesh/shared/registry_client_wrapper.py +0 -515
  119. mcp_mesh-0.7.21.dist-info/RECORD +0 -152
  120. {mcp_mesh-0.7.21.dist-info → mcp_mesh-0.8.0b1.dist-info}/WHEEL +0 -0
  121. {mcp_mesh-0.7.21.dist-info → mcp_mesh-0.8.0b1.dist-info}/licenses/LICENSE +0 -0
@@ -8,15 +8,19 @@ robust app discovery, and graceful error handling.
8
8
 
9
9
  import gc
10
10
  import logging
11
+ import threading
11
12
  import time
12
13
  from typing import List, Optional, Set
13
14
 
14
15
  logger = logging.getLogger(__name__)
15
16
 
17
+ # Lock for thread-safe singleton initialization
18
+ _middleware_manager_lock = threading.Lock()
19
+
16
20
 
17
21
  class FastAPIMiddlewareManager:
18
22
  """Enhanced FastAPI middleware injection manager.
19
-
23
+
20
24
  Provides robust middleware injection capabilities with:
21
25
  - Multiple app discovery methods
22
26
  - App state detection and validation
@@ -24,60 +28,72 @@ class FastAPIMiddlewareManager:
24
28
  - Graceful error handling
25
29
  - Monkey-patch FastAPI creation for immediate injection
26
30
  """
27
-
31
+
28
32
  def __init__(self):
29
33
  self._processed_apps: Set[int] = set() # Track processed apps by id()
30
34
  self._monkey_patch_applied = False
31
35
  self._pending_middleware_needed = False # Flag that middleware is needed
32
-
36
+
33
37
  def enable_middleware_injection(self) -> bool:
34
38
  """
35
39
  Enable middleware injection via monkey-patching FastAPI creation.
36
-
40
+
37
41
  This sets up automatic middleware injection when FastAPI apps are created,
38
42
  eliminating timing issues with app startup.
39
-
43
+
40
44
  Returns:
41
45
  bool: True if monkey-patch was applied successfully
42
46
  """
43
47
  if self._monkey_patch_applied:
44
48
  logger.debug("🔍 TRACING: Monkey-patch already applied")
45
49
  return True
46
-
50
+
47
51
  try:
48
52
  from fastapi import FastAPI
49
-
53
+
50
54
  # Store original FastAPI.__init__
51
- if not hasattr(FastAPI, '_original_init'):
55
+ if not hasattr(FastAPI, "_original_init"):
52
56
  FastAPI._original_init = FastAPI.__init__
53
-
57
+
54
58
  # Create enhanced __init__ that adds middleware immediately
55
59
  def enhanced_fastapi_init(self, *args, **kwargs):
56
60
  # Call original FastAPI initialization
57
61
  result = FastAPI._original_init(self, *args, **kwargs)
58
-
62
+
59
63
  # Immediately add middleware to this newly created app
60
64
  try:
61
65
  manager = get_fastapi_middleware_manager()
62
66
  if manager._pending_middleware_needed:
63
67
  success = manager.add_middleware_to_specific_app(self)
64
68
  if success:
65
- logger.debug(f"🔍 TRACING: Auto-injected middleware to FastAPI app '{getattr(self, 'title', 'Unknown')}' during creation")
69
+ logger.debug(
70
+ f"🔍 TRACING: Auto-injected middleware to FastAPI app '{getattr(self, 'title', 'Unknown')}' during creation"
71
+ )
66
72
  else:
67
- logger.debug(f"🔍 TRACING: Failed to auto-inject middleware during app creation")
73
+ logger.debug(
74
+ f"🔍 TRACING: Failed to auto-inject middleware during app creation"
75
+ )
68
76
  except Exception as e:
69
77
  # Never break FastAPI app creation
70
- logger.debug(f"🔍 TRACING: Auto-injection failed during app creation: {e}")
71
-
78
+ logger.debug(
79
+ f"🔍 TRACING: Auto-injection failed during app creation: {e}"
80
+ )
81
+
72
82
  return result
73
-
83
+
74
84
  # Apply the monkey-patch
75
85
  FastAPI.__init__ = enhanced_fastapi_init
76
86
  self._monkey_patch_applied = True
77
-
78
- logger.debug("🔍 TRACING: Successfully applied FastAPI creation monkey-patch")
87
+
88
+ # Ensure manager is initialized before any app instances are created
89
+ # This guarantees the closure in enhanced_fastapi_init can rely on a ready manager
90
+ get_fastapi_middleware_manager()
91
+
92
+ logger.debug(
93
+ "🔍 TRACING: Successfully applied FastAPI creation monkey-patch"
94
+ )
79
95
  return True
80
-
96
+
81
97
  except Exception as e:
82
98
  logger.debug(f"🔍 TRACING: Failed to apply FastAPI monkey-patch: {e}")
83
99
  return False
@@ -85,89 +101,95 @@ class FastAPIMiddlewareManager:
85
101
  def request_middleware_injection(self) -> bool:
86
102
  """
87
103
  Request that middleware be injected into FastAPI apps.
88
-
104
+
89
105
  This method should be called from @mesh.route decorators to signal
90
106
  that middleware injection is needed.
91
-
107
+
92
108
  Returns:
93
109
  bool: True if injection was set up successfully
94
110
  """
95
111
  self._pending_middleware_needed = True
96
-
112
+
97
113
  # Try immediate discovery first (for apps that already exist)
98
114
  immediate_success = self.add_tracing_middleware_to_discovered_apps()
99
-
115
+
100
116
  # Also enable monkey-patch for future app creation
101
117
  monkey_patch_success = self.enable_middleware_injection()
102
-
103
- logger.debug(f"🔍 TRACING: Middleware injection requested - immediate: {immediate_success}, monkey-patch: {monkey_patch_success}")
118
+
119
+ logger.debug(
120
+ f"🔍 TRACING: Middleware injection requested - immediate: {immediate_success}, monkey-patch: {monkey_patch_success}"
121
+ )
104
122
  return immediate_success or monkey_patch_success
105
123
 
106
124
  def add_tracing_middleware_to_discovered_apps(self) -> bool:
107
125
  """
108
126
  Add tracing middleware to all discovered FastAPI apps.
109
-
127
+
110
128
  Returns:
111
129
  bool: True if any middleware was successfully added, False otherwise
112
130
  """
113
- logger.debug("🔍 TRACING: Starting enhanced middleware injection for FastAPI apps...")
114
-
131
+ logger.debug(
132
+ "🔍 TRACING: Starting enhanced middleware injection for FastAPI apps..."
133
+ )
134
+
115
135
  apps = self._discover_fastapi_apps()
116
136
  if not apps:
117
137
  logger.debug("🔍 TRACING: No FastAPI apps discovered")
118
138
  return False
119
-
139
+
120
140
  success_count = 0
121
141
  for app in apps:
122
142
  if self._add_middleware_to_app_with_retry(app):
123
143
  success_count += 1
124
-
125
- logger.debug(f"🔍 TRACING: Enhanced middleware injection completed - {success_count}/{len(apps)} apps processed")
144
+
145
+ logger.debug(
146
+ f"🔍 TRACING: Enhanced middleware injection completed - {success_count}/{len(apps)} apps processed"
147
+ )
126
148
  return success_count > 0
127
-
149
+
128
150
  def add_middleware_to_specific_app(self, app) -> bool:
129
151
  """
130
152
  Add middleware to a specific FastAPI app.
131
-
153
+
132
154
  Args:
133
155
  app: FastAPI application instance
134
-
156
+
135
157
  Returns:
136
158
  bool: True if middleware was successfully added
137
159
  """
138
160
  return self._add_middleware_to_app_with_retry(app)
139
-
161
+
140
162
  def _discover_fastapi_apps(self) -> List:
141
163
  """
142
164
  Discover FastAPI apps using multiple methods.
143
-
165
+
144
166
  Returns:
145
167
  List of FastAPI app instances
146
168
  """
147
169
  apps = []
148
-
170
+
149
171
  # Try to import FastAPI
150
172
  try:
151
173
  from fastapi import FastAPI
152
174
  except ImportError:
153
175
  logger.debug("🔍 TRACING: FastAPI not available")
154
176
  return apps
155
-
177
+
156
178
  # Method 1: Garbage collector discovery (current approach)
157
179
  gc_apps = self._discover_apps_via_gc(FastAPI)
158
180
  logger.debug(f"🔍 TRACING: GC discovery found {len(gc_apps)} apps")
159
181
  apps.extend(gc_apps)
160
-
182
+
161
183
  # Method 2: Module globals discovery
162
184
  module_apps = self._discover_apps_via_modules(FastAPI)
163
185
  logger.debug(f"🔍 TRACING: Module discovery found {len(module_apps)} apps")
164
186
  apps.extend(module_apps)
165
-
187
+
166
188
  # Method 3: Stack frame inspection (new)
167
189
  stack_apps = self._discover_apps_via_stack(FastAPI)
168
190
  logger.debug(f"🔍 TRACING: Stack discovery found {len(stack_apps)} apps")
169
191
  apps.extend(stack_apps)
170
-
192
+
171
193
  # Remove duplicates while preserving order
172
194
  unique_apps = []
173
195
  seen_ids = set()
@@ -176,10 +198,10 @@ class FastAPIMiddlewareManager:
176
198
  if app_id not in seen_ids:
177
199
  unique_apps.append(app)
178
200
  seen_ids.add(app_id)
179
-
201
+
180
202
  logger.debug(f"🔍 TRACING: Discovered {len(unique_apps)} unique FastAPI apps")
181
203
  return unique_apps
182
-
204
+
183
205
  def _discover_apps_via_gc(self, FastAPI) -> List:
184
206
  """Discover FastAPI apps via garbage collector."""
185
207
  apps = []
@@ -190,28 +212,31 @@ class FastAPIMiddlewareManager:
190
212
  except Exception as e:
191
213
  logger.debug(f"🔍 TRACING: GC discovery failed: {e}")
192
214
  return apps
193
-
215
+
194
216
  def _discover_apps_via_modules(self, FastAPI) -> List:
195
217
  """Discover FastAPI apps via module globals."""
196
218
  apps = []
197
219
  try:
198
220
  import sys
221
+
199
222
  for module_name, module in sys.modules.items():
200
- if module and hasattr(module, '__dict__'):
223
+ if module and hasattr(module, "__dict__"):
201
224
  for attr_name, attr_value in module.__dict__.items():
202
225
  if isinstance(attr_value, FastAPI):
203
- logger.debug(f"🔍 TRACING: Found FastAPI app '{attr_name}' in module '{module_name}'")
226
+ logger.debug(
227
+ f"🔍 TRACING: Found FastAPI app '{attr_name}' in module '{module_name}'"
228
+ )
204
229
  apps.append(attr_value)
205
230
  except Exception as e:
206
231
  logger.debug(f"🔍 TRACING: Module discovery failed: {e}")
207
232
  return apps
208
-
233
+
209
234
  def _discover_apps_via_stack(self, FastAPI) -> List:
210
235
  """Discover FastAPI apps via stack frame inspection."""
211
236
  apps = []
212
237
  try:
213
238
  import inspect
214
-
239
+
215
240
  # Look through stack frames for 'app' variables
216
241
  for frame_info in inspect.stack():
217
242
  frame = frame_info.frame
@@ -220,7 +245,9 @@ class FastAPIMiddlewareManager:
220
245
  for var_dict in [frame.f_locals, frame.f_globals]:
221
246
  for var_name, var_value in var_dict.items():
222
247
  if isinstance(var_value, FastAPI):
223
- logger.debug(f"🔍 TRACING: Found FastAPI app '{var_name}' in stack frame")
248
+ logger.debug(
249
+ f"🔍 TRACING: Found FastAPI app '{var_name}' in stack frame"
250
+ )
224
251
  apps.append(var_value)
225
252
  finally:
226
253
  # Avoid reference cycles
@@ -228,128 +255,147 @@ class FastAPIMiddlewareManager:
228
255
  except Exception as e:
229
256
  logger.debug(f"🔍 TRACING: Stack discovery failed: {e}")
230
257
  return apps
231
-
258
+
232
259
  def _add_middleware_to_app_with_retry(self, app) -> bool:
233
260
  """
234
261
  Add middleware to a single app with retry logic.
235
-
262
+
236
263
  Args:
237
264
  app: FastAPI application instance
238
-
265
+
239
266
  Returns:
240
267
  bool: True if middleware was successfully added
241
268
  """
242
269
  app_id = id(app)
243
- app_title = getattr(app, 'title', 'Unknown FastAPI App')
244
-
270
+ app_title = getattr(app, "title", "Unknown FastAPI App")
271
+
245
272
  # Skip if already processed
246
273
  if app_id in self._processed_apps:
247
274
  logger.debug(f"🔍 TRACING: App '{app_title}' already processed, skipping")
248
275
  return False
249
-
276
+
250
277
  logger.debug(f"🔍 TRACING: Processing app '{app_title}' (app_{app_id})")
251
-
278
+
252
279
  # Check if middleware already exists
253
280
  if self._has_tracing_middleware(app):
254
- logger.debug(f"🔍 TRACING: App '{app_title}' already has tracing middleware")
281
+ logger.debug(
282
+ f"🔍 TRACING: App '{app_title}' already has tracing middleware"
283
+ )
255
284
  self._processed_apps.add(app_id)
256
285
  return False
257
-
286
+
258
287
  # Check if app can accept middleware
259
288
  if not self._can_add_middleware(app):
260
- logger.debug(f"🔍 TRACING: App '{app_title}' cannot accept middleware (already started)")
289
+ logger.debug(
290
+ f"🔍 TRACING: App '{app_title}' cannot accept middleware (already started)"
291
+ )
261
292
  return False
262
-
293
+
263
294
  # Attempt to add middleware with retry logic
264
295
  for attempt in range(3):
265
296
  try:
266
297
  self._add_middleware_to_app(app)
267
- logger.debug(f"🔍 TRACING: Successfully added middleware to '{app_title}' on attempt {attempt + 1}")
298
+ logger.debug(
299
+ f"🔍 TRACING: Successfully added middleware to '{app_title}' on attempt {attempt + 1}"
300
+ )
268
301
  self._processed_apps.add(app_id)
269
302
  return True
270
-
303
+
271
304
  except Exception as e:
272
305
  error_msg = str(e)
273
- if "Cannot add middleware after an application has started" in error_msg:
306
+ if (
307
+ "Cannot add middleware after an application has started"
308
+ in error_msg
309
+ ):
274
310
  if attempt < 2:
275
- logger.debug(f"🔍 TRACING: App startup timing issue for '{app_title}', retrying in 50ms...")
311
+ logger.debug(
312
+ f"🔍 TRACING: App startup timing issue for '{app_title}', retrying in 50ms..."
313
+ )
276
314
  time.sleep(0.05) # Brief delay
277
315
  continue
278
316
  else:
279
- logger.debug(f"🔍 TRACING: App '{app_title}' already started after {attempt + 1} attempts")
317
+ logger.debug(
318
+ f"🔍 TRACING: App '{app_title}' already started after {attempt + 1} attempts"
319
+ )
280
320
  return False
281
321
  else:
282
- logger.debug(f"🔍 TRACING: Failed to add middleware to '{app_title}': {e}")
322
+ logger.debug(
323
+ f"🔍 TRACING: Failed to add middleware to '{app_title}': {e}"
324
+ )
283
325
  return False
284
-
326
+
285
327
  return False
286
-
328
+
287
329
  def _can_add_middleware(self, app) -> bool:
288
330
  """
289
331
  Check if middleware can be added to the app.
290
-
332
+
291
333
  Args:
292
334
  app: FastAPI application instance
293
-
335
+
294
336
  Returns:
295
337
  bool: True if middleware can be added
296
338
  """
297
339
  try:
298
340
  # Check for obvious signs the app has started
299
- if hasattr(app, '_server') and app._server is not None:
341
+ if hasattr(app, "_server") and app._server is not None:
300
342
  return False
301
-
343
+
302
344
  # Check app state
303
- if hasattr(app, 'state') and hasattr(app.state, 'started'):
345
+ if hasattr(app, "state") and hasattr(app.state, "started"):
304
346
  if app.state.started:
305
347
  return False
306
-
348
+
307
349
  # Try a harmless test - check if we can access middleware list
308
- if hasattr(app, 'user_middleware'):
350
+ if hasattr(app, "user_middleware"):
309
351
  # If we can access this without error, app is likely still configurable
310
352
  return True
311
-
353
+
312
354
  return True
313
-
355
+
314
356
  except Exception as e:
315
357
  logger.debug(f"🔍 TRACING: Middleware capability check failed: {e}")
316
358
  return False
317
-
359
+
318
360
  def _has_tracing_middleware(self, app) -> bool:
319
361
  """
320
362
  Check if the app already has our tracing middleware.
321
-
363
+
322
364
  Args:
323
365
  app: FastAPI application instance
324
-
366
+
325
367
  Returns:
326
368
  bool: True if tracing middleware is already present
327
369
  """
328
370
  try:
329
- if hasattr(app, 'user_middleware'):
371
+ if hasattr(app, "user_middleware"):
330
372
  for middleware in app.user_middleware:
331
- if hasattr(middleware, 'cls'):
373
+ if hasattr(middleware, "cls"):
332
374
  # Check for both old and new middleware names
333
375
  middleware_name = middleware.cls.__name__
334
- if middleware_name in ('MCPMeshTracingMiddleware', 'FastAPITracingMiddleware'):
376
+ if middleware_name in (
377
+ "MCPMeshTracingMiddleware",
378
+ "FastAPITracingMiddleware",
379
+ ):
335
380
  return True
336
381
  return False
337
382
  except Exception as e:
338
383
  logger.debug(f"🔍 TRACING: Middleware detection failed: {e}")
339
384
  return False
340
-
385
+
341
386
  def _add_middleware_to_app(self, app):
342
387
  """Add dedicated FastAPI tracing middleware to a single FastAPI app."""
343
- from ..tracing.fastapi_tracing_middleware import FastAPITracingMiddleware
344
-
388
+ from ..tracing.fastapi_tracing_middleware import \
389
+ FastAPITracingMiddleware
390
+
345
391
  # Add the dedicated FastAPI tracing middleware
346
392
  app.add_middleware(FastAPITracingMiddleware, logger_instance=logger)
347
393
  logger.debug(f"🔍 TRACING: Added dedicated FastAPI tracing middleware to app")
348
-
394
+
349
395
  def get_stats(self) -> dict:
350
396
  """
351
397
  Get statistics about processed apps.
352
-
398
+
353
399
  Returns:
354
400
  dict: Statistics about middleware injection
355
401
  """
@@ -364,8 +410,20 @@ _middleware_manager: Optional[FastAPIMiddlewareManager] = None
364
410
 
365
411
 
366
412
  def get_fastapi_middleware_manager() -> FastAPIMiddlewareManager:
367
- """Get or create global FastAPI middleware manager instance."""
413
+ """Get or create global FastAPI middleware manager instance.
414
+
415
+ Uses double-checked locking for thread-safe singleton initialization.
416
+ """
368
417
  global _middleware_manager
369
- if _middleware_manager is None:
370
- _middleware_manager = FastAPIMiddlewareManager()
371
- return _middleware_manager
418
+
419
+ # First check without lock (fast path)
420
+ if _middleware_manager is not None:
421
+ return _middleware_manager
422
+
423
+ # Acquire lock for initialization
424
+ with _middleware_manager_lock:
425
+ # Double-check after acquiring lock
426
+ if _middleware_manager is None:
427
+ _middleware_manager = FastAPIMiddlewareManager()
428
+
429
+ return _middleware_manager
@@ -6,8 +6,8 @@ Provides clean, testable logic for determining hostnames for different purposes:
6
6
  """
7
7
 
8
8
  import logging
9
- import os
10
- import socket
9
+
10
+ import mcp_mesh_core
11
11
 
12
12
  logger = logging.getLogger(__name__)
13
13
 
@@ -20,8 +20,9 @@ class HostResolver:
20
20
  """Get external hostname for registry advertisement.
21
21
 
22
22
  This is what other agents will use to connect to this agent.
23
+ Uses Rust core for consistent config resolution across all SDKs.
23
24
 
24
- Priority order:
25
+ Priority order (handled by Rust core):
25
26
  1. MCP_MESH_HTTP_HOST (explicit override - for production K8s deployments)
26
27
  2. Auto-detection (socket-based external IP - for development/testing)
27
28
  3. localhost (fallback)
@@ -29,23 +30,10 @@ class HostResolver:
29
30
  Returns:
30
31
  str: External hostname for registry advertisement
31
32
  """
32
- # Priority 1: Explicit override for production deployments
33
- explicit_host = os.getenv("MCP_MESH_HTTP_HOST")
34
- if explicit_host:
35
- logger.debug(f"Using explicit external host: {explicit_host}")
36
- return explicit_host
37
-
38
- # Priority 2: Auto-detection for development/testing
39
- try:
40
- auto_detected = HostResolver._auto_detect_external_ip()
41
- logger.debug(f"Auto-detected external host: {auto_detected}")
42
- return auto_detected
43
- except Exception as e:
44
- logger.warning(f"Failed to auto-detect external IP: {e}")
45
-
46
- # Priority 3: Fallback
47
- logger.debug("Using fallback external host: localhost")
48
- return "localhost"
33
+ # Rust core handles: ENV > auto-detect > localhost
34
+ host = mcp_mesh_core.resolve_config_py("http_host", None)
35
+ logger.debug(f"Resolved external host via Rust core: {host}")
36
+ return host
49
37
 
50
38
  @staticmethod
51
39
  def get_binding_host() -> str:
@@ -58,29 +46,3 @@ class HostResolver:
58
46
  str: Always "0.0.0.0" for binding to all interfaces
59
47
  """
60
48
  return "0.0.0.0"
61
-
62
- @staticmethod
63
- def _auto_detect_external_ip() -> str:
64
- """Auto-detect external IP by connecting to a public DNS server.
65
-
66
- This determines what IP address would be used for outbound connections,
67
- which is typically the correct IP for other services to connect back to.
68
-
69
- Returns:
70
- str: Auto-detected external IP address
71
-
72
- Raises:
73
- Exception: If auto-detection fails
74
- """
75
- with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
76
- # Connect to a public DNS server to determine our outbound IP
77
- s.connect(("8.8.8.8", 80))
78
- local_ip = s.getsockname()[0]
79
-
80
- # Validate the IP isn't localhost
81
- if local_ip.startswith("127."):
82
- raise Exception(
83
- "Auto-detected IP is localhost, not useful for external connections"
84
- )
85
-
86
- return local_ip