mcp-mesh 0.4.2__py3-none-any.whl → 0.5.1__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 (61) hide show
  1. _mcp_mesh/__init__.py +14 -3
  2. _mcp_mesh/engine/__init__.py +12 -1
  3. _mcp_mesh/engine/async_mcp_client.py +2 -2
  4. _mcp_mesh/engine/decorator_registry.py +98 -8
  5. _mcp_mesh/engine/dependency_injector.py +249 -71
  6. _mcp_mesh/engine/full_mcp_proxy.py +4 -4
  7. _mcp_mesh/engine/http_wrapper.py +9 -20
  8. _mcp_mesh/engine/mcp_client_proxy.py +1 -1
  9. _mcp_mesh/engine/unified_mcp_proxy.py +813 -0
  10. _mcp_mesh/generated/.openapi-generator/FILES +2 -0
  11. _mcp_mesh/generated/mcp_mesh_registry_client/__init__.py +2 -0
  12. _mcp_mesh/generated/mcp_mesh_registry_client/api/__init__.py +1 -0
  13. _mcp_mesh/generated/mcp_mesh_registry_client/api/tracing_api.py +305 -0
  14. _mcp_mesh/generated/mcp_mesh_registry_client/models/__init__.py +1 -0
  15. _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_info.py +10 -1
  16. _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_agent_registration.py +4 -4
  17. _mcp_mesh/generated/mcp_mesh_registry_client/models/trace_event.py +108 -0
  18. _mcp_mesh/pipeline/__init__.py +2 -2
  19. _mcp_mesh/pipeline/api_heartbeat/__init__.py +16 -0
  20. _mcp_mesh/pipeline/api_heartbeat/api_dependency_resolution.py +506 -0
  21. _mcp_mesh/pipeline/api_heartbeat/api_fast_heartbeat_check.py +117 -0
  22. _mcp_mesh/pipeline/api_heartbeat/api_health_check.py +140 -0
  23. _mcp_mesh/pipeline/api_heartbeat/api_heartbeat_orchestrator.py +247 -0
  24. _mcp_mesh/pipeline/api_heartbeat/api_heartbeat_pipeline.py +309 -0
  25. _mcp_mesh/pipeline/api_heartbeat/api_heartbeat_send.py +332 -0
  26. _mcp_mesh/pipeline/api_heartbeat/api_lifespan_integration.py +147 -0
  27. _mcp_mesh/pipeline/api_heartbeat/api_registry_connection.py +104 -0
  28. _mcp_mesh/pipeline/api_startup/__init__.py +20 -0
  29. _mcp_mesh/pipeline/api_startup/api_pipeline.py +61 -0
  30. _mcp_mesh/pipeline/api_startup/api_server_setup.py +367 -0
  31. _mcp_mesh/pipeline/api_startup/fastapi_discovery.py +302 -0
  32. _mcp_mesh/pipeline/api_startup/route_collection.py +56 -0
  33. _mcp_mesh/pipeline/api_startup/route_integration.py +318 -0
  34. _mcp_mesh/pipeline/{heartbeat → mcp_heartbeat}/dependency_resolution.py +19 -183
  35. _mcp_mesh/pipeline/{startup → mcp_startup}/heartbeat_loop.py +1 -1
  36. _mcp_mesh/pipeline/{startup → mcp_startup}/startup_orchestrator.py +170 -5
  37. _mcp_mesh/shared/config_resolver.py +0 -3
  38. _mcp_mesh/shared/logging_config.py +2 -1
  39. _mcp_mesh/tracing/agent_context_helper.py +1 -1
  40. _mcp_mesh/tracing/execution_tracer.py +41 -0
  41. {mcp_mesh-0.4.2.dist-info → mcp_mesh-0.5.1.dist-info}/METADATA +1 -1
  42. {mcp_mesh-0.4.2.dist-info → mcp_mesh-0.5.1.dist-info}/RECORD +61 -43
  43. mesh/__init__.py +3 -1
  44. mesh/decorators.py +143 -1
  45. mesh/types.py +109 -48
  46. /_mcp_mesh/pipeline/{heartbeat → mcp_heartbeat}/__init__.py +0 -0
  47. /_mcp_mesh/pipeline/{heartbeat → mcp_heartbeat}/fast_heartbeat_check.py +0 -0
  48. /_mcp_mesh/pipeline/{heartbeat → mcp_heartbeat}/heartbeat_orchestrator.py +0 -0
  49. /_mcp_mesh/pipeline/{heartbeat → mcp_heartbeat}/heartbeat_pipeline.py +0 -0
  50. /_mcp_mesh/pipeline/{heartbeat → mcp_heartbeat}/heartbeat_send.py +0 -0
  51. /_mcp_mesh/pipeline/{heartbeat → mcp_heartbeat}/lifespan_integration.py +0 -0
  52. /_mcp_mesh/pipeline/{heartbeat → mcp_heartbeat}/registry_connection.py +0 -0
  53. /_mcp_mesh/pipeline/{startup → mcp_startup}/__init__.py +0 -0
  54. /_mcp_mesh/pipeline/{startup → mcp_startup}/configuration.py +0 -0
  55. /_mcp_mesh/pipeline/{startup → mcp_startup}/decorator_collection.py +0 -0
  56. /_mcp_mesh/pipeline/{startup → mcp_startup}/fastapiserver_setup.py +0 -0
  57. /_mcp_mesh/pipeline/{startup → mcp_startup}/fastmcpserver_discovery.py +0 -0
  58. /_mcp_mesh/pipeline/{startup → mcp_startup}/heartbeat_preparation.py +0 -0
  59. /_mcp_mesh/pipeline/{startup → mcp_startup}/startup_pipeline.py +0 -0
  60. {mcp_mesh-0.4.2.dist-info → mcp_mesh-0.5.1.dist-info}/WHEEL +0 -0
  61. {mcp_mesh-0.4.2.dist-info → mcp_mesh-0.5.1.dist-info}/licenses/LICENSE +0 -0
@@ -72,6 +72,36 @@ class DebounceCoordinator:
72
72
  f"⏰ Scheduled processing in {self.delay_seconds} seconds"
73
73
  )
74
74
 
75
+ def _determine_pipeline_type(self) -> str:
76
+ """
77
+ Determine which pipeline to execute based on registered decorators.
78
+
79
+ Returns:
80
+ "mcp": Only MCP agents/tools found
81
+ "api": Only API routes found
82
+ "mixed": Both MCP and API decorators found (throws exception)
83
+ "none": No decorators found
84
+ """
85
+ from ...engine.decorator_registry import DecoratorRegistry
86
+
87
+ agents = DecoratorRegistry.get_mesh_agents()
88
+ tools = DecoratorRegistry.get_mesh_tools()
89
+ routes = DecoratorRegistry.get_all_by_type("mesh_route")
90
+
91
+ has_mcp = len(agents) > 0 or len(tools) > 0
92
+ has_api = len(routes) > 0
93
+
94
+ self.logger.debug(f"🔍 Pipeline type detection: MCP={has_mcp} ({len(agents)} agents, {len(tools)} tools), API={has_api} ({len(routes)} routes)")
95
+
96
+ if has_api and has_mcp:
97
+ return "mixed"
98
+ elif has_api:
99
+ return "api"
100
+ elif has_mcp:
101
+ return "mcp"
102
+ else:
103
+ return "none"
104
+
75
105
  def _execute_processing(self) -> None:
76
106
  """Execute the processing (called by timer)."""
77
107
  try:
@@ -83,6 +113,21 @@ class DebounceCoordinator:
83
113
  f"🚀 Debounce delay ({self.delay_seconds}s) complete, processing all decorators"
84
114
  )
85
115
 
116
+ # Determine which pipeline to execute
117
+ pipeline_type = self._determine_pipeline_type()
118
+
119
+ if pipeline_type == "mixed":
120
+ error_msg = (
121
+ "❌ Mixed mode not supported: Cannot use @mesh.route decorators "
122
+ "together with @mesh.tool/@mesh.agent decorators in the same process. "
123
+ "Please use either MCP agent decorators OR API route decorators, not both."
124
+ )
125
+ self.logger.error(error_msg)
126
+ raise RuntimeError(error_msg)
127
+ elif pipeline_type == "none":
128
+ self.logger.warning("⚠️ No decorators found - nothing to process")
129
+ return
130
+
86
131
  # Execute the pipeline using asyncio.run
87
132
  import asyncio
88
133
 
@@ -90,18 +135,36 @@ class DebounceCoordinator:
90
135
  auto_run_enabled = self._check_auto_run_enabled()
91
136
 
92
137
  self.logger.debug(f"🔍 Auto-run enabled: {auto_run_enabled}")
138
+ self.logger.info(f"🎯 Pipeline type: {pipeline_type}")
93
139
 
94
140
  if auto_run_enabled:
95
141
  self.logger.info("🔄 Auto-run enabled - using FastAPI natural blocking")
96
- # Phase 1: Run async pipeline setup
97
- result = asyncio.run(self._orchestrator.process_once())
142
+
143
+ # Execute appropriate pipeline based on type
144
+ if pipeline_type == "mcp":
145
+ # Phase 1: Run async MCP pipeline setup
146
+ result = asyncio.run(self._orchestrator.process_once())
147
+ elif pipeline_type == "api":
148
+ # Phase 1: Run async API pipeline setup
149
+ result = asyncio.run(self._orchestrator.process_api_once())
150
+ else:
151
+ raise RuntimeError(f"Unsupported pipeline type: {pipeline_type}")
98
152
 
99
153
  # Phase 2: Extract FastAPI app and start synchronous server
100
154
  pipeline_context = result.get("context", {}).get("pipeline_context", {})
101
155
  fastapi_app = pipeline_context.get("fastapi_app")
102
156
  binding_config = pipeline_context.get("fastapi_binding_config", {})
103
-
104
- if fastapi_app and binding_config:
157
+ heartbeat_config = pipeline_context.get("heartbeat_config", {})
158
+
159
+ if pipeline_type == "api":
160
+ # For API services, ONLY do dependency injection - user controls their FastAPI server
161
+ # Dependency injection is already complete from pipeline execution
162
+ # Optionally start heartbeat in background (non-blocking)
163
+ self._setup_api_heartbeat_background(heartbeat_config, pipeline_context)
164
+ self.logger.info("✅ API dependency injection complete - user's FastAPI server can now start")
165
+ return # Don't block - let user's uvicorn run
166
+ elif fastapi_app and binding_config:
167
+ # For MCP agents with FastAPI server
105
168
  self._start_blocking_fastapi_server(fastapi_app, binding_config)
106
169
  else:
107
170
  self.logger.warning(
@@ -110,11 +173,20 @@ class DebounceCoordinator:
110
173
  else:
111
174
  # Single execution mode (for testing/debugging)
112
175
  self.logger.info("🏁 Auto-run disabled - single execution mode")
113
- result = asyncio.run(self._orchestrator.process_once())
176
+
177
+ if pipeline_type == "mcp":
178
+ result = asyncio.run(self._orchestrator.process_once())
179
+ elif pipeline_type == "api":
180
+ result = asyncio.run(self._orchestrator.process_api_once())
181
+ else:
182
+ raise RuntimeError(f"Unsupported pipeline type: {pipeline_type}")
183
+
114
184
  self.logger.info("✅ Pipeline execution completed, exiting")
115
185
 
116
186
  except Exception as e:
117
187
  self.logger.error(f"❌ Error in debounced processing: {e}")
188
+ # Re-raise to ensure the system exits on mixed mode or other critical errors
189
+ raise
118
190
 
119
191
  def _start_blocking_fastapi_server(
120
192
  self, app: Any, binding_config: dict[str, Any]
@@ -148,6 +220,58 @@ class DebounceCoordinator:
148
220
  self.logger.error(f"❌ FastAPI server error: {e}")
149
221
  raise
150
222
 
223
+ def _setup_api_heartbeat_background(
224
+ self, heartbeat_config: dict[str, Any], pipeline_context: dict[str, Any]
225
+ ) -> None:
226
+ """Setup API heartbeat to run in background - non-blocking."""
227
+ try:
228
+ # Populate heartbeat context with current pipeline context
229
+ heartbeat_config["context"] = pipeline_context
230
+ service_id = heartbeat_config.get("service_id", "unknown")
231
+ standalone_mode = heartbeat_config.get("standalone_mode", False)
232
+
233
+ if standalone_mode:
234
+ self.logger.info(
235
+ f"📝 API service '{service_id}' configured in standalone mode - no heartbeat"
236
+ )
237
+ return
238
+
239
+ self.logger.info(
240
+ f"🔗 Setting up background API heartbeat for service '{service_id}'"
241
+ )
242
+
243
+ # Import heartbeat functionality
244
+ from ..api_heartbeat.api_lifespan_integration import api_heartbeat_lifespan_task
245
+ import threading
246
+ import asyncio
247
+
248
+ def run_heartbeat():
249
+ """Run heartbeat in separate thread with its own event loop."""
250
+ self.logger.debug(f"Starting background heartbeat thread for {service_id}")
251
+ try:
252
+ # Create new event loop for this thread
253
+ loop = asyncio.new_event_loop()
254
+ asyncio.set_event_loop(loop)
255
+
256
+ # Run heartbeat task
257
+ loop.run_until_complete(api_heartbeat_lifespan_task(heartbeat_config))
258
+ except Exception as e:
259
+ self.logger.error(f"❌ Background heartbeat error: {e}")
260
+ finally:
261
+ loop.close()
262
+
263
+ # Start heartbeat in daemon thread (won't prevent process exit)
264
+ heartbeat_thread = threading.Thread(target=run_heartbeat, daemon=True)
265
+ heartbeat_thread.start()
266
+
267
+ self.logger.info(
268
+ f"💓 Background API heartbeat thread started for service '{service_id}'"
269
+ )
270
+
271
+ except Exception as e:
272
+ self.logger.warning(f"⚠️ Could not setup API heartbeat: {e}")
273
+ # Don't fail - heartbeat is optional for API services
274
+
151
275
  def _perform_graceful_shutdown(self) -> None:
152
276
  """Perform graceful shutdown by unregistering from registry."""
153
277
  try:
@@ -272,6 +396,43 @@ class MeshOrchestrator:
272
396
  "timestamp": result.timestamp.isoformat(),
273
397
  }
274
398
 
399
+ async def process_api_once(self) -> dict:
400
+ """
401
+ Execute the API pipeline once for @mesh.route decorators.
402
+
403
+ This handles FastAPI route integration and dependency injection setup.
404
+ """
405
+ self.logger.info(f"🚀 Starting API pipeline execution: {self.name}")
406
+
407
+ try:
408
+ # Import API pipeline here to avoid circular imports
409
+ from ..api_startup import APIPipeline
410
+
411
+ # Create and execute API pipeline
412
+ api_pipeline = APIPipeline(name=f"{self.name}-api")
413
+ result = await api_pipeline.execute()
414
+
415
+ # Convert result to dict for return type (same format as MCP pipeline)
416
+ return {
417
+ "status": result.status.value,
418
+ "message": result.message,
419
+ "errors": result.errors,
420
+ "context": result.context,
421
+ "timestamp": result.timestamp.isoformat(),
422
+ }
423
+
424
+ except Exception as e:
425
+ error_msg = f"API pipeline execution failed: {e}"
426
+ self.logger.error(f"❌ {error_msg}")
427
+
428
+ return {
429
+ "status": "failed",
430
+ "message": error_msg,
431
+ "errors": [str(e)],
432
+ "context": {},
433
+ "timestamp": "unknown",
434
+ }
435
+
275
436
  async def start_service(self, auto_run_config: Optional[dict] = None) -> None:
276
437
  """
277
438
  Start the service with optional auto-run behavior.
@@ -366,6 +527,10 @@ def start_runtime() -> None:
366
527
  Actual pipeline execution will be triggered by decorator registration
367
528
  with a configurable delay to ensure all decorators are captured.
368
529
  """
530
+ # Configure logging FIRST before any log messages
531
+ from ...shared.logging_config import configure_logging
532
+ configure_logging()
533
+
369
534
  logger.info("🔧 Starting MCP Mesh runtime with debouncing")
370
535
 
371
536
  # Install signal handlers in main thread FIRST (before any threading)
@@ -71,12 +71,9 @@ def get_config_value(
71
71
  raw_value = default
72
72
  source = "default"
73
73
 
74
- logger.debug(f"Config {env_var}: raw_value={raw_value} (from {source})")
75
-
76
74
  # Step 2: Validate and convert the value
77
75
  try:
78
76
  validated_value = _validate_value(raw_value, rule, env_var)
79
- logger.debug(f"Config {env_var}: validated_value={validated_value}")
80
77
  return validated_value
81
78
 
82
79
  except ConfigResolutionError as e:
@@ -69,8 +69,9 @@ def configure_logging():
69
69
  root_logger.addHandler(handler)
70
70
  root_logger.setLevel(log_level)
71
71
 
72
- # Set level for all mcp_mesh loggers
72
+ # Set level for all mcp_mesh loggers (both mcp_mesh and _mcp_mesh namespaces)
73
73
  logging.getLogger("mcp_mesh").setLevel(log_level)
74
+ logging.getLogger("_mcp_mesh").setLevel(log_level)
74
75
 
75
76
  # Return the configured level for reference
76
77
  return log_level
@@ -41,7 +41,7 @@ class AgentContextHelper:
41
41
  from ..engine.decorator_registry import DecoratorRegistry
42
42
 
43
43
  agent_config = DecoratorRegistry.get_resolved_agent_config()
44
-
44
+
45
45
  # Extract core configuration fields using proper resolution
46
46
  context["agent_id"] = agent_config.get("agent_id", "unknown")
47
47
  context["agent_name"] = agent_config.get("name") or os.getenv(
@@ -191,3 +191,44 @@ class ExecutionTracer:
191
191
  except Exception as e:
192
192
  tracer.end_execution(error=str(e), success=False)
193
193
  raise # Re-raise the exception
194
+
195
+ @staticmethod
196
+ async def trace_function_execution_async(
197
+ func: Callable,
198
+ args: tuple,
199
+ kwargs: dict,
200
+ dependencies: list[str],
201
+ mesh_positions: list[int],
202
+ injected_count: int,
203
+ logger_instance: logging.Logger,
204
+ ) -> Any:
205
+ """
206
+ Trace async function execution with comprehensive logging.
207
+
208
+ This is a static method that handles the complete execution flow with proper
209
+ exception handling and cleanup. If tracing is disabled, calls function directly.
210
+ """
211
+ import inspect
212
+
213
+ # If tracing is disabled, call function directly without any overhead
214
+ if not _is_tracing_enabled():
215
+ if inspect.iscoroutinefunction(func):
216
+ return await func(*args, **kwargs)
217
+ else:
218
+ return func(*args, **kwargs)
219
+
220
+ tracer = ExecutionTracer(func.__name__, logger_instance)
221
+ tracer.start_execution(
222
+ args, kwargs, dependencies, mesh_positions, injected_count
223
+ )
224
+
225
+ try:
226
+ if inspect.iscoroutinefunction(func):
227
+ result = await func(*args, **kwargs)
228
+ else:
229
+ result = func(*args, **kwargs)
230
+ tracer.end_execution(result, success=True)
231
+ return result
232
+ except Exception as e:
233
+ tracer.end_execution(error=str(e), success=False)
234
+ raise # Re-raise the exception
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-mesh
3
- Version: 0.4.2
3
+ Version: 0.5.1
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,30 +1,32 @@
1
- _mcp_mesh/__init__.py,sha256=4YFRyw3RwxKNKMPP9rmT4enhGSe8ceAG8AgOKbU5ITY,2103
2
- _mcp_mesh/engine/__init__.py,sha256=o4axdnjeUnUTsukZFacMNnxd6LRX2D46qJzMzUfEZUI,3259
3
- _mcp_mesh/engine/async_mcp_client.py,sha256=IdcCsswl1WXnzIWGb686Morgjf4CROm411EiDeE0cac,6308
4
- _mcp_mesh/engine/decorator_registry.py,sha256=w_-10yBkgYAu_u-SUrTaZcCOv1t_MqlHPg0vFQGdy2I,15506
5
- _mcp_mesh/engine/dependency_injector.py,sha256=RbXa9ap1PYRc7G3-ZnJkWQOcibQfecQDGIU-_7O2Raw,17310
6
- _mcp_mesh/engine/full_mcp_proxy.py,sha256=fJyU5mbmvqDXaSgnT00eVb9EnumTHP2QMwAPB_IfIQc,25301
7
- _mcp_mesh/engine/http_wrapper.py,sha256=NFKCcUPwSLeyLuTc1TwWpBRrlsbgkzBoiZiZixhOias,21453
8
- _mcp_mesh/engine/mcp_client_proxy.py,sha256=RpHdwCjC6mwiZ5idv7h8OchFRbufyC2NLaHxzU2kKyo,17623
1
+ _mcp_mesh/__init__.py,sha256=BAOIf5sCRsdTxO-EgvzGKGN24_NRdv21wYj6uKjSHYM,2646
2
+ _mcp_mesh/engine/__init__.py,sha256=2ennzbo7yJcpkXO9BqN69TruLjJfmJY4Y5VEsG644K4,3630
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
5
+ _mcp_mesh/engine/dependency_injector.py,sha256=tzsv10U_EG8GjhTvlF2fsoXkKQ2x52_WYQCOaLlgWfs,26568
6
+ _mcp_mesh/engine/full_mcp_proxy.py,sha256=PlRv7GSKqn5riOCqeCVulVdtq3z1Ug76mOkwMsOFHXw,25297
7
+ _mcp_mesh/engine/http_wrapper.py,sha256=7Djx5zreai4c644sDpwpV_Kn35Ie6_ulUhqFOWoU_kU,21110
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/unified_mcp_proxy.py,sha256=pKKwfe5qEKa_JwKelHw6t0hJyYpBMHGYdZ3mFuo3QxY,32748
13
14
  _mcp_mesh/generated/.openapi-generator-ignore,sha256=-d-Y-RVAZRrHw36jO0b79oDXpfA8rZdBGPCG4Vs_rUs,227
14
- _mcp_mesh/generated/.openapi-generator/FILES,sha256=S08v94ANDOk4-N-dprHSrXaqWwHp_D2mvVMQl3j283A,2078
15
+ _mcp_mesh/generated/.openapi-generator/FILES,sha256=BXFXHe4FTV5GFUTNjMhmvOrVcYVlHJc5O-3UXyp8OCY,2169
15
16
  _mcp_mesh/generated/.openapi-generator/VERSION,sha256=nMm490YXJUW3_vAdeAsg7E3yRgUqVwk5-50PuaFonM8,7
16
- _mcp_mesh/generated/mcp_mesh_registry_client/__init__.py,sha256=dz-3o3W3r_hDAFAGOXr4UCeVvXWqkZyCx4KtfI4xYHY,5433
17
+ _mcp_mesh/generated/mcp_mesh_registry_client/__init__.py,sha256=C_J51kNjdAQ2ikcXQzaIC0x9HQgbibT02x1rmWpeKGw,5604
17
18
  _mcp_mesh/generated/mcp_mesh_registry_client/api_client.py,sha256=SLzm-p1vhbhufVDn7sNKjq9aSBkZUXTdwRIf7Ne57l8,28104
18
19
  _mcp_mesh/generated/mcp_mesh_registry_client/api_response.py,sha256=eMxw1mpmJcoGZ3gs9z6jM4oYoZ10Gjk333s9sKxGv7s,652
19
20
  _mcp_mesh/generated/mcp_mesh_registry_client/configuration.py,sha256=PCZ03PQGCEG0lfIn4s1tk8WrB9HpQ-LCXIUK110F1gY,18544
20
21
  _mcp_mesh/generated/mcp_mesh_registry_client/exceptions.py,sha256=wMmZWaCPfoAloDVS4u9MkRKG0oUjXl_B-3KAOrFQbUM,6921
21
22
  _mcp_mesh/generated/mcp_mesh_registry_client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
23
  _mcp_mesh/generated/mcp_mesh_registry_client/rest.py,sha256=myEMrqhlSTaYUu52NlhUqFFTTMkhBI4V5cXaxjciWSI,9948
23
- _mcp_mesh/generated/mcp_mesh_registry_client/api/__init__.py,sha256=61J3DpxqJfJ91WPD6gw_ngZEqtGzCFydc9hsqPKucd0,212
24
+ _mcp_mesh/generated/mcp_mesh_registry_client/api/__init__.py,sha256=CP4BRsWFhioEIcU_fXbONaCa222wnWB8fTwADfqGUsc,296
24
25
  _mcp_mesh/generated/mcp_mesh_registry_client/api/agents_api.py,sha256=rHY0h_wcv9eLRzOitkRZ7PDKtgWr5xism3azkZpuI6I,46445
25
26
  _mcp_mesh/generated/mcp_mesh_registry_client/api/health_api.py,sha256=BJsqdNHz-dZXJsvQmYdZLLM_vf1Lf6DKRH9xzpyfkuU,30552
26
- _mcp_mesh/generated/mcp_mesh_registry_client/models/__init__.py,sha256=dVazgBm35vuNmYFfb1nJooMT0GHu9VNwGuysjKWzNYM,3881
27
- _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_info.py,sha256=Yh57CvFPjiaNLy-ub81kYPcm1eqc8Fpn9OXQZGtgZxg,4845
27
+ _mcp_mesh/generated/mcp_mesh_registry_client/api/tracing_api.py,sha256=2FmSDoWiAfx404kB5mkWvYviE37QVY-Cb-kA90o1rn8,13179
28
+ _mcp_mesh/generated/mcp_mesh_registry_client/models/__init__.py,sha256=s54kd3wYhgltJgOFD0RLQX7prcuI4uDibwVXXo5SrTs,3968
29
+ _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_info.py,sha256=ZD3qwc7GlKYV3Jlxbc79gEJWf-2_H0m07GVyHL4cCVw,5293
28
30
  _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_metadata.py,sha256=_ixbM8wlhL5C7jx-NXoA0zdJSK88-TX8URuCIOv7_E8,6520
29
31
  _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_metadata_dependencies_inner.py,sha256=LiO2JfFwasidNJ859LKmDHa6kV1boGIlTdhFwmpgoxw,6303
30
32
  _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_metadata_dependencies_inner_one_of.py,sha256=aQ7h5TloM-FvBrQ0uzH0m9iba4EbX--djxh8etcXids,3615
@@ -42,7 +44,7 @@ _mcp_mesh/generated/mcp_mesh_registry_client/models/heartbeat_request.py,sha256=
42
44
  _mcp_mesh/generated/mcp_mesh_registry_client/models/heartbeat_request_metadata.py,sha256=A-L6mFz19Pa-QExvCihCHrJDHIlIV18w290usgD6_Yo,4317
43
45
  _mcp_mesh/generated/mcp_mesh_registry_client/models/heartbeat_response.py,sha256=dPJxz5XQukw4QejkeP7F3FAm8m0wW-5gm9xpajlkGqQ,4907
44
46
  _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_agent_register_metadata.py,sha256=HGABUatJp_mu2uFWzC7HBEZACHXIKFmIeegFYfHEcN4,4820
45
- _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_agent_registration.py,sha256=9LylYbX2tI-59H-WzTwz1elrKELALAia9UGi83pDD9s,6139
47
+ _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_agent_registration.py,sha256=qZiljiQH877iBN35poelxDPPnHwpjbu9J2yCwKmZgg8,6182
46
48
  _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_registration_response.py,sha256=tKG2maoADs_cXc2IdU-Pycsf0jyfOLnqeGZJtLXwf3E,5075
47
49
  _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_registration_response_dependencies_resolved_value_inner.py,sha256=tmcFBO8rbB_F-TfpU2lsG29CKTpQPfiuJqE62r81p0k,4071
48
50
  _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_tool_dependency_registration.py,sha256=PkY1rWdS3y5TTxRRFpATWmm3dhw_Qw2_4QBzCv_7ZuU,3658
@@ -52,48 +54,64 @@ _mcp_mesh/generated/mcp_mesh_registry_client/models/registration_response.py,sha
52
54
  _mcp_mesh/generated/mcp_mesh_registry_client/models/rich_dependency.py,sha256=Y6TB1Zvfkq2U8YMSmWUmIcOZMU6ZZxRFmifUGwdRwa0,3591
53
55
  _mcp_mesh/generated/mcp_mesh_registry_client/models/root_response.py,sha256=Das3mRHvgqDJVuWsoWLtwUTv_I5nFejhV10U0JZ_CZM,3221
54
56
  _mcp_mesh/generated/mcp_mesh_registry_client/models/standardized_dependency.py,sha256=fm1tmP0f07ziGMz0zYvpIvMGeIcfff2wrS6jlWgvm9A,3650
55
- _mcp_mesh/pipeline/__init__.py,sha256=MMTikEB9V7JkYsVG_sf78vNv0fdO2d_9CLawdqyMToo,1549
56
- _mcp_mesh/pipeline/heartbeat/__init__.py,sha256=nRNjZ3VD_9bPLQuJ6Nc02gE7KSLcMP7TMquB0hP6hHs,844
57
- _mcp_mesh/pipeline/heartbeat/dependency_resolution.py,sha256=SMSwoB0MvXV-TrgCbAXB59AotIZYQW9Dinrkhsircdw,25145
58
- _mcp_mesh/pipeline/heartbeat/fast_heartbeat_check.py,sha256=QTzYdL81WERkOaBVOgNbFQh1ddTn70urNtyIgtFTudA,4465
59
- _mcp_mesh/pipeline/heartbeat/heartbeat_orchestrator.py,sha256=56IK5p8xlppJ5i6oMMh12fin3XwrOTcppTuVO2CnlV8,11292
60
- _mcp_mesh/pipeline/heartbeat/heartbeat_pipeline.py,sha256=Jb7EVJO13trUVO3aCSgzGqAtoc4vie5kDrYLZtOkiXg,11601
61
- _mcp_mesh/pipeline/heartbeat/heartbeat_send.py,sha256=ydVx-Vb_RgW1WPCboHVdZfEwNbgDngFV6Y9elZIBrAw,3602
62
- _mcp_mesh/pipeline/heartbeat/lifespan_integration.py,sha256=yCt731vgniSfxuClIcMvlMtDrubTu_9crgwiOwOYoBQ,2510
63
- _mcp_mesh/pipeline/heartbeat/registry_connection.py,sha256=4abbOKN3echwX82PV0RvxF6cJZUu0pMgisOpILZ_ZzY,2875
57
+ _mcp_mesh/generated/mcp_mesh_registry_client/models/trace_event.py,sha256=9Q_8WaVl0MxswRnHpkqq9GKnvOW54HW4tkrTM9oda14,4461
58
+ _mcp_mesh/pipeline/__init__.py,sha256=9Aplh4m1z-rYTQys0JQLYlq9wTPdI72eSOhUPqcnvpA,1557
59
+ _mcp_mesh/pipeline/api_heartbeat/__init__.py,sha256=IXTLoQLAPqQEWZ8VMWc5W_cQJkDv95rlVGXyXoQDjHk,473
60
+ _mcp_mesh/pipeline/api_heartbeat/api_dependency_resolution.py,sha256=EcWfqZ2IWcagt0pGkVa944uW7KePABVXn6CPAAspjYo,23582
61
+ _mcp_mesh/pipeline/api_heartbeat/api_fast_heartbeat_check.py,sha256=PY4bbuZgxy3r0ccuBl-OuJvcPSMhyGz4FomxwYFhuvM,4821
62
+ _mcp_mesh/pipeline/api_heartbeat/api_health_check.py,sha256=kDmFeOG_4tyqyJSBZjPcc7xTzGpP4vq6ObW_WBqXvzM,5130
63
+ _mcp_mesh/pipeline/api_heartbeat/api_heartbeat_orchestrator.py,sha256=uBswzWOBzU8p_C0AE2DF8UwIWG4rP2zecHfPqKzNuC0,10367
64
+ _mcp_mesh/pipeline/api_heartbeat/api_heartbeat_pipeline.py,sha256=so8IyKT-Wg7lQk3ULdox9CAOrowzxpUs76e93PQnCik,13520
65
+ _mcp_mesh/pipeline/api_heartbeat/api_heartbeat_send.py,sha256=0Qy6zau_3a2qtQdd2wzjXv-QhhzR6bwAaEun8Re9hXo,14546
66
+ _mcp_mesh/pipeline/api_heartbeat/api_lifespan_integration.py,sha256=WBo2crcaGfxi8Q46TU-i5OMhAv0sQKz7Z9jps-GLkvM,5183
67
+ _mcp_mesh/pipeline/api_heartbeat/api_registry_connection.py,sha256=6N0JdXdnLkaXau4t8syt9DLgv9Y51SPfTXYK3DefBk8,3846
68
+ _mcp_mesh/pipeline/api_startup/__init__.py,sha256=eivolkSKot2bJTWP2BV8-RKRT1Zm7SGQYuEUiTxusOQ,577
69
+ _mcp_mesh/pipeline/api_startup/api_pipeline.py,sha256=9KMyryefhaPBkGJylNoFcFr5Ezh6NJWKBOJfVJaVJ5A,2302
70
+ _mcp_mesh/pipeline/api_startup/api_server_setup.py,sha256=Qy0wbXyIWIQYA7CjiGVZwn0nWCKK85ZzFTRI2JDA9Aw,15099
71
+ _mcp_mesh/pipeline/api_startup/fastapi_discovery.py,sha256=BOo7_cBwABNpk_3Y-Lh_3ygwAOQ_254dwblPyO_vLIw,11991
72
+ _mcp_mesh/pipeline/api_startup/route_collection.py,sha256=UjA-F5_RbGVU5TfDT19Np5_x2PtYkNn2mGFyivDsk24,2031
73
+ _mcp_mesh/pipeline/api_startup/route_integration.py,sha256=aMT7p7cwK8N3tZBRqeGQF8upc7tU-Exj6Dz0a4cSBhU,13441
74
+ _mcp_mesh/pipeline/mcp_heartbeat/__init__.py,sha256=nRNjZ3VD_9bPLQuJ6Nc02gE7KSLcMP7TMquB0hP6hHs,844
75
+ _mcp_mesh/pipeline/mcp_heartbeat/dependency_resolution.py,sha256=nHjKSM4Yyn05wnfw1dI4VGroHZy8lRkDihnZfFHeF8U,16217
76
+ _mcp_mesh/pipeline/mcp_heartbeat/fast_heartbeat_check.py,sha256=QTzYdL81WERkOaBVOgNbFQh1ddTn70urNtyIgtFTudA,4465
77
+ _mcp_mesh/pipeline/mcp_heartbeat/heartbeat_orchestrator.py,sha256=56IK5p8xlppJ5i6oMMh12fin3XwrOTcppTuVO2CnlV8,11292
78
+ _mcp_mesh/pipeline/mcp_heartbeat/heartbeat_pipeline.py,sha256=Jb7EVJO13trUVO3aCSgzGqAtoc4vie5kDrYLZtOkiXg,11601
79
+ _mcp_mesh/pipeline/mcp_heartbeat/heartbeat_send.py,sha256=ydVx-Vb_RgW1WPCboHVdZfEwNbgDngFV6Y9elZIBrAw,3602
80
+ _mcp_mesh/pipeline/mcp_heartbeat/lifespan_integration.py,sha256=yCt731vgniSfxuClIcMvlMtDrubTu_9crgwiOwOYoBQ,2510
81
+ _mcp_mesh/pipeline/mcp_heartbeat/registry_connection.py,sha256=4abbOKN3echwX82PV0RvxF6cJZUu0pMgisOpILZ_ZzY,2875
82
+ _mcp_mesh/pipeline/mcp_startup/__init__.py,sha256=_Y25DgT9eYJ_7Qe7x1Z7y4VFUIaFEmCBS9YD9QIlo7k,1142
83
+ _mcp_mesh/pipeline/mcp_startup/configuration.py,sha256=6LRLIxrqFMU76qrBb6GjGknUlKPZZ9iqOlxE7F9ZhLs,2808
84
+ _mcp_mesh/pipeline/mcp_startup/decorator_collection.py,sha256=RHC6MHtfP9aP0hZ-IJjISZu72e0Pml3LU0qr7dc284w,2294
85
+ _mcp_mesh/pipeline/mcp_startup/fastapiserver_setup.py,sha256=pQIrEc70qeWP732S9LF5A2qWq0CZ5wK-bxls7AjX_2o,33346
86
+ _mcp_mesh/pipeline/mcp_startup/fastmcpserver_discovery.py,sha256=ktsE9EZYdyZbCtCKB6HVdzGFMQ0E9n0-7I55LRO99sE,10270
87
+ _mcp_mesh/pipeline/mcp_startup/heartbeat_loop.py,sha256=v85B0ynomvYu87eIvLe-aSZ7-Iwov2VtM4Fg3PkmrZs,3865
88
+ _mcp_mesh/pipeline/mcp_startup/heartbeat_preparation.py,sha256=v3Fl0PvW5s7Ib_Cy7WtXA7gDvsFGiz54a-IlQRTcLPg,10410
89
+ _mcp_mesh/pipeline/mcp_startup/startup_orchestrator.py,sha256=W9o_d0xTOAqK5i8Xd7BJgiUJou1uPZwsygslM3I9HYo,25149
90
+ _mcp_mesh/pipeline/mcp_startup/startup_pipeline.py,sha256=E-kmmfkdtRMhpEAF-_qFLAdaY3sLo165SUHjlRXwqC4,2069
64
91
  _mcp_mesh/pipeline/shared/__init__.py,sha256=s9xmdf6LkoetrVRGd7Zp3NUxcJCW6YZ_yNKzUBcnYys,352
65
92
  _mcp_mesh/pipeline/shared/base_step.py,sha256=kyPbNUX79NyGrE_0Q-e-Aek7m1J0TW036njWfv0iZ0I,1080
66
93
  _mcp_mesh/pipeline/shared/mesh_pipeline.py,sha256=wTODlMb7ByhzsT3DnM0lzY3gS9fKLDUxL0yDEozCVeo,6494
67
94
  _mcp_mesh/pipeline/shared/pipeline_types.py,sha256=iKSgzCoYu3bpIwLViw6lE7T7irEzOm7gpie29lyy7SQ,1504
68
95
  _mcp_mesh/pipeline/shared/registry_connection.py,sha256=jmlgULixRM1NMNyMc-8SJN3RpCtV9hUf76vn9nciAN4,2904
69
- _mcp_mesh/pipeline/startup/__init__.py,sha256=_Y25DgT9eYJ_7Qe7x1Z7y4VFUIaFEmCBS9YD9QIlo7k,1142
70
- _mcp_mesh/pipeline/startup/configuration.py,sha256=6LRLIxrqFMU76qrBb6GjGknUlKPZZ9iqOlxE7F9ZhLs,2808
71
- _mcp_mesh/pipeline/startup/decorator_collection.py,sha256=RHC6MHtfP9aP0hZ-IJjISZu72e0Pml3LU0qr7dc284w,2294
72
- _mcp_mesh/pipeline/startup/fastapiserver_setup.py,sha256=pQIrEc70qeWP732S9LF5A2qWq0CZ5wK-bxls7AjX_2o,33346
73
- _mcp_mesh/pipeline/startup/fastmcpserver_discovery.py,sha256=ktsE9EZYdyZbCtCKB6HVdzGFMQ0E9n0-7I55LRO99sE,10270
74
- _mcp_mesh/pipeline/startup/heartbeat_loop.py,sha256=0IX2Q-OJvTVsNILUKxl7v3-BM8DdAW3UB-W-Z2gAWrc,3861
75
- _mcp_mesh/pipeline/startup/heartbeat_preparation.py,sha256=v3Fl0PvW5s7Ib_Cy7WtXA7gDvsFGiz54a-IlQRTcLPg,10410
76
- _mcp_mesh/pipeline/startup/startup_orchestrator.py,sha256=r0po5cQKe9F5nO6ey5PmYGjYKHe5u8pNEFy7Da0Lh5w,17835
77
- _mcp_mesh/pipeline/startup/startup_pipeline.py,sha256=E-kmmfkdtRMhpEAF-_qFLAdaY3sLo165SUHjlRXwqC4,2069
78
96
  _mcp_mesh/shared/__init__.py,sha256=L0detgEWjnc_XfxA_5vIBcaGTuLcT6AARKkUpUBIaH4,1116
79
- _mcp_mesh/shared/config_resolver.py,sha256=aEsb9mMciQUjpdOpmPpwjgSRESjqZTvzKscoYoUKBhQ,7935
97
+ _mcp_mesh/shared/config_resolver.py,sha256=r8WcO8lrd4XBHhH9IkAM7iy-QeCmTiVLmKhnJ47pFP0,7780
80
98
  _mcp_mesh/shared/content_extractor.py,sha256=culjhieFl_J6EMDv1VFKvS38O3IMhWMs8fHhNuR2rVk,3656
81
99
  _mcp_mesh/shared/defaults.py,sha256=5qazybkn7QHi418dXCS0b6QlNQl3DMg97ItzNGkc8d4,1851
82
100
  _mcp_mesh/shared/fast_heartbeat_status.py,sha256=OquEsX9ZTbxY1lIsll0Mbb2KDzSJD76sLMOlr3Z73Sc,5188
83
101
  _mcp_mesh/shared/host_resolver.py,sha256=ycs6gXnI1zJX5KiqiLJPX5GkHX8r4j8NMHQOlG2J2X8,2964
84
- _mcp_mesh/shared/logging_config.py,sha256=m_eW5ub01mjjoLdF55lv9JwSK9y52rqpVTRN8jNz_6s,2381
102
+ _mcp_mesh/shared/logging_config.py,sha256=n9AqShZ5BZgyrkoTlvux6ECRVpM9dUYvmGB0NPMl-Ak,2477
85
103
  _mcp_mesh/shared/registry_client_wrapper.py,sha256=d8yL-MiCrQr_WYdRFStOd531qaLv9kZjh0zJAmCJ-Cc,16976
86
104
  _mcp_mesh/shared/sse_parser.py,sha256=OEPnfL9xL3rsjQrbyvfUO82WljPSDeO6Z61uUwN1NAo,8035
87
105
  _mcp_mesh/shared/support_types.py,sha256=k-ICF_UwDkHxQ1D5LwFZrp-UrNb4E5dzw02CRuLW9iI,7264
88
- _mcp_mesh/tracing/agent_context_helper.py,sha256=XR3OyVT40lWyXaSWYBWdOhs0ZqTj3Xa49BSG8EUzrjI,4595
106
+ _mcp_mesh/tracing/agent_context_helper.py,sha256=gODirjGf80vTLoHaVZgOOLlf068Tfxg-3GBn2GOBUaY,4607
89
107
  _mcp_mesh/tracing/context.py,sha256=yZtTZXVKltX2BUBrWeeNfQ8Y4n6L_2ywFVlFP-wU9og,2353
90
- _mcp_mesh/tracing/execution_tracer.py,sha256=rAyAhSMy7Mr5KpZcQUfDcQ5QW8bj-g-H6wVB3tvkkR4,6852
108
+ _mcp_mesh/tracing/execution_tracer.py,sha256=Wx6cE7CskZqBPsrn4Fmkyd3DLR9R_E4j_BCLVx1cOWE,8293
91
109
  _mcp_mesh/tracing/redis_metadata_publisher.py,sha256=5U5GojBFYZdxJJ1RvmdxXHC-VuPQz4kMla6n0xdZxKc,5371
92
110
  _mcp_mesh/tracing/trace_context_helper.py,sha256=xPDygKrXcoJbrr7013Av_I0ajammWEq68ihdP7DZ-Zo,6072
93
- mesh/__init__.py,sha256=XtquSwwLeVJwQZjg8jO4_vRNSQZtWo_Mv2tixI1_Yzg,3171
94
- mesh/decorators.py,sha256=_WLqfMXQgi_4Xarci-sRa3zxqKWIf-2HYELh9MAitYY,18397
95
- mesh/types.py,sha256=cR8oRKHZjZXNkbHPYdRfLNDyCE4yEz_Fbrmdc08NY6k,8775
96
- mcp_mesh-0.4.2.dist-info/METADATA,sha256=clX_rcn6hz6Q3wSbA4Wx2fkqNSm4LWmbOVL0nhojKA8,4879
97
- mcp_mesh-0.4.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
98
- mcp_mesh-0.4.2.dist-info/licenses/LICENSE,sha256=_EBQHRQThv9FPOLc5eFOUdeeRO0mYwChC7cx60dM1tM,1078
99
- mcp_mesh-0.4.2.dist-info/RECORD,,
111
+ mesh/__init__.py,sha256=l5RSMV8Kx0h7cvku8YkZPbTHjEPWciGT0bcEB2O_eNU,3242
112
+ mesh/decorators.py,sha256=UWNfykDBCChpIqqljcjAAzlJphv5TMZLEBCNzwfl478,24264
113
+ mesh/types.py,sha256=g37DXAzya-xGPa1_WKlW3T3_VqyTn8ZVepIDSrhBTkc,10815
114
+ mcp_mesh-0.5.1.dist-info/METADATA,sha256=8R-o_N6olx0pnUwDUij1I2PN2IYFkd9Mmrg-ocBL3ls,4879
115
+ mcp_mesh-0.5.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
116
+ mcp_mesh-0.5.1.dist-info/licenses/LICENSE,sha256=_EBQHRQThv9FPOLc5eFOUdeeRO0mYwChC7cx60dM1tM,1078
117
+ mcp_mesh-0.5.1.dist-info/RECORD,,
mesh/__init__.py CHANGED
@@ -87,12 +87,14 @@ def create_server(name: str | None = None) -> "FastMCP":
87
87
  return FastMCP(name=name)
88
88
 
89
89
 
90
- # Make decorators available as mesh.tool and mesh.agent
90
+ # Make decorators available as mesh.tool, mesh.agent, and mesh.route
91
91
  def __getattr__(name):
92
92
  if name == "tool":
93
93
  return decorators.tool
94
94
  elif name == "agent":
95
95
  return decorators.agent
96
+ elif name == "route":
97
+ return decorators.route
96
98
  elif name == "McpMeshAgent":
97
99
  return McpMeshAgent
98
100
  elif name == "McpAgent":
mesh/decorators.py CHANGED
@@ -32,7 +32,7 @@ def _trigger_debounced_processing():
32
32
  all decorators are captured before processing begins.
33
33
  """
34
34
  try:
35
- from _mcp_mesh.pipeline.startup import get_debounce_coordinator
35
+ from _mcp_mesh.pipeline.mcp_startup import get_debounce_coordinator
36
36
 
37
37
  coordinator = get_debounce_coordinator()
38
38
  coordinator.trigger_processing()
@@ -485,3 +485,145 @@ def agent(
485
485
  return target
486
486
 
487
487
  return decorator
488
+
489
+
490
+ def route(
491
+ *,
492
+ dependencies: list[dict[str, Any]] | list[str] | None = None,
493
+ **kwargs: Any,
494
+ ) -> Callable[[T], T]:
495
+ """
496
+ FastAPI route handler decorator for dependency injection.
497
+
498
+ Enables automatic dependency injection of MCP agents into FastAPI route handlers,
499
+ eliminating the need for manual MCP client management in backend services.
500
+
501
+ Args:
502
+ dependencies: Optional list of agent capabilities to inject (default: [])
503
+ **kwargs: Additional metadata for the route
504
+
505
+ Returns:
506
+ The original route handler function with dependency injection enabled
507
+
508
+ Example:
509
+ @app.post("/upload")
510
+ @mesh.route(dependencies=["pdf-extractor", "user-service"])
511
+ async def upload_resume(
512
+ request: Request,
513
+ file: UploadFile = File(...),
514
+ pdf_agent: McpAgent = None, # Injected by MCP Mesh
515
+ user_service: McpAgent = None # Injected by MCP Mesh
516
+ ):
517
+ result = await pdf_agent.extract_text_from_pdf(file)
518
+ await user_service.update_profile(user_data, result)
519
+ return {"success": True}
520
+ """
521
+
522
+ def decorator(target: T) -> T:
523
+ # Validate and process dependencies (reuse logic from tool decorator)
524
+ if dependencies is not None:
525
+ if not isinstance(dependencies, list):
526
+ raise ValueError("dependencies must be a list")
527
+
528
+ validated_dependencies = []
529
+ for dep in dependencies:
530
+ if isinstance(dep, str):
531
+ # Simple string dependency
532
+ validated_dependencies.append({
533
+ "capability": dep,
534
+ "tags": [],
535
+ })
536
+ elif isinstance(dep, dict):
537
+ # Complex dependency with metadata
538
+ if "capability" not in dep:
539
+ raise ValueError("dependency must have 'capability' field")
540
+ if not isinstance(dep["capability"], str):
541
+ raise ValueError("dependency capability must be a string")
542
+
543
+ # Validate optional dependency fields
544
+ dep_tags = dep.get("tags", [])
545
+ if not isinstance(dep_tags, list):
546
+ raise ValueError("dependency tags must be a list")
547
+ for tag in dep_tags:
548
+ if not isinstance(tag, str):
549
+ raise ValueError("all dependency tags must be strings")
550
+
551
+ dep_version = dep.get("version")
552
+ if dep_version is not None and not isinstance(dep_version, str):
553
+ raise ValueError("dependency version must be a string")
554
+
555
+ dependency_dict = {
556
+ "capability": dep["capability"],
557
+ "tags": dep_tags,
558
+ }
559
+ if dep_version is not None:
560
+ dependency_dict["version"] = dep_version
561
+ validated_dependencies.append(dependency_dict)
562
+ else:
563
+ raise ValueError("dependencies must be strings or dictionaries")
564
+ else:
565
+ validated_dependencies = []
566
+
567
+ # Build route metadata
568
+ metadata = {
569
+ "dependencies": validated_dependencies,
570
+ "description": getattr(target, "__doc__", None),
571
+ **kwargs,
572
+ }
573
+
574
+ # Store metadata on function
575
+ target._mesh_route_metadata = metadata
576
+
577
+ # Register with DecoratorRegistry using custom decorator type
578
+ DecoratorRegistry.register_custom_decorator("mesh_route", target, metadata)
579
+
580
+ logger.debug(
581
+ f"🔍 Route '{target.__name__}' registered with {len(validated_dependencies)} dependencies"
582
+ )
583
+
584
+ try:
585
+ # Import here to avoid circular imports
586
+ from _mcp_mesh.engine.dependency_injector import get_global_injector
587
+
588
+ # Extract dependency names for injector
589
+ dependency_names = [dep["capability"] for dep in validated_dependencies]
590
+
591
+ # Log the original function pointer
592
+ logger.debug(f"🔸 ORIGINAL route function pointer: {target} at {hex(id(target))}")
593
+
594
+ injector = get_global_injector()
595
+ wrapped = injector.create_injection_wrapper(target, dependency_names)
596
+
597
+ # Log the wrapper function pointer
598
+ logger.debug(
599
+ f"🔹 WRAPPER route function pointer: {wrapped} at {hex(id(wrapped))}"
600
+ )
601
+
602
+ # Preserve metadata on wrapper
603
+ wrapped._mesh_route_metadata = metadata
604
+
605
+ # Store the wrapper on the original function for reference
606
+ target._mesh_injection_wrapper = wrapped
607
+
608
+ # Also store a flag on the wrapper itself so route integration can detect it
609
+ wrapped._mesh_is_injection_wrapper = True
610
+
611
+ # Return the wrapped function - FastAPI will register this wrapper when it runs
612
+ logger.debug(f"✅ Returning injection wrapper for route '{target.__name__}'")
613
+ logger.debug(f"🔹 Returning WRAPPER: {wrapped} at {hex(id(wrapped))}")
614
+
615
+ # Trigger debounced processing before returning
616
+ _trigger_debounced_processing()
617
+ return wrapped
618
+
619
+ except Exception as e:
620
+ # Log but don't fail - graceful degradation
621
+ logger.error(
622
+ f"Route dependency injection setup failed for {target.__name__}: {e}"
623
+ )
624
+
625
+ # Fallback: return original function and trigger processing
626
+ _trigger_debounced_processing()
627
+ return target
628
+
629
+ return decorator