mcp-mesh 0.5.0__tar.gz → 0.5.1__tar.gz

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 (118) hide show
  1. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/PKG-INFO +1 -1
  2. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/__init__.py +1 -1
  3. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/engine/__init__.py +12 -1
  4. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/engine/async_mcp_client.py +2 -2
  5. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/engine/decorator_registry.py +98 -8
  6. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/engine/dependency_injector.py +105 -14
  7. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/engine/full_mcp_proxy.py +4 -4
  8. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/engine/http_wrapper.py +9 -20
  9. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/engine/mcp_client_proxy.py +1 -1
  10. mcp_mesh-0.5.1/_mcp_mesh/engine/unified_mcp_proxy.py +813 -0
  11. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/api_heartbeat/api_dependency_resolution.py +12 -21
  12. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/api_heartbeat/api_registry_connection.py +12 -5
  13. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/api_startup/api_server_setup.py +95 -20
  14. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/mcp_heartbeat/dependency_resolution.py +19 -183
  15. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/tracing/agent_context_helper.py +1 -1
  16. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/tracing/execution_tracer.py +41 -0
  17. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/mesh/types.py +109 -48
  18. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/pyproject.toml +4 -4
  19. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/.gitignore +0 -0
  20. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/LICENSE +0 -0
  21. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/README.md +0 -0
  22. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/engine/self_dependency_proxy.py +0 -0
  23. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/engine/session_aware_client.py +0 -0
  24. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/engine/session_manager.py +0 -0
  25. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/engine/signature_analyzer.py +0 -0
  26. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/.openapi-generator/FILES +0 -0
  27. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/.openapi-generator/VERSION +0 -0
  28. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/.openapi-generator-ignore +0 -0
  29. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/__init__.py +0 -0
  30. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/api/__init__.py +0 -0
  31. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/api/agents_api.py +0 -0
  32. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/api/health_api.py +0 -0
  33. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/api/tracing_api.py +0 -0
  34. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/api_client.py +0 -0
  35. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/api_response.py +0 -0
  36. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/configuration.py +0 -0
  37. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/exceptions.py +0 -0
  38. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/__init__.py +0 -0
  39. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/agent_info.py +0 -0
  40. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/agent_metadata.py +0 -0
  41. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/agent_metadata_dependencies_inner.py +0 -0
  42. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/agent_metadata_dependencies_inner_one_of.py +0 -0
  43. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/agent_registration.py +0 -0
  44. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/agent_registration_metadata.py +0 -0
  45. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/agents_list_response.py +0 -0
  46. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/capability_info.py +0 -0
  47. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/decorator_agent_metadata.py +0 -0
  48. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/decorator_agent_request.py +0 -0
  49. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/decorator_info.py +0 -0
  50. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/dependency_info.py +0 -0
  51. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/error_response.py +0 -0
  52. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/health_response.py +0 -0
  53. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/heartbeat_request.py +0 -0
  54. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/heartbeat_request_metadata.py +0 -0
  55. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/heartbeat_response.py +0 -0
  56. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_agent_register_metadata.py +0 -0
  57. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_agent_registration.py +0 -0
  58. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_registration_response.py +0 -0
  59. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_registration_response_dependencies_resolved_value_inner.py +0 -0
  60. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_tool_dependency_registration.py +0 -0
  61. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_tool_register_metadata.py +0 -0
  62. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_tool_registration.py +0 -0
  63. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/registration_response.py +0 -0
  64. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/rich_dependency.py +0 -0
  65. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/root_response.py +0 -0
  66. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/standardized_dependency.py +0 -0
  67. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/models/trace_event.py +0 -0
  68. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/py.typed +0 -0
  69. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/generated/mcp_mesh_registry_client/rest.py +0 -0
  70. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/__init__.py +0 -0
  71. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/api_heartbeat/__init__.py +0 -0
  72. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/api_heartbeat/api_fast_heartbeat_check.py +0 -0
  73. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/api_heartbeat/api_health_check.py +0 -0
  74. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/api_heartbeat/api_heartbeat_orchestrator.py +0 -0
  75. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/api_heartbeat/api_heartbeat_pipeline.py +0 -0
  76. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/api_heartbeat/api_heartbeat_send.py +0 -0
  77. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/api_heartbeat/api_lifespan_integration.py +0 -0
  78. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/api_startup/__init__.py +0 -0
  79. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/api_startup/api_pipeline.py +0 -0
  80. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/api_startup/fastapi_discovery.py +0 -0
  81. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/api_startup/route_collection.py +0 -0
  82. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/api_startup/route_integration.py +0 -0
  83. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/mcp_heartbeat/__init__.py +0 -0
  84. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/mcp_heartbeat/fast_heartbeat_check.py +0 -0
  85. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/mcp_heartbeat/heartbeat_orchestrator.py +0 -0
  86. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/mcp_heartbeat/heartbeat_pipeline.py +0 -0
  87. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/mcp_heartbeat/heartbeat_send.py +0 -0
  88. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/mcp_heartbeat/lifespan_integration.py +0 -0
  89. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/mcp_heartbeat/registry_connection.py +0 -0
  90. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/mcp_startup/__init__.py +0 -0
  91. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/mcp_startup/configuration.py +0 -0
  92. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/mcp_startup/decorator_collection.py +0 -0
  93. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/mcp_startup/fastapiserver_setup.py +0 -0
  94. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/mcp_startup/fastmcpserver_discovery.py +0 -0
  95. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/mcp_startup/heartbeat_loop.py +0 -0
  96. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/mcp_startup/heartbeat_preparation.py +0 -0
  97. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/mcp_startup/startup_orchestrator.py +0 -0
  98. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/mcp_startup/startup_pipeline.py +0 -0
  99. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/shared/__init__.py +0 -0
  100. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/shared/base_step.py +0 -0
  101. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/shared/mesh_pipeline.py +0 -0
  102. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/shared/pipeline_types.py +0 -0
  103. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/pipeline/shared/registry_connection.py +0 -0
  104. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/shared/__init__.py +0 -0
  105. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/shared/config_resolver.py +0 -0
  106. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/shared/content_extractor.py +0 -0
  107. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/shared/defaults.py +0 -0
  108. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/shared/fast_heartbeat_status.py +0 -0
  109. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/shared/host_resolver.py +0 -0
  110. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/shared/logging_config.py +0 -0
  111. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/shared/registry_client_wrapper.py +0 -0
  112. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/shared/sse_parser.py +0 -0
  113. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/shared/support_types.py +0 -0
  114. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/tracing/context.py +0 -0
  115. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/tracing/redis_metadata_publisher.py +0 -0
  116. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/_mcp_mesh/tracing/trace_context_helper.py +0 -0
  117. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/mesh/__init__.py +0 -0
  118. {mcp_mesh-0.5.0 → mcp_mesh-0.5.1}/mesh/decorators.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-mesh
3
- Version: 0.5.0
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
@@ -31,7 +31,7 @@ from .engine.decorator_registry import (
31
31
  get_decorator_stats,
32
32
  )
33
33
 
34
- __version__ = "0.5.0"
34
+ __version__ = "0.5.1"
35
35
 
36
36
  # Store reference to runtime processor if initialized
37
37
  _runtime_processor = None
@@ -17,12 +17,15 @@ __all__ = [
17
17
  # Dependency injection
18
18
  "DependencyInjector",
19
19
  "get_global_injector",
20
- # MCP client proxies
20
+ # MCP client proxies (legacy)
21
21
  "MCPClientProxy",
22
22
  "EnhancedMCPClientProxy",
23
23
  "FullMCPProxy",
24
24
  "EnhancedFullMCPProxy",
25
25
  "AsyncMCPClient",
26
+ # Unified MCP proxy (recommended)
27
+ "UnifiedMCPProxy",
28
+ "EnhancedUnifiedMCPProxy",
26
29
  # Self-dependency proxy
27
30
  "SelfDependencyProxy",
28
31
  # Decorator registry
@@ -76,6 +79,14 @@ def __getattr__(name):
76
79
  from .async_mcp_client import AsyncMCPClient
77
80
 
78
81
  return AsyncMCPClient
82
+ elif name == "UnifiedMCPProxy":
83
+ from .unified_mcp_proxy import UnifiedMCPProxy
84
+
85
+ return UnifiedMCPProxy
86
+ elif name == "EnhancedUnifiedMCPProxy":
87
+ from .unified_mcp_proxy import EnhancedUnifiedMCPProxy
88
+
89
+ return EnhancedUnifiedMCPProxy
79
90
  # Self-dependency proxy
80
91
  elif name == "SelfDependencyProxy":
81
92
  from .self_dependency_proxy import SelfDependencyProxy
@@ -39,7 +39,7 @@ class AsyncMCPClient:
39
39
 
40
40
  async def _make_request(self, payload: dict) -> dict:
41
41
  """Make async HTTP request to MCP endpoint."""
42
- url = f"{self.endpoint}/mcp/"
42
+ url = f"{self.endpoint}/mcp"
43
43
 
44
44
  try:
45
45
  # Use httpx for proper async HTTP requests (better threading support than aiohttp)
@@ -91,7 +91,7 @@ class AsyncMCPClient:
91
91
 
92
92
  async def _make_request_sync(self, payload: dict) -> dict:
93
93
  """Fallback sync HTTP request using urllib."""
94
- url = f"{self.endpoint}/mcp/"
94
+ url = f"{self.endpoint}/mcp"
95
95
  data = json.dumps(payload).encode("utf-8")
96
96
 
97
97
  # Create request
@@ -277,6 +277,28 @@ class DecoratorRegistry:
277
277
  # Cache for resolved agent configuration to avoid repeated work
278
278
  _cached_agent_config: Optional[dict[str, Any]] = None
279
279
 
280
+ @classmethod
281
+ def update_agent_config(cls, updates: dict[str, Any]) -> None:
282
+ """
283
+ Update the cached agent configuration with new values.
284
+
285
+ This is useful for API services that generate their agent ID
286
+ during pipeline execution and need to store it for telemetry.
287
+
288
+ Args:
289
+ updates: Dictionary of config values to update
290
+ """
291
+ if cls._cached_agent_config is None:
292
+ # Initialize with current resolved config if not cached yet
293
+ cls._cached_agent_config = cls.get_resolved_agent_config().copy()
294
+
295
+ # Update with new values
296
+ cls._cached_agent_config.update(updates)
297
+
298
+ logger.debug(
299
+ f"🔧 Updated cached agent configuration with: {updates}"
300
+ )
301
+
280
302
  @classmethod
281
303
  def get_resolved_agent_config(cls) -> dict[str, Any]:
282
304
  """
@@ -288,11 +310,14 @@ class DecoratorRegistry:
288
310
  Returns:
289
311
  dict: Pre-resolved configuration with consistent agent_id
290
312
  """
291
- # Return cached configuration if available
292
- if cls._cached_agent_config is not None:
313
+ # Step 1: Check if cached configuration already has agent_id (from API pipeline)
314
+ if cls._cached_agent_config is not None and cls._cached_agent_config.get('agent_id'):
315
+ logger.debug(
316
+ f"🔧 Using cached agent configuration: agent_id='{cls._cached_agent_config.get('agent_id')}'"
317
+ )
293
318
  return cls._cached_agent_config
294
319
 
295
- # If we have explicit @mesh.agent configuration, use it
320
+ # Step 2: If we have explicit @mesh.agent configuration, use it
296
321
  if cls._mesh_agents:
297
322
  for agent_name, decorated_func in cls._mesh_agents.items():
298
323
  # Return the already-resolved configuration from decorator
@@ -306,14 +331,23 @@ class DecoratorRegistry:
306
331
  )
307
332
  return resolved_config
308
333
 
309
- # Fallback: Synthetic defaults when no @mesh.agent decorator exists
310
- # This happens when only @mesh.tool decorators are used
311
- from mesh.decorators import _get_or_create_agent_id
312
-
334
+ # Step 3: Fallback to synthetic defaults when no @mesh.agent decorator exists
335
+ # This happens when only @mesh.tool decorators are used and no cached agent_id
313
336
  from ..shared.config_resolver import ValidationRule, get_config_value
314
337
  from ..shared.defaults import MeshDefaults
315
338
 
316
- agent_id = _get_or_create_agent_id()
339
+ # Check if we're in an API context (have mesh_route decorators)
340
+ mesh_routes = cls.get_all_by_type("mesh_route")
341
+ is_api_context = len(mesh_routes) > 0
342
+
343
+ if is_api_context:
344
+ # Use API service ID generation logic for consistency
345
+ agent_id = cls._generate_api_service_id_fallback()
346
+ else:
347
+ # Use standard MCP agent ID generation
348
+ from mesh.decorators import _get_or_create_agent_id
349
+ agent_id = _get_or_create_agent_id()
350
+
317
351
  fallback_config = {
318
352
  "name": None,
319
353
  "version": get_config_value(
@@ -368,6 +402,62 @@ class DecoratorRegistry:
368
402
  )
369
403
  return fallback_config
370
404
 
405
+ @classmethod
406
+ def _generate_api_service_id_fallback(cls) -> str:
407
+ """
408
+ Generate API service ID as fallback using same priority logic as API pipeline.
409
+
410
+ Priority order:
411
+ 1. MCP_MESH_API_NAME environment variable
412
+ 2. MCP_MESH_AGENT_NAME environment variable (fallback)
413
+ 3. Default to "api-{uuid8}"
414
+
415
+ Returns:
416
+ Generated service ID with UUID suffix matching API service format
417
+ """
418
+ import uuid
419
+
420
+ from ..shared.config_resolver import ValidationRule, get_config_value
421
+
422
+ # Check for API-specific environment variable first (same as API pipeline)
423
+ api_name = get_config_value(
424
+ "MCP_MESH_API_NAME",
425
+ default=None,
426
+ rule=ValidationRule.STRING_RULE,
427
+ )
428
+
429
+ # Fallback to general agent name env var
430
+ if not api_name:
431
+ api_name = get_config_value(
432
+ "MCP_MESH_AGENT_NAME",
433
+ default=None,
434
+ rule=ValidationRule.STRING_RULE,
435
+ )
436
+
437
+ # Clean the service name if provided
438
+ if api_name:
439
+ cleaned_name = api_name.lower().replace(" ", "-").replace("_", "-")
440
+ cleaned_name = "-".join(part for part in cleaned_name.split("-") if part)
441
+ else:
442
+ cleaned_name = ""
443
+
444
+ # Generate UUID suffix
445
+ uuid_suffix = str(uuid.uuid4())[:8]
446
+
447
+ # Apply same naming logic as API pipeline
448
+ if not cleaned_name:
449
+ # No name provided: default to "api-{uuid8}"
450
+ service_id = f"api-{uuid_suffix}"
451
+ elif "api" in cleaned_name.lower():
452
+ # Name already contains "api": use "{name}-{uuid8}"
453
+ service_id = f"{cleaned_name}-{uuid_suffix}"
454
+ else:
455
+ # Name doesn't contain "api": use "{name}-api-{uuid8}"
456
+ service_id = f"{cleaned_name}-api-{uuid_suffix}"
457
+
458
+ logger.debug(f"Generated fallback API service ID: '{service_id}' from env name: '{api_name}'")
459
+ return service_id
460
+
371
461
  @classmethod
372
462
  def get_all_agents(cls) -> list[tuple[Any, dict[str, Any]]]:
373
463
  """
@@ -288,9 +288,45 @@ class DependencyInjector:
288
288
  f"🔧 No injection positions for {func.__name__}, creating minimal wrapper for tracking"
289
289
  )
290
290
 
291
- @functools.wraps(func)
292
- def minimal_wrapper(*args, **kwargs):
293
- return func(*args, **kwargs)
291
+ # Check if we need async wrapper for minimal case
292
+ if inspect.iscoroutinefunction(func):
293
+ @functools.wraps(func)
294
+ async def minimal_wrapper(*args, **kwargs):
295
+ # Execute with telemetry tracing even for async functions without dependencies
296
+ try:
297
+ from ..tracing.execution_tracer import ExecutionTracer
298
+
299
+ # Use ExecutionTracer for minimal async wrapper (no dependencies)
300
+ return await ExecutionTracer.trace_function_execution_async(
301
+ func, args, kwargs, [], [], 0, wrapper_logger
302
+ )
303
+ except ImportError:
304
+ # Fallback if tracing is unavailable - never fail user function
305
+ wrapper_logger.debug("🔇 Tracing unavailable, executing without telemetry")
306
+ return await func(*args, **kwargs)
307
+ except Exception as e:
308
+ # Never fail user function due to tracing errors
309
+ wrapper_logger.warning(f"⚠️ Telemetry failed, executing without: {e}")
310
+ return await func(*args, **kwargs)
311
+ else:
312
+ @functools.wraps(func)
313
+ def minimal_wrapper(*args, **kwargs):
314
+ # Execute with telemetry tracing even for functions without dependencies
315
+ try:
316
+ from ..tracing.execution_tracer import ExecutionTracer
317
+
318
+ # Use ExecutionTracer for minimal wrapper (no dependencies)
319
+ return ExecutionTracer.trace_original_function(
320
+ func, args, kwargs, wrapper_logger
321
+ )
322
+ except ImportError:
323
+ # Fallback if tracing is unavailable - never fail user function
324
+ wrapper_logger.debug("🔇 Tracing unavailable, executing without telemetry")
325
+ return func(*args, **kwargs)
326
+ except Exception as e:
327
+ # Never fail user function due to tracing errors
328
+ wrapper_logger.warning(f"⚠️ Telemetry failed, executing without: {e}")
329
+ return func(*args, **kwargs)
294
330
 
295
331
  # Add minimal metadata for compatibility
296
332
  minimal_wrapper._mesh_injected_deps = {}
@@ -390,17 +426,51 @@ class DependencyInjector:
390
426
  f"🔧 DEPENDENCY_WRAPPER: final_kwargs={final_kwargs}"
391
427
  )
392
428
 
393
- # ===== EXECUTE WITH DEPENDENCY INJECTION =====
394
- # Call the original function with injected dependencies
429
+ # ===== EXECUTE WITH TELEMETRY AND DEPENDENCY INJECTION =====
430
+ # Call the original function with telemetry tracing and injected dependencies
395
431
  original_func = func._mesh_original_func
396
432
 
397
- # Check if the function is async and handle accordingly
398
- if inspect.iscoroutinefunction(original_func):
399
- # For async functions, await the result directly
400
- result = await original_func(*args, **final_kwargs)
401
- else:
402
- # For sync functions, call directly
403
- result = original_func(*args, **final_kwargs)
433
+ # Execute with telemetry tracing
434
+ try:
435
+ from ..tracing.execution_tracer import ExecutionTracer
436
+
437
+ # Use ExecutionTracer for comprehensive telemetry
438
+ if inspect.iscoroutinefunction(original_func):
439
+ # For async functions, await the result directly
440
+ result = await ExecutionTracer.trace_function_execution_async(
441
+ original_func,
442
+ args,
443
+ final_kwargs,
444
+ dependencies,
445
+ mesh_positions,
446
+ injected_count,
447
+ wrapper_logger,
448
+ )
449
+ else:
450
+ # For sync functions, call directly
451
+ result = ExecutionTracer.trace_function_execution(
452
+ original_func,
453
+ args,
454
+ final_kwargs,
455
+ dependencies,
456
+ mesh_positions,
457
+ injected_count,
458
+ wrapper_logger,
459
+ )
460
+ except ImportError:
461
+ # Fallback if tracing is unavailable - never fail user function
462
+ wrapper_logger.debug("🔇 Tracing unavailable, executing without telemetry")
463
+ if inspect.iscoroutinefunction(original_func):
464
+ result = await original_func(*args, **final_kwargs)
465
+ else:
466
+ result = original_func(*args, **final_kwargs)
467
+ except Exception as e:
468
+ # Never fail user function due to tracing errors
469
+ wrapper_logger.warning(f"⚠️ Telemetry failed, executing without: {e}")
470
+ if inspect.iscoroutinefunction(original_func):
471
+ result = await original_func(*args, **final_kwargs)
472
+ else:
473
+ result = original_func(*args, **final_kwargs)
404
474
 
405
475
  wrapper_logger.debug(
406
476
  f"🔧 DEPENDENCY_WRAPPER: Original returned: {type(result)}"
@@ -454,8 +524,29 @@ class DependencyInjector:
454
524
  final_kwargs[param_name] = dependency
455
525
  injected_count += 1
456
526
 
457
- # Call the original function with injected dependencies
458
- return func._mesh_original_func(*args, **final_kwargs)
527
+ # ===== EXECUTE WITH TELEMETRY AND DEPENDENCY INJECTION =====
528
+ # Call the original function with telemetry tracing and injected dependencies
529
+ try:
530
+ from ..tracing.execution_tracer import ExecutionTracer
531
+
532
+ # Use ExecutionTracer for comprehensive telemetry
533
+ return ExecutionTracer.trace_function_execution(
534
+ func._mesh_original_func,
535
+ args,
536
+ final_kwargs,
537
+ dependencies,
538
+ mesh_positions,
539
+ injected_count,
540
+ wrapper_logger,
541
+ )
542
+ except ImportError:
543
+ # Fallback if tracing is unavailable - never fail user function
544
+ wrapper_logger.debug("🔇 Tracing unavailable, executing without telemetry")
545
+ return func._mesh_original_func(*args, **final_kwargs)
546
+ except Exception as e:
547
+ # Never fail user function due to tracing errors
548
+ wrapper_logger.warning(f"⚠️ Telemetry failed, executing without: {e}")
549
+ return func._mesh_original_func(*args, **final_kwargs)
459
550
 
460
551
  # Store dependency state on wrapper
461
552
  dependency_wrapper._mesh_injected_deps = {}
@@ -85,7 +85,7 @@ class FullMCPProxy(MCPClientProxy):
85
85
  try:
86
86
  import httpx
87
87
 
88
- url = f"{self.endpoint}/mcp/"
88
+ url = f"{self.endpoint}/mcp"
89
89
 
90
90
  # Build headers with trace context
91
91
  headers = {
@@ -234,7 +234,7 @@ class FullMCPProxy(MCPClientProxy):
234
234
  }
235
235
 
236
236
  # URL for MCP protocol endpoint
237
- url = f"{self.endpoint.rstrip('/')}/mcp/"
237
+ url = f"{self.endpoint.rstrip('/')}/mcp"
238
238
 
239
239
  # Add session ID to headers for session routing
240
240
  headers = {
@@ -500,7 +500,7 @@ class EnhancedFullMCPProxy(FullMCPProxy):
500
500
  # Inject trace context headers
501
501
  headers = self._inject_trace_headers(headers)
502
502
 
503
- url = f"{self.endpoint}/mcp/"
503
+ url = f"{self.endpoint}/mcp"
504
504
 
505
505
  try:
506
506
  import httpx
@@ -580,7 +580,7 @@ class EnhancedFullMCPProxy(FullMCPProxy):
580
580
  # Inject trace context headers
581
581
  headers = self._inject_trace_headers(headers)
582
582
 
583
- url = f"{self.endpoint}/mcp/"
583
+ url = f"{self.endpoint}/mcp"
584
584
 
585
585
  try:
586
586
  import httpx
@@ -395,6 +395,12 @@ class HttpMcpWrapper:
395
395
  from starlette.responses import Response
396
396
 
397
397
  class MCPSessionRoutingMiddleware(BaseHTTPMiddleware):
398
+ """Session routing middleware for MCP requests.
399
+
400
+ Handles session affinity by routing requests to appropriate pods
401
+ based on session ID. Telemetry/tracing is now handled in the
402
+ DI function wrapper for unified coverage of both MCP and API calls.
403
+ """
398
404
  def __init__(self, app, http_wrapper):
399
405
  super().__init__(app)
400
406
  self.http_wrapper = http_wrapper
@@ -402,26 +408,9 @@ class HttpMcpWrapper:
402
408
 
403
409
  async def dispatch(self, request: Request, call_next):
404
410
  # Only handle MCP requests (FastMCP app already only handles /mcp)
405
-
406
- # Extract and set trace context from headers for distributed tracing
407
- try:
408
- from ..tracing.trace_context_helper import TraceContextHelper
409
-
410
- # Use helper class for trace context extraction and setup
411
- trace_context = (
412
- await TraceContextHelper.extract_trace_context_from_request(
413
- request
414
- )
415
- )
416
- TraceContextHelper.setup_request_trace_context(
417
- trace_context, self.logger
418
- )
419
- except Exception as e:
420
- import logging
421
-
422
- logger = logging.getLogger(__name__)
423
- logger.warning(f"Failed to set trace context: {e}")
424
- pass
411
+
412
+ # Note: Telemetry/tracing now handled in DI function wrapper for unified approach
413
+ # This middleware focuses purely on session routing
425
414
 
426
415
  # Extract session ID from request
427
416
  session_id = await self.http_wrapper._extract_session_id(request)
@@ -93,7 +93,7 @@ class MCPClientProxy:
93
93
  "params": {"name": self.function_name, "arguments": kwargs},
94
94
  }
95
95
 
96
- url = f"{self.endpoint}/mcp/" # Use trailing slash to avoid 307 redirect
96
+ url = f"{self.endpoint}/mcp" # Remove trailing slash to avoid 307 redirect
97
97
  data = json.dumps(payload).encode("utf-8")
98
98
 
99
99
  # Build headers with trace context injection