mcp-mesh 0.7.21__py3-none-any.whl → 0.8.0__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 (124) hide show
  1. _mcp_mesh/__init__.py +1 -1
  2. _mcp_mesh/engine/dependency_injector.py +13 -15
  3. _mcp_mesh/engine/http_wrapper.py +69 -10
  4. _mcp_mesh/engine/mesh_llm_agent.py +29 -10
  5. _mcp_mesh/engine/mesh_llm_agent_injector.py +77 -41
  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/signature_analyzer.py +58 -68
  14. _mcp_mesh/engine/unified_mcp_proxy.py +19 -35
  15. _mcp_mesh/pipeline/__init__.py +9 -20
  16. _mcp_mesh/pipeline/api_heartbeat/__init__.py +12 -7
  17. _mcp_mesh/pipeline/api_heartbeat/api_lifespan_integration.py +23 -49
  18. _mcp_mesh/pipeline/api_heartbeat/rust_api_heartbeat.py +429 -0
  19. _mcp_mesh/pipeline/api_startup/api_pipeline.py +7 -9
  20. _mcp_mesh/pipeline/api_startup/api_server_setup.py +91 -70
  21. _mcp_mesh/pipeline/api_startup/fastapi_discovery.py +22 -23
  22. _mcp_mesh/pipeline/api_startup/middleware_integration.py +32 -24
  23. _mcp_mesh/pipeline/api_startup/route_collection.py +2 -4
  24. _mcp_mesh/pipeline/mcp_heartbeat/__init__.py +5 -17
  25. _mcp_mesh/pipeline/mcp_heartbeat/rust_heartbeat.py +710 -0
  26. _mcp_mesh/pipeline/mcp_startup/__init__.py +2 -5
  27. _mcp_mesh/pipeline/mcp_startup/configuration.py +1 -1
  28. _mcp_mesh/pipeline/mcp_startup/fastapiserver_setup.py +31 -8
  29. _mcp_mesh/pipeline/mcp_startup/heartbeat_loop.py +6 -7
  30. _mcp_mesh/pipeline/mcp_startup/startup_orchestrator.py +23 -11
  31. _mcp_mesh/pipeline/mcp_startup/startup_pipeline.py +3 -8
  32. _mcp_mesh/pipeline/shared/mesh_pipeline.py +0 -2
  33. _mcp_mesh/reload.py +1 -3
  34. _mcp_mesh/shared/__init__.py +2 -8
  35. _mcp_mesh/shared/config_resolver.py +124 -80
  36. _mcp_mesh/shared/defaults.py +89 -14
  37. _mcp_mesh/shared/fastapi_middleware_manager.py +149 -91
  38. _mcp_mesh/shared/host_resolver.py +8 -46
  39. _mcp_mesh/shared/server_discovery.py +115 -86
  40. _mcp_mesh/shared/simple_shutdown.py +44 -86
  41. _mcp_mesh/tracing/execution_tracer.py +2 -6
  42. _mcp_mesh/tracing/redis_metadata_publisher.py +24 -79
  43. _mcp_mesh/tracing/trace_context_helper.py +3 -13
  44. _mcp_mesh/tracing/utils.py +29 -15
  45. _mcp_mesh/utils/fastmcp_schema_extractor.py +5 -4
  46. {mcp_mesh-0.7.21.dist-info → mcp_mesh-0.8.0.dist-info}/METADATA +7 -5
  47. mcp_mesh-0.8.0.dist-info/RECORD +85 -0
  48. mesh/__init__.py +12 -1
  49. mesh/decorators.py +248 -33
  50. mesh/helpers.py +52 -0
  51. mesh/types.py +40 -13
  52. _mcp_mesh/generated/.openapi-generator/FILES +0 -50
  53. _mcp_mesh/generated/.openapi-generator/VERSION +0 -1
  54. _mcp_mesh/generated/.openapi-generator-ignore +0 -15
  55. _mcp_mesh/generated/mcp_mesh_registry_client/__init__.py +0 -90
  56. _mcp_mesh/generated/mcp_mesh_registry_client/api/__init__.py +0 -6
  57. _mcp_mesh/generated/mcp_mesh_registry_client/api/agents_api.py +0 -1088
  58. _mcp_mesh/generated/mcp_mesh_registry_client/api/health_api.py +0 -764
  59. _mcp_mesh/generated/mcp_mesh_registry_client/api/tracing_api.py +0 -303
  60. _mcp_mesh/generated/mcp_mesh_registry_client/api_client.py +0 -798
  61. _mcp_mesh/generated/mcp_mesh_registry_client/api_response.py +0 -21
  62. _mcp_mesh/generated/mcp_mesh_registry_client/configuration.py +0 -577
  63. _mcp_mesh/generated/mcp_mesh_registry_client/exceptions.py +0 -217
  64. _mcp_mesh/generated/mcp_mesh_registry_client/models/__init__.py +0 -55
  65. _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_info.py +0 -158
  66. _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_metadata.py +0 -126
  67. _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_metadata_dependencies_inner.py +0 -139
  68. _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_metadata_dependencies_inner_one_of.py +0 -92
  69. _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_registration.py +0 -103
  70. _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_registration_metadata.py +0 -136
  71. _mcp_mesh/generated/mcp_mesh_registry_client/models/agents_list_response.py +0 -100
  72. _mcp_mesh/generated/mcp_mesh_registry_client/models/capability_info.py +0 -107
  73. _mcp_mesh/generated/mcp_mesh_registry_client/models/decorator_agent_metadata.py +0 -112
  74. _mcp_mesh/generated/mcp_mesh_registry_client/models/decorator_agent_request.py +0 -103
  75. _mcp_mesh/generated/mcp_mesh_registry_client/models/decorator_info.py +0 -105
  76. _mcp_mesh/generated/mcp_mesh_registry_client/models/dependency_info.py +0 -103
  77. _mcp_mesh/generated/mcp_mesh_registry_client/models/dependency_resolution_info.py +0 -106
  78. _mcp_mesh/generated/mcp_mesh_registry_client/models/error_response.py +0 -91
  79. _mcp_mesh/generated/mcp_mesh_registry_client/models/health_response.py +0 -103
  80. _mcp_mesh/generated/mcp_mesh_registry_client/models/heartbeat_request.py +0 -101
  81. _mcp_mesh/generated/mcp_mesh_registry_client/models/heartbeat_request_metadata.py +0 -111
  82. _mcp_mesh/generated/mcp_mesh_registry_client/models/heartbeat_response.py +0 -117
  83. _mcp_mesh/generated/mcp_mesh_registry_client/models/llm_provider.py +0 -93
  84. _mcp_mesh/generated/mcp_mesh_registry_client/models/llm_provider_resolution_info.py +0 -106
  85. _mcp_mesh/generated/mcp_mesh_registry_client/models/llm_tool_filter.py +0 -109
  86. _mcp_mesh/generated/mcp_mesh_registry_client/models/llm_tool_filter_filter_inner.py +0 -139
  87. _mcp_mesh/generated/mcp_mesh_registry_client/models/llm_tool_filter_filter_inner_one_of.py +0 -91
  88. _mcp_mesh/generated/mcp_mesh_registry_client/models/llm_tool_info.py +0 -101
  89. _mcp_mesh/generated/mcp_mesh_registry_client/models/llm_tool_resolution_info.py +0 -120
  90. _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_agent_register_metadata.py +0 -112
  91. _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_agent_registration.py +0 -129
  92. _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_registration_response.py +0 -153
  93. _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_registration_response_dependencies_resolved_value_inner.py +0 -101
  94. _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_tool_dependency_registration.py +0 -93
  95. _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_tool_register_metadata.py +0 -107
  96. _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_tool_registration.py +0 -117
  97. _mcp_mesh/generated/mcp_mesh_registry_client/models/registration_response.py +0 -119
  98. _mcp_mesh/generated/mcp_mesh_registry_client/models/resolved_llm_provider.py +0 -110
  99. _mcp_mesh/generated/mcp_mesh_registry_client/models/rich_dependency.py +0 -93
  100. _mcp_mesh/generated/mcp_mesh_registry_client/models/root_response.py +0 -92
  101. _mcp_mesh/generated/mcp_mesh_registry_client/models/standardized_dependency.py +0 -93
  102. _mcp_mesh/generated/mcp_mesh_registry_client/models/trace_event.py +0 -106
  103. _mcp_mesh/generated/mcp_mesh_registry_client/py.typed +0 -0
  104. _mcp_mesh/generated/mcp_mesh_registry_client/rest.py +0 -259
  105. _mcp_mesh/pipeline/api_heartbeat/api_dependency_resolution.py +0 -418
  106. _mcp_mesh/pipeline/api_heartbeat/api_fast_heartbeat_check.py +0 -117
  107. _mcp_mesh/pipeline/api_heartbeat/api_health_check.py +0 -140
  108. _mcp_mesh/pipeline/api_heartbeat/api_heartbeat_orchestrator.py +0 -243
  109. _mcp_mesh/pipeline/api_heartbeat/api_heartbeat_pipeline.py +0 -311
  110. _mcp_mesh/pipeline/api_heartbeat/api_heartbeat_send.py +0 -386
  111. _mcp_mesh/pipeline/api_heartbeat/api_registry_connection.py +0 -104
  112. _mcp_mesh/pipeline/mcp_heartbeat/dependency_resolution.py +0 -396
  113. _mcp_mesh/pipeline/mcp_heartbeat/fast_heartbeat_check.py +0 -116
  114. _mcp_mesh/pipeline/mcp_heartbeat/heartbeat_orchestrator.py +0 -311
  115. _mcp_mesh/pipeline/mcp_heartbeat/heartbeat_pipeline.py +0 -282
  116. _mcp_mesh/pipeline/mcp_heartbeat/heartbeat_send.py +0 -98
  117. _mcp_mesh/pipeline/mcp_heartbeat/lifespan_integration.py +0 -84
  118. _mcp_mesh/pipeline/mcp_heartbeat/llm_tools_resolution.py +0 -264
  119. _mcp_mesh/pipeline/mcp_heartbeat/registry_connection.py +0 -79
  120. _mcp_mesh/pipeline/shared/registry_connection.py +0 -80
  121. _mcp_mesh/shared/registry_client_wrapper.py +0 -515
  122. mcp_mesh-0.7.21.dist-info/RECORD +0 -152
  123. {mcp_mesh-0.7.21.dist-info → mcp_mesh-0.8.0.dist-info}/WHEEL +0 -0
  124. {mcp_mesh-0.7.21.dist-info → mcp_mesh-0.8.0.dist-info}/licenses/LICENSE +0 -0
@@ -5,21 +5,67 @@ Function signature analysis for MCP Mesh dependency injection.
5
5
  import inspect
6
6
  from typing import Any, get_type_hints
7
7
 
8
- from mesh.types import McpMeshAgent, MeshLlmAgent
8
+ from mesh.types import McpMeshTool, MeshLlmAgent
9
+
10
+ # Also support deprecated McpMeshAgent for backwards compatibility
11
+ try:
12
+ from mesh.types import McpMeshAgent
13
+ except ImportError:
14
+ McpMeshAgent = McpMeshTool # type: ignore
15
+
16
+
17
+ def _is_mesh_tool_type(param_type: Any) -> bool:
18
+ """Check if a type is McpMeshTool or deprecated McpMeshAgent."""
19
+ # Direct McpMeshTool type
20
+ if (
21
+ param_type == McpMeshTool
22
+ or (hasattr(param_type, "__name__") and param_type.__name__ == "McpMeshTool")
23
+ or (
24
+ hasattr(param_type, "__origin__")
25
+ and param_type.__origin__ == type(McpMeshTool)
26
+ )
27
+ ):
28
+ return True
29
+
30
+ # Support deprecated McpMeshAgent
31
+ if (
32
+ param_type == McpMeshAgent
33
+ or (hasattr(param_type, "__name__") and param_type.__name__ == "McpMeshAgent")
34
+ or (
35
+ hasattr(param_type, "__origin__")
36
+ and param_type.__origin__ == type(McpMeshAgent)
37
+ )
38
+ ):
39
+ return True
40
+
41
+ # Union type (e.g., McpMeshTool | None)
42
+ if hasattr(param_type, "__args__"):
43
+ for arg in param_type.__args__:
44
+ if arg == McpMeshTool or (
45
+ hasattr(arg, "__name__") and arg.__name__ == "McpMeshTool"
46
+ ):
47
+ return True
48
+ # Support deprecated McpMeshAgent in unions
49
+ if arg == McpMeshAgent or (
50
+ hasattr(arg, "__name__") and arg.__name__ == "McpMeshAgent"
51
+ ):
52
+ return True
53
+
54
+ return False
9
55
 
10
56
 
11
57
  def get_mesh_agent_positions(func: Any) -> list[int]:
12
58
  """
13
- Get positions of McpMeshAgent parameters in function signature.
59
+ Get positions of McpMeshTool parameters in function signature.
14
60
 
15
61
  Args:
16
62
  func: Function to analyze
17
63
 
18
64
  Returns:
19
- List of parameter positions (0-indexed) that are McpMeshAgent types
65
+ List of parameter positions (0-indexed) that are McpMeshTool types
20
66
 
21
67
  Example:
22
- def greet(name: str, date_svc: McpMeshAgent, file_svc: McpMeshAgent):
68
+ def greet(name: str, date_svc: McpMeshTool, file_svc: McpMeshTool):
23
69
  pass
24
70
 
25
71
  get_mesh_agent_positions(greet) → [1, 2]
@@ -32,40 +78,12 @@ def get_mesh_agent_positions(func: Any) -> list[int]:
32
78
  sig = inspect.signature(func)
33
79
  param_names = list(sig.parameters.keys())
34
80
 
35
- # Find positions of McpMeshAgent parameters
81
+ # Find positions of McpMeshTool parameters
36
82
  mesh_positions = []
37
83
  for i, param_name in enumerate(param_names):
38
84
  if param_name in type_hints:
39
85
  param_type = type_hints[param_name]
40
-
41
- # Check if it's McpMeshAgent type (handle different import paths and Union types)
42
- is_agent = False
43
-
44
- # Direct McpMeshAgent type
45
- if (
46
- param_type == McpMeshAgent
47
- or (
48
- hasattr(param_type, "__name__")
49
- and param_type.__name__ == "McpMeshAgent"
50
- )
51
- or (
52
- hasattr(param_type, "__origin__")
53
- and param_type.__origin__ == type(McpMeshAgent)
54
- )
55
- ):
56
- is_agent = True
57
-
58
- # Union type (e.g., McpMeshAgent | None)
59
- elif hasattr(param_type, "__args__"):
60
- # Check if any arg in the union is McpMeshAgent
61
- for arg in param_type.__args__:
62
- if arg == McpMeshAgent or (
63
- hasattr(arg, "__name__") and arg.__name__ == "McpMeshAgent"
64
- ):
65
- is_agent = True
66
- break
67
-
68
- if is_agent:
86
+ if _is_mesh_tool_type(param_type):
69
87
  mesh_positions.append(i)
70
88
 
71
89
  return mesh_positions
@@ -81,13 +99,13 @@ def get_mesh_agent_positions(func: Any) -> list[int]:
81
99
 
82
100
  def get_mesh_agent_parameter_names(func: Any) -> list[str]:
83
101
  """
84
- Get names of McpMeshAgent parameters in function signature.
102
+ Get names of McpMeshTool parameters in function signature.
85
103
 
86
104
  Args:
87
105
  func: Function to analyze
88
106
 
89
107
  Returns:
90
- List of parameter names that are McpMeshAgent types
108
+ List of parameter names that are McpMeshTool types
91
109
  """
92
110
  try:
93
111
  type_hints = get_type_hints(func)
@@ -97,35 +115,7 @@ def get_mesh_agent_parameter_names(func: Any) -> list[str]:
97
115
  for param_name, param in sig.parameters.items():
98
116
  if param_name in type_hints:
99
117
  param_type = type_hints[param_name]
100
-
101
- # Check if it's McpMeshAgent type (handle different import paths and Union types)
102
- is_mesh_agent = False
103
-
104
- # Direct McpMeshAgent type
105
- if (
106
- param_type == McpMeshAgent
107
- or (
108
- hasattr(param_type, "__name__")
109
- and param_type.__name__ == "McpMeshAgent"
110
- )
111
- or (
112
- hasattr(param_type, "__origin__")
113
- and param_type.__origin__ == type(McpMeshAgent)
114
- )
115
- ):
116
- is_mesh_agent = True
117
-
118
- # Union type (e.g., McpMeshAgent | None)
119
- elif hasattr(param_type, "__args__"):
120
- # Check if any arg in the union is McpMeshAgent
121
- for arg in param_type.__args__:
122
- if arg == McpMeshAgent or (
123
- hasattr(arg, "__name__") and arg.__name__ == "McpMeshAgent"
124
- ):
125
- is_mesh_agent = True
126
- break
127
-
128
- if is_mesh_agent:
118
+ if _is_mesh_tool_type(param_type):
129
119
  mesh_param_names.append(param_name)
130
120
 
131
121
  return mesh_param_names
@@ -136,7 +126,7 @@ def get_mesh_agent_parameter_names(func: Any) -> list[str]:
136
126
 
137
127
  def validate_mesh_dependencies(func: Any, dependencies: list[dict]) -> tuple[bool, str]:
138
128
  """
139
- Validate that the number of dependencies matches McpMeshAgent parameters.
129
+ Validate that the number of dependencies matches McpMeshTool parameters.
140
130
 
141
131
  Args:
142
132
  func: Function to validate
@@ -149,9 +139,9 @@ def validate_mesh_dependencies(func: Any, dependencies: list[dict]) -> tuple[boo
149
139
 
150
140
  if len(dependencies) != len(mesh_positions):
151
141
  return False, (
152
- f"Function {func.__name__} has {len(mesh_positions)} McpMeshAgent parameters "
142
+ f"Function {func.__name__} has {len(mesh_positions)} McpMeshTool parameters "
153
143
  f"but {len(dependencies)} dependencies declared. "
154
- f"Each McpMeshAgent parameter needs a corresponding dependency."
144
+ f"Each McpMeshTool parameter needs a corresponding dependency."
155
145
  )
156
146
 
157
147
  return True, ""
@@ -16,6 +16,8 @@ from ..shared.logging_config import (
16
16
  get_trace_prefix,
17
17
  )
18
18
  from ..shared.sse_parser import SSEParser
19
+ from ..tracing.context import TraceContext
20
+ from ..tracing.utils import generate_span_id
19
21
 
20
22
  logger = logging.getLogger(__name__)
21
23
 
@@ -23,7 +25,7 @@ logger = logging.getLogger(__name__)
23
25
  class UnifiedMCPProxy:
24
26
  """Unified MCP proxy using FastMCP's built-in client.
25
27
 
26
- This provides the implementation for McpMeshAgent type parameters,
28
+ This provides the implementation for McpMeshTool type parameters,
27
29
  offering all MCP protocol features using FastMCP's superior client.
28
30
 
29
31
  Features:
@@ -178,37 +180,6 @@ class UnifiedMCPProxy:
178
180
  f"FastMCP client failed: {e}"
179
181
  ) # Convert to ImportError to trigger fallback
180
182
 
181
- def _get_trace_headers(self) -> dict[str, str]:
182
- """Extract trace headers from current context for distributed tracing.
183
-
184
- Returns:
185
- Dict of trace headers or empty dict if no trace context available
186
- """
187
- try:
188
- from ..tracing.context import TraceContext
189
-
190
- current_trace = TraceContext.get_current()
191
- if current_trace:
192
- headers = {
193
- "X-Trace-ID": current_trace.trace_id,
194
- "X-Parent-Span": current_trace.span_id, # Current span becomes parent for downstream
195
- }
196
- self.logger.info(
197
- f"🔗 TRACE_PROPAGATION: Injecting headers trace_id={current_trace.trace_id[:8]}... "
198
- f"parent_span={current_trace.span_id[:8]}..."
199
- )
200
- return headers
201
- else:
202
- self.logger.warning("🔗 TRACE_PROPAGATION: No trace context available")
203
- return {}
204
-
205
- except Exception as e:
206
- # Never fail MCP calls due to tracing issues
207
- self.logger.warning(
208
- f"🔗 TRACE_PROPAGATION: Exception getting trace context: {e}"
209
- )
210
- return {}
211
-
212
183
  def _configure_from_kwargs(self):
213
184
  """Auto-configure proxy settings from kwargs."""
214
185
  # Basic configuration
@@ -433,6 +404,19 @@ class UnifiedMCPProxy:
433
404
  # Get trace prefix if available
434
405
  tp = get_trace_prefix()
435
406
 
407
+ # Inject trace context into arguments for downstream agents
408
+ # This is the fallback mechanism for agents that can't access HTTP headers (e.g., TypeScript)
409
+ args_with_trace = dict(arguments) if arguments else {}
410
+ current_trace = TraceContext.get_current()
411
+ if current_trace:
412
+ # Use current function's span_id as parent for downstream call
413
+ # Don't generate a new span - that creates unpublished "ghost" spans that break the tree
414
+ args_with_trace["_trace_id"] = current_trace.trace_id
415
+ args_with_trace["_parent_span"] = current_trace.span_id
416
+ self.logger.debug(
417
+ f"{tp}🔗 Injecting trace context: trace_id={current_trace.trace_id[:8]}..., parent_span={current_trace.span_id[:8]}..."
418
+ )
419
+
436
420
  # Log cross-agent call - summary line
437
421
  arg_keys = list(arguments.keys()) if arguments else []
438
422
  self.logger.debug(
@@ -451,7 +435,7 @@ class UnifiedMCPProxy:
451
435
  async with client_instance as client:
452
436
 
453
437
  # Use FastMCP's call_tool which returns CallToolResult object
454
- result = await client.call_tool(name, arguments or {})
438
+ result = await client.call_tool(name, args_with_trace)
455
439
 
456
440
  # Calculate performance metrics
457
441
  end_time = time.time()
@@ -479,12 +463,12 @@ class UnifiedMCPProxy:
479
463
  self.logger.warning(
480
464
  f"FastMCP Client not available: {e}, falling back to HTTP"
481
465
  )
482
- return await self._fallback_http_call(name, arguments)
466
+ return await self._fallback_http_call(name, args_with_trace)
483
467
  except Exception as e:
484
468
  self.logger.warning(f"FastMCP Client failed: {e}, falling back to HTTP")
485
469
  # Try HTTP fallback
486
470
  try:
487
- result = await self._fallback_http_call(name, arguments)
471
+ result = await self._fallback_http_call(name, args_with_trace)
488
472
  return result
489
473
  except Exception as fallback_error:
490
474
  raise RuntimeError(
@@ -2,31 +2,22 @@
2
2
  MCP Mesh Pipeline Architecture
3
3
 
4
4
  This module provides a clean, explicit pipeline-based architecture for processing
5
- decorators and managing the mesh agent lifecycle. This replaces the scattered
6
- async processing with a clear, sequential flow that can be easily tested and debugged.
5
+ decorators and managing the mesh agent lifecycle. The Rust core handles registry
6
+ communication including heartbeats, dependency resolution, and deregistration.
7
7
 
8
8
  Key Components:
9
9
  - MeshPipeline: Main orchestrator that executes steps in sequence
10
10
  - PipelineStep: Interface for individual processing steps
11
11
  - PipelineResult: Result container with status and context
12
- - Built-in steps for common operations (collection, config, heartbeat, etc.)
12
+ - Built-in steps for common operations (collection, config, etc.)
13
13
  """
14
14
 
15
- from .mcp_heartbeat import (
16
- DependencyResolutionStep,
17
- HeartbeatSendStep,
18
- RegistryConnectionStep,
19
- )
15
+ from .mcp_heartbeat import rust_heartbeat_task
16
+ from .mcp_startup import (ConfigurationStep, DecoratorCollectionStep,
17
+ FastAPIServerSetupStep, FastMCPServerDiscoveryStep,
18
+ HeartbeatLoopStep, HeartbeatPreparationStep,
19
+ StartupPipeline)
20
20
  from .shared import MeshPipeline, PipelineResult, PipelineStatus, PipelineStep
21
- from .mcp_startup import (
22
- ConfigurationStep,
23
- DecoratorCollectionStep,
24
- FastAPIServerSetupStep,
25
- FastMCPServerDiscoveryStep,
26
- HeartbeatLoopStep,
27
- HeartbeatPreparationStep,
28
- StartupPipeline,
29
- )
30
21
 
31
22
  __all__ = [
32
23
  "MeshPipeline",
@@ -39,8 +30,6 @@ __all__ = [
39
30
  "FastMCPServerDiscoveryStep",
40
31
  "HeartbeatLoopStep",
41
32
  "HeartbeatPreparationStep",
42
- "RegistryConnectionStep",
43
- "HeartbeatSendStep",
44
- "DependencyResolutionStep",
45
33
  "StartupPipeline",
34
+ "rust_heartbeat_task",
46
35
  ]
@@ -3,14 +3,19 @@ API heartbeat pipeline for FastAPI integration.
3
3
 
4
4
  Provides periodic service registration and health monitoring
5
5
  for FastAPI applications using @mesh.route decorators.
6
+
7
+ Uses Rust core for registry communication, dependency resolution,
8
+ and deregistration.
6
9
  """
7
10
 
8
- from .api_heartbeat_pipeline import APIHeartbeatPipeline
9
- from .api_heartbeat_orchestrator import APIHeartbeatOrchestrator
10
- from .api_dependency_resolution import APIDependencyResolutionStep
11
+ from .api_lifespan_integration import (api_heartbeat_lifespan_task,
12
+ create_api_lifespan_handler,
13
+ integrate_api_heartbeat_with_fastapi)
14
+ from .rust_api_heartbeat import rust_api_heartbeat_task
11
15
 
12
16
  __all__ = [
13
- "APIHeartbeatPipeline",
14
- "APIHeartbeatOrchestrator",
15
- "APIDependencyResolutionStep",
16
- ]
17
+ "api_heartbeat_lifespan_task",
18
+ "create_api_lifespan_handler",
19
+ "integrate_api_heartbeat_with_fastapi",
20
+ "rust_api_heartbeat_task",
21
+ ]
@@ -1,8 +1,10 @@
1
1
  """
2
2
  FastAPI lifespan integration for API heartbeat pipeline.
3
3
 
4
- Handles the execution of API heartbeat pipeline as a background task
4
+ Handles the execution of API heartbeat as a background task
5
5
  during FastAPI application lifespan for @mesh.route decorator services.
6
+
7
+ Uses the Rust core for registry communication.
6
8
  """
7
9
 
8
10
  import asyncio
@@ -14,65 +16,35 @@ logger = logging.getLogger(__name__)
14
16
 
15
17
  async def api_heartbeat_lifespan_task(heartbeat_config: dict[str, Any]) -> None:
16
18
  """
17
- API heartbeat task that runs in FastAPI lifespan using pipeline architecture.
19
+ API heartbeat task that runs in FastAPI lifespan.
20
+
21
+ Uses Rust-backed heartbeat for registry communication.
18
22
 
19
23
  Args:
20
- heartbeat_config: Configuration containing service_id, interval,
24
+ heartbeat_config: Configuration containing service_id, interval,
21
25
  and context for API heartbeat execution
22
26
  """
23
- service_id = heartbeat_config["service_id"]
24
- interval = heartbeat_config["interval"] # Already validated by get_config_value in setup
25
- context = heartbeat_config["context"]
27
+ service_id = heartbeat_config.get("service_id", "unknown-api-service")
26
28
  standalone_mode = heartbeat_config.get("standalone_mode", False)
27
29
 
28
30
  # Check if running in standalone mode
29
31
  if standalone_mode:
30
32
  logger.info(
31
- f"💓 Starting API heartbeat pipeline in standalone mode for service '{service_id}' "
33
+ f"💓 API heartbeat in standalone mode for service '{service_id}' "
32
34
  f"(no registry communication)"
33
35
  )
34
- return # For now, skip heartbeat in standalone mode
35
-
36
- # Create API heartbeat orchestrator for pipeline execution
37
- from .api_heartbeat_orchestrator import APIHeartbeatOrchestrator
36
+ return
38
37
 
39
- api_heartbeat_orchestrator = APIHeartbeatOrchestrator()
38
+ # Use Rust-backed heartbeat
39
+ from .rust_api_heartbeat import rust_api_heartbeat_task
40
40
 
41
- logger.info(f"💓 Starting API heartbeat pipeline task for service '{service_id}'")
42
-
43
- try:
44
- while True:
45
- try:
46
- # Execute API heartbeat pipeline
47
- success = await api_heartbeat_orchestrator.execute_api_heartbeat(
48
- service_id, context
49
- )
50
-
51
- if not success:
52
- # Log failure but continue to next cycle (pipeline handles detailed logging)
53
- logger.debug(
54
- f"💔 API heartbeat pipeline failed for service '{service_id}' - "
55
- f"continuing to next cycle"
56
- )
57
-
58
- except Exception as e:
59
- # Log pipeline execution error but continue to next cycle for resilience
60
- logger.error(
61
- f"❌ API heartbeat pipeline execution error for service '{service_id}': {e}"
62
- )
63
- # Continue to next cycle - heartbeat should be resilient
64
-
65
- # Wait for next heartbeat interval
66
- await asyncio.sleep(interval)
67
-
68
- except asyncio.CancelledError:
69
- logger.info(f"🛑 API heartbeat pipeline task cancelled for service '{service_id}'")
70
- raise
41
+ logger.info(f"💓 Using Rust-backed heartbeat for API service '{service_id}'")
42
+ await rust_api_heartbeat_task(heartbeat_config)
71
43
 
72
44
 
73
45
  def create_api_lifespan_handler(heartbeat_config: dict[str, Any]) -> Any:
74
46
  """
75
- Create a FastAPI lifespan context manager that runs API heartbeat pipeline.
47
+ Create a FastAPI lifespan context manager that runs API heartbeat.
76
48
 
77
49
  Args:
78
50
  heartbeat_config: Configuration for API heartbeat execution
@@ -100,11 +72,13 @@ def create_api_lifespan_handler(heartbeat_config: dict[str, Any]) -> Any:
100
72
  # Cleanup: cancel heartbeat task
101
73
  logger.info(f"🛑 Shutting down FastAPI lifespan for service '{service_id}'")
102
74
  heartbeat_task.cancel()
103
-
75
+
104
76
  try:
105
77
  await heartbeat_task
106
78
  except asyncio.CancelledError:
107
- logger.info(f"✅ API heartbeat task cancelled for service '{service_id}'")
79
+ logger.info(
80
+ f"✅ API heartbeat task cancelled for service '{service_id}'"
81
+ )
108
82
 
109
83
  return api_lifespan
110
84
 
@@ -113,18 +87,18 @@ def integrate_api_heartbeat_with_fastapi(
113
87
  fastapi_app: Any, heartbeat_config: dict[str, Any]
114
88
  ) -> None:
115
89
  """
116
- Integrate API heartbeat pipeline with FastAPI lifespan events.
90
+ Integrate API heartbeat with FastAPI lifespan events.
117
91
 
118
92
  Args:
119
93
  fastapi_app: FastAPI application instance
120
94
  heartbeat_config: Configuration for heartbeat execution
121
95
  """
122
96
  service_id = heartbeat_config.get("service_id", "unknown")
123
-
97
+
124
98
  try:
125
99
  # Check if FastAPI app already has a lifespan handler
126
100
  existing_lifespan = getattr(fastapi_app, "router.lifespan_context", None)
127
-
101
+
128
102
  if existing_lifespan is not None:
129
103
  logger.warning(
130
104
  f"⚠️ FastAPI app already has lifespan handler - "
@@ -144,4 +118,4 @@ def integrate_api_heartbeat_with_fastapi(
144
118
  f"❌ Failed to integrate API heartbeat with FastAPI lifespan "
145
119
  f"for service '{service_id}': {e}"
146
120
  )
147
- raise
121
+ raise