mcp-mesh 0.4.1__tar.gz → 0.5.0__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 (117) hide show
  1. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/PKG-INFO +1 -1
  2. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/__init__.py +14 -3
  3. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/engine/async_mcp_client.py +6 -19
  4. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/engine/dependency_injector.py +161 -74
  5. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/engine/full_mcp_proxy.py +25 -20
  6. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/engine/mcp_client_proxy.py +5 -19
  7. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/.openapi-generator/FILES +2 -0
  8. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/__init__.py +2 -0
  9. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/api/__init__.py +1 -0
  10. mcp_mesh-0.5.0/_mcp_mesh/generated/mcp_mesh_registry_client/api/tracing_api.py +305 -0
  11. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/__init__.py +1 -0
  12. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/agent_info.py +10 -1
  13. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_agent_registration.py +4 -4
  14. mcp_mesh-0.5.0/_mcp_mesh/generated/mcp_mesh_registry_client/models/trace_event.py +108 -0
  15. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/pipeline/__init__.py +2 -2
  16. mcp_mesh-0.5.0/_mcp_mesh/pipeline/api_heartbeat/__init__.py +16 -0
  17. mcp_mesh-0.5.0/_mcp_mesh/pipeline/api_heartbeat/api_dependency_resolution.py +515 -0
  18. mcp_mesh-0.5.0/_mcp_mesh/pipeline/api_heartbeat/api_fast_heartbeat_check.py +117 -0
  19. mcp_mesh-0.5.0/_mcp_mesh/pipeline/api_heartbeat/api_health_check.py +140 -0
  20. mcp_mesh-0.5.0/_mcp_mesh/pipeline/api_heartbeat/api_heartbeat_orchestrator.py +247 -0
  21. mcp_mesh-0.5.0/_mcp_mesh/pipeline/api_heartbeat/api_heartbeat_pipeline.py +309 -0
  22. mcp_mesh-0.5.0/_mcp_mesh/pipeline/api_heartbeat/api_heartbeat_send.py +332 -0
  23. mcp_mesh-0.5.0/_mcp_mesh/pipeline/api_heartbeat/api_lifespan_integration.py +147 -0
  24. mcp_mesh-0.5.0/_mcp_mesh/pipeline/api_heartbeat/api_registry_connection.py +97 -0
  25. mcp_mesh-0.5.0/_mcp_mesh/pipeline/api_startup/__init__.py +20 -0
  26. mcp_mesh-0.5.0/_mcp_mesh/pipeline/api_startup/api_pipeline.py +61 -0
  27. mcp_mesh-0.5.0/_mcp_mesh/pipeline/api_startup/api_server_setup.py +292 -0
  28. mcp_mesh-0.5.0/_mcp_mesh/pipeline/api_startup/fastapi_discovery.py +302 -0
  29. mcp_mesh-0.5.0/_mcp_mesh/pipeline/api_startup/route_collection.py +56 -0
  30. mcp_mesh-0.5.0/_mcp_mesh/pipeline/api_startup/route_integration.py +318 -0
  31. {mcp_mesh-0.4.1/_mcp_mesh/pipeline/startup → mcp_mesh-0.5.0/_mcp_mesh/pipeline/mcp_startup}/fastmcpserver_discovery.py +4 -4
  32. {mcp_mesh-0.4.1/_mcp_mesh/pipeline/startup → mcp_mesh-0.5.0/_mcp_mesh/pipeline/mcp_startup}/heartbeat_loop.py +1 -1
  33. {mcp_mesh-0.4.1/_mcp_mesh/pipeline/startup → mcp_mesh-0.5.0/_mcp_mesh/pipeline/mcp_startup}/startup_orchestrator.py +170 -5
  34. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/shared/config_resolver.py +0 -3
  35. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/shared/logging_config.py +2 -1
  36. mcp_mesh-0.5.0/_mcp_mesh/shared/sse_parser.py +217 -0
  37. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/mesh/__init__.py +6 -2
  38. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/mesh/decorators.py +143 -1
  39. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/pyproject.toml +4 -4
  40. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/.gitignore +0 -0
  41. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/LICENSE +0 -0
  42. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/README.md +0 -0
  43. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/engine/__init__.py +0 -0
  44. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/engine/decorator_registry.py +0 -0
  45. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/engine/http_wrapper.py +0 -0
  46. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/engine/self_dependency_proxy.py +0 -0
  47. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/engine/session_aware_client.py +0 -0
  48. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/engine/session_manager.py +0 -0
  49. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/engine/signature_analyzer.py +0 -0
  50. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/.openapi-generator/VERSION +0 -0
  51. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/.openapi-generator-ignore +0 -0
  52. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/api/agents_api.py +0 -0
  53. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/api/health_api.py +0 -0
  54. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/api_client.py +0 -0
  55. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/api_response.py +0 -0
  56. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/configuration.py +0 -0
  57. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/exceptions.py +0 -0
  58. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/agent_metadata.py +0 -0
  59. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/agent_metadata_dependencies_inner.py +0 -0
  60. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/agent_metadata_dependencies_inner_one_of.py +0 -0
  61. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/agent_registration.py +0 -0
  62. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/agent_registration_metadata.py +0 -0
  63. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/agents_list_response.py +0 -0
  64. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/capability_info.py +0 -0
  65. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/decorator_agent_metadata.py +0 -0
  66. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/decorator_agent_request.py +0 -0
  67. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/decorator_info.py +0 -0
  68. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/dependency_info.py +0 -0
  69. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/error_response.py +0 -0
  70. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/health_response.py +0 -0
  71. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/heartbeat_request.py +0 -0
  72. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/heartbeat_request_metadata.py +0 -0
  73. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/heartbeat_response.py +0 -0
  74. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_agent_register_metadata.py +0 -0
  75. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_registration_response.py +0 -0
  76. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_registration_response_dependencies_resolved_value_inner.py +0 -0
  77. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_tool_dependency_registration.py +0 -0
  78. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_tool_register_metadata.py +0 -0
  79. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_tool_registration.py +0 -0
  80. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/registration_response.py +0 -0
  81. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/rich_dependency.py +0 -0
  82. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/root_response.py +0 -0
  83. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/models/standardized_dependency.py +0 -0
  84. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/py.typed +0 -0
  85. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/generated/mcp_mesh_registry_client/rest.py +0 -0
  86. {mcp_mesh-0.4.1/_mcp_mesh/pipeline/heartbeat → mcp_mesh-0.5.0/_mcp_mesh/pipeline/mcp_heartbeat}/__init__.py +0 -0
  87. {mcp_mesh-0.4.1/_mcp_mesh/pipeline/heartbeat → mcp_mesh-0.5.0/_mcp_mesh/pipeline/mcp_heartbeat}/dependency_resolution.py +0 -0
  88. {mcp_mesh-0.4.1/_mcp_mesh/pipeline/heartbeat → mcp_mesh-0.5.0/_mcp_mesh/pipeline/mcp_heartbeat}/fast_heartbeat_check.py +0 -0
  89. {mcp_mesh-0.4.1/_mcp_mesh/pipeline/heartbeat → mcp_mesh-0.5.0/_mcp_mesh/pipeline/mcp_heartbeat}/heartbeat_orchestrator.py +0 -0
  90. {mcp_mesh-0.4.1/_mcp_mesh/pipeline/heartbeat → mcp_mesh-0.5.0/_mcp_mesh/pipeline/mcp_heartbeat}/heartbeat_pipeline.py +0 -0
  91. {mcp_mesh-0.4.1/_mcp_mesh/pipeline/heartbeat → mcp_mesh-0.5.0/_mcp_mesh/pipeline/mcp_heartbeat}/heartbeat_send.py +0 -0
  92. {mcp_mesh-0.4.1/_mcp_mesh/pipeline/heartbeat → mcp_mesh-0.5.0/_mcp_mesh/pipeline/mcp_heartbeat}/lifespan_integration.py +0 -0
  93. {mcp_mesh-0.4.1/_mcp_mesh/pipeline/heartbeat → mcp_mesh-0.5.0/_mcp_mesh/pipeline/mcp_heartbeat}/registry_connection.py +0 -0
  94. {mcp_mesh-0.4.1/_mcp_mesh/pipeline/startup → mcp_mesh-0.5.0/_mcp_mesh/pipeline/mcp_startup}/__init__.py +0 -0
  95. {mcp_mesh-0.4.1/_mcp_mesh/pipeline/startup → mcp_mesh-0.5.0/_mcp_mesh/pipeline/mcp_startup}/configuration.py +0 -0
  96. {mcp_mesh-0.4.1/_mcp_mesh/pipeline/startup → mcp_mesh-0.5.0/_mcp_mesh/pipeline/mcp_startup}/decorator_collection.py +0 -0
  97. {mcp_mesh-0.4.1/_mcp_mesh/pipeline/startup → mcp_mesh-0.5.0/_mcp_mesh/pipeline/mcp_startup}/fastapiserver_setup.py +0 -0
  98. {mcp_mesh-0.4.1/_mcp_mesh/pipeline/startup → mcp_mesh-0.5.0/_mcp_mesh/pipeline/mcp_startup}/heartbeat_preparation.py +0 -0
  99. {mcp_mesh-0.4.1/_mcp_mesh/pipeline/startup → mcp_mesh-0.5.0/_mcp_mesh/pipeline/mcp_startup}/startup_pipeline.py +0 -0
  100. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/pipeline/shared/__init__.py +0 -0
  101. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/pipeline/shared/base_step.py +0 -0
  102. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/pipeline/shared/mesh_pipeline.py +0 -0
  103. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/pipeline/shared/pipeline_types.py +0 -0
  104. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/pipeline/shared/registry_connection.py +0 -0
  105. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/shared/__init__.py +0 -0
  106. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/shared/content_extractor.py +0 -0
  107. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/shared/defaults.py +0 -0
  108. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/shared/fast_heartbeat_status.py +0 -0
  109. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/shared/host_resolver.py +0 -0
  110. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/shared/registry_client_wrapper.py +0 -0
  111. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/shared/support_types.py +0 -0
  112. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/tracing/agent_context_helper.py +0 -0
  113. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/tracing/context.py +0 -0
  114. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/tracing/execution_tracer.py +0 -0
  115. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/tracing/redis_metadata_publisher.py +0 -0
  116. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/_mcp_mesh/tracing/trace_context_helper.py +0 -0
  117. {mcp_mesh-0.4.1 → mcp_mesh-0.5.0}/mesh/types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-mesh
3
- Version: 0.4.1
3
+ Version: 0.5.0
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.4.1"
34
+ __version__ = "0.5.0"
35
35
 
36
36
  # Store reference to runtime processor if initialized
37
37
  _runtime_processor = None
@@ -48,7 +48,7 @@ def initialize_runtime():
48
48
  # Legacy processor system has been replaced by pipeline architecture
49
49
 
50
50
  # Use pipeline-based runtime
51
- from .pipeline.startup import start_runtime
51
+ from .pipeline.mcp_startup import start_runtime
52
52
 
53
53
  start_runtime()
54
54
 
@@ -60,7 +60,18 @@ def initialize_runtime():
60
60
 
61
61
  # Auto-initialize runtime if enabled
62
62
  if os.getenv("MCP_MESH_ENABLED", "true").lower() == "true":
63
- initialize_runtime()
63
+ # Use debounced initialization instead of immediate MCP startup
64
+ # This allows the system to determine MCP vs API pipeline based on decorators
65
+ try:
66
+ from .pipeline.mcp_startup import start_runtime
67
+
68
+ # Start the debounced runtime (sets up coordinator, no immediate pipeline execution)
69
+ start_runtime()
70
+
71
+ sys.stderr.write("MCP Mesh debounced runtime initialized\n")
72
+ except Exception as e:
73
+ # Log but don't fail - allows graceful degradation
74
+ sys.stderr.write(f"MCP Mesh runtime initialization failed: {e}\n")
64
75
 
65
76
 
66
77
  __all__ = [
@@ -6,6 +6,8 @@ import urllib.error
6
6
  import urllib.request
7
7
  from typing import Any
8
8
 
9
+ from ..shared.sse_parser import SSEParser
10
+
9
11
  logger = logging.getLogger(__name__)
10
12
 
11
13
 
@@ -62,25 +64,10 @@ class AsyncMCPClient:
62
64
 
63
65
  response_text = response.text
64
66
 
65
- # Handle Server-Sent Events format from FastMCP
66
- if response_text.startswith("event:"):
67
- # Parse SSE format: extract JSON from "data:" lines
68
- json_data = None
69
- for line in response_text.split("\n"):
70
- if line.startswith("data:"):
71
- json_str = line[5:].strip() # Remove 'data:' prefix
72
- try:
73
- json_data = json.loads(json_str)
74
- break
75
- except json.JSONDecodeError:
76
- continue
77
-
78
- if json_data is None:
79
- raise RuntimeError("Could not parse SSE response from FastMCP")
80
- data = json_data
81
- else:
82
- # Plain JSON response
83
- data = response.json()
67
+ # Use shared SSE parser
68
+ data = SSEParser.parse_sse_response(
69
+ response_text, f"AsyncMCPClient.{self.endpoint}"
70
+ )
84
71
 
85
72
  # Check for JSON-RPC error
86
73
  if "error" in data:
@@ -126,6 +126,9 @@ class DependencyInjector:
126
126
  for func_id in self._dependency_mapping[name]:
127
127
  if func_id in self._function_registry:
128
128
  func = self._function_registry[func_id]
129
+ logger.debug(
130
+ f"🔄 UPDATING dependency '{name}' for {func_id} -> {func} at {hex(id(func))}"
131
+ )
129
132
  if hasattr(func, "_mesh_update_dependency"):
130
133
  func._mesh_update_dependency(name, instance)
131
134
 
@@ -279,99 +282,180 @@ class DependencyInjector:
279
282
  # Capture logger in local scope to avoid NameError
280
283
  wrapper_logger = logger
281
284
 
282
- @functools.wraps(func)
283
- def dependency_wrapper(*args, **kwargs):
284
- wrapper_logger.debug(
285
- f"🔧 DEPENDENCY_WRAPPER: Function {func.__name__} called"
285
+ # If no mesh positions to inject, create minimal wrapper for tracking
286
+ if not mesh_positions:
287
+ logger.debug(
288
+ f"🔧 No injection positions for {func.__name__}, creating minimal wrapper for tracking"
286
289
  )
287
- wrapper_logger.debug(f"🔧 DEPENDENCY_WRAPPER: args={args}, kwargs={kwargs}")
288
- wrapper_logger.debug(
289
- f"🔧 DEPENDENCY_WRAPPER: mesh_positions={mesh_positions}"
290
+
291
+ @functools.wraps(func)
292
+ def minimal_wrapper(*args, **kwargs):
293
+ return func(*args, **kwargs)
294
+
295
+ # Add minimal metadata for compatibility
296
+ minimal_wrapper._mesh_injected_deps = {}
297
+ minimal_wrapper._mesh_dependencies = dependencies
298
+ minimal_wrapper._mesh_positions = mesh_positions
299
+ minimal_wrapper._mesh_parameter_types = get_agent_parameter_types(func)
300
+ minimal_wrapper._mesh_original_func = func
301
+
302
+ def update_dependency(name: str, instance: Any | None) -> None:
303
+ """No-op update for functions without injection positions."""
304
+ pass
305
+
306
+ minimal_wrapper._mesh_update_dependency = update_dependency
307
+
308
+ # Register this wrapper for dependency updates (even though it won't use them)
309
+ logger.debug(
310
+ f"🔧 REGISTERING minimal wrapper: {func_id} -> {minimal_wrapper} at {hex(id(minimal_wrapper))}"
290
311
  )
291
- wrapper_logger.debug(f"🔧 DEPENDENCY_WRAPPER: dependencies={dependencies}")
312
+ self._function_registry[func_id] = minimal_wrapper
313
+
314
+ return minimal_wrapper
292
315
 
293
- # If no mesh positions to inject into, still do execution logging but call original function
294
- if not mesh_positions:
316
+ # Determine if we need async wrapper
317
+ need_async_wrapper = inspect.iscoroutinefunction(func)
318
+
319
+ if need_async_wrapper:
320
+
321
+ @functools.wraps(func)
322
+ async def dependency_wrapper(*args, **kwargs):
295
323
  wrapper_logger.debug(
296
- "🔧 DEPENDENCY_WRAPPER: No mesh positions, calling original with execution logging"
324
+ f"🔧 DEPENDENCY_WRAPPER: Function {func.__name__} called"
297
325
  )
298
- from ..tracing.execution_tracer import ExecutionTracer
299
-
300
- return ExecutionTracer.trace_original_function(
301
- func._mesh_original_func, args, kwargs, wrapper_logger
326
+ wrapper_logger.debug(
327
+ f"🔧 DEPENDENCY_WRAPPER: args={args}, kwargs={kwargs}"
328
+ )
329
+ wrapper_logger.debug(
330
+ f"🔧 DEPENDENCY_WRAPPER: mesh_positions={mesh_positions}"
331
+ )
332
+ wrapper_logger.debug(
333
+ f"🔧 DEPENDENCY_WRAPPER: dependencies={dependencies}"
302
334
  )
303
335
 
304
- # Get function signature
305
- sig = inspect.signature(func)
306
- params = list(sig.parameters.keys())
307
- final_kwargs = kwargs.copy()
336
+ # We know mesh_positions is not empty since we checked above
308
337
 
309
- wrapper_logger.debug(f"🔧 DEPENDENCY_WRAPPER: params={params}")
310
- wrapper_logger.debug(f"🔧 DEPENDENCY_WRAPPER: original kwargs={kwargs}")
338
+ # Get function signature
339
+ sig = inspect.signature(func)
340
+ params = list(sig.parameters.keys())
341
+ final_kwargs = kwargs.copy()
311
342
 
312
- # Inject dependencies as kwargs
313
- injected_count = 0
314
- for dep_index, param_position in enumerate(mesh_positions):
315
- if dep_index < len(dependencies):
316
- dep_name = dependencies[dep_index]
317
- param_name = params[param_position]
343
+ wrapper_logger.debug(f"🔧 DEPENDENCY_WRAPPER: params={params}")
344
+ wrapper_logger.debug(f"🔧 DEPENDENCY_WRAPPER: original kwargs={kwargs}")
318
345
 
319
- wrapper_logger.debug(
320
- f"🔧 DEPENDENCY_WRAPPER: Processing dep {dep_index}: {dep_name} -> {param_name}"
321
- )
346
+ # Inject dependencies as kwargs
347
+ injected_count = 0
348
+ for dep_index, param_position in enumerate(mesh_positions):
349
+ if dep_index < len(dependencies):
350
+ dep_name = dependencies[dep_index]
351
+ param_name = params[param_position]
322
352
 
323
- # Only inject if the parameter wasn't explicitly provided
324
- if (
325
- param_name not in final_kwargs
326
- or final_kwargs.get(param_name) is None
327
- ):
328
- # Get the dependency from wrapper's storage
329
- dependency = dependency_wrapper._mesh_injected_deps.get(
330
- dep_name
331
- )
332
353
  wrapper_logger.debug(
333
- f"🔧 DEPENDENCY_WRAPPER: From wrapper storage: {dependency}"
354
+ f"🔧 DEPENDENCY_WRAPPER: Processing dep {dep_index}: {dep_name} -> {param_name}"
334
355
  )
335
356
 
336
- if dependency is None:
337
- dependency = self.get_dependency(dep_name)
357
+ # Only inject if the parameter wasn't explicitly provided
358
+ if (
359
+ param_name not in final_kwargs
360
+ or final_kwargs.get(param_name) is None
361
+ ):
362
+ # Get the dependency from wrapper's storage
363
+ dependency = dependency_wrapper._mesh_injected_deps.get(
364
+ dep_name
365
+ )
338
366
  wrapper_logger.debug(
339
- f"🔧 DEPENDENCY_WRAPPER: From global storage: {dependency}"
367
+ f"🔧 DEPENDENCY_WRAPPER: From wrapper storage: {dependency}"
340
368
  )
341
369
 
342
- final_kwargs[param_name] = dependency
343
- injected_count += 1
344
- wrapper_logger.debug(
345
- f"🔧 DEPENDENCY_WRAPPER: Injected {dep_name} as {param_name}"
346
- )
347
- else:
348
- wrapper_logger.debug(
349
- f"🔧 DEPENDENCY_WRAPPER: Skipping {param_name} - already provided"
350
- )
370
+ if dependency is None:
371
+ dependency = self.get_dependency(dep_name)
372
+ wrapper_logger.debug(
373
+ f"🔧 DEPENDENCY_WRAPPER: From global storage: {dependency}"
374
+ )
351
375
 
352
- wrapper_logger.debug(
353
- f"🔧 DEPENDENCY_WRAPPER: Injected {injected_count} dependencies"
354
- )
355
- wrapper_logger.debug(f"🔧 DEPENDENCY_WRAPPER: final_kwargs={final_kwargs}")
356
-
357
- # ===== EXECUTE WITH DEPENDENCY INJECTION AND TRACING =====
358
- from ..tracing.execution_tracer import ExecutionTracer
359
-
360
- # Use helper class for clean execution tracing
361
- result = ExecutionTracer.trace_function_execution(
362
- func._mesh_original_func,
363
- args,
364
- final_kwargs,
365
- dependencies,
366
- mesh_positions,
367
- injected_count,
368
- wrapper_logger,
369
- )
376
+ final_kwargs[param_name] = dependency
377
+ injected_count += 1
378
+ wrapper_logger.debug(
379
+ f"🔧 DEPENDENCY_WRAPPER: Injected {dep_name} as {param_name}"
380
+ )
381
+ else:
382
+ wrapper_logger.debug(
383
+ f"🔧 DEPENDENCY_WRAPPER: Skipping {param_name} - already provided"
384
+ )
370
385
 
371
- wrapper_logger.debug(
372
- f"🔧 DEPENDENCY_WRAPPER: Original returned: {type(result)}"
373
- )
374
- return result
386
+ wrapper_logger.debug(
387
+ f"🔧 DEPENDENCY_WRAPPER: Injected {injected_count} dependencies"
388
+ )
389
+ wrapper_logger.debug(
390
+ f"🔧 DEPENDENCY_WRAPPER: final_kwargs={final_kwargs}"
391
+ )
392
+
393
+ # ===== EXECUTE WITH DEPENDENCY INJECTION =====
394
+ # Call the original function with injected dependencies
395
+ original_func = func._mesh_original_func
396
+
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)
404
+
405
+ wrapper_logger.debug(
406
+ f"🔧 DEPENDENCY_WRAPPER: Original returned: {type(result)}"
407
+ )
408
+ return result
409
+
410
+ else:
411
+ # Create sync wrapper for sync functions without dependencies
412
+ @functools.wraps(func)
413
+ def dependency_wrapper(*args, **kwargs):
414
+ wrapper_logger.debug(
415
+ f"🔧 DEPENDENCY_WRAPPER: Function {func.__name__} called"
416
+ )
417
+ wrapper_logger.debug(
418
+ f"🔧 DEPENDENCY_WRAPPER: args={args}, kwargs={kwargs}"
419
+ )
420
+ wrapper_logger.debug(
421
+ f"🔧 DEPENDENCY_WRAPPER: mesh_positions={mesh_positions}"
422
+ )
423
+ wrapper_logger.debug(
424
+ f"🔧 DEPENDENCY_WRAPPER: dependencies={dependencies}"
425
+ )
426
+
427
+ # We know mesh_positions is not empty since we checked above
428
+
429
+ # Handle dependency injection for sync functions
430
+ sig = inspect.signature(func)
431
+ params = list(sig.parameters.keys())
432
+ final_kwargs = kwargs.copy()
433
+
434
+ # Inject dependencies as kwargs
435
+ injected_count = 0
436
+ for dep_index, param_position in enumerate(mesh_positions):
437
+ if dep_index < len(dependencies):
438
+ dep_name = dependencies[dep_index]
439
+ param_name = params[param_position]
440
+
441
+ # Only inject if the parameter wasn't explicitly provided
442
+ if (
443
+ param_name not in final_kwargs
444
+ or final_kwargs.get(param_name) is None
445
+ ):
446
+ # Get the dependency from wrapper's storage
447
+ dependency = dependency_wrapper._mesh_injected_deps.get(
448
+ dep_name
449
+ )
450
+
451
+ if dependency is None:
452
+ dependency = self.get_dependency(dep_name)
453
+
454
+ final_kwargs[param_name] = dependency
455
+ injected_count += 1
456
+
457
+ # Call the original function with injected dependencies
458
+ return func._mesh_original_func(*args, **final_kwargs)
375
459
 
376
460
  # Store dependency state on wrapper
377
461
  dependency_wrapper._mesh_injected_deps = {}
@@ -399,6 +483,9 @@ class DependencyInjector:
399
483
  dependency_wrapper._mesh_original_func = func
400
484
 
401
485
  # Register this wrapper for dependency updates
486
+ logger.debug(
487
+ f"🔧 REGISTERING in function_registry: {func_id} -> {dependency_wrapper} at {hex(id(dependency_wrapper))}"
488
+ )
402
489
  self._function_registry[func_id] = dependency_wrapper
403
490
 
404
491
  # Return the wrapper (which FastMCP will register)
@@ -7,6 +7,7 @@ import uuid
7
7
  from collections.abc import AsyncIterator
8
8
  from typing import Any, Optional
9
9
 
10
+ from ..shared.sse_parser import SSEStreamProcessor
10
11
  from .async_mcp_client import AsyncMCPClient
11
12
  from .mcp_client_proxy import MCPClientProxy
12
13
 
@@ -103,15 +104,18 @@ class FullMCPProxy(MCPClientProxy):
103
104
  if response.status_code >= 400:
104
105
  raise RuntimeError(f"HTTP error {response.status_code}")
105
106
 
106
- async for line in response.aiter_lines():
107
- if line.startswith("data: "):
108
- try:
109
- data_str = line[6:] # Remove "data: " prefix
110
- if data_str.strip():
111
- chunk = json.loads(data_str)
112
- yield chunk
113
- except json.JSONDecodeError:
114
- continue
107
+ # Use shared SSE stream processor
108
+ sse_processor = SSEStreamProcessor(f"FullMCPProxy.{name}")
109
+
110
+ async for chunk_bytes in response.aiter_bytes(8192):
111
+ chunks = sse_processor.process_chunk(chunk_bytes)
112
+ for chunk in chunks:
113
+ yield chunk
114
+
115
+ # Process any remaining data
116
+ final_chunks = sse_processor.finalize()
117
+ for chunk in final_chunks:
118
+ yield chunk
115
119
 
116
120
  except ImportError:
117
121
  # Fallback: if httpx not available, use sync call
@@ -588,19 +592,20 @@ class EnhancedFullMCPProxy(FullMCPProxy):
588
592
  ) as response:
589
593
  response.raise_for_status()
590
594
 
591
- buffer = ""
592
- async for chunk in response.aiter_bytes(self.buffer_size):
593
- buffer += chunk.decode("utf-8")
595
+ # Use shared SSE stream processor
596
+ sse_processor = SSEStreamProcessor(f"EnhancedFullMCPProxy.{name}")
594
597
 
595
- while "\n" in buffer:
596
- line, buffer = buffer.split("\n", 1)
598
+ async for chunk_bytes in response.aiter_bytes(
599
+ max(self.buffer_size, 8192)
600
+ ):
601
+ chunks = sse_processor.process_chunk(chunk_bytes)
602
+ for chunk in chunks:
603
+ yield chunk
597
604
 
598
- if line.startswith("data: "):
599
- try:
600
- data = json.loads(line[6:])
601
- yield data
602
- except json.JSONDecodeError:
603
- continue
605
+ # Process any remaining data
606
+ final_chunks = sse_processor.finalize()
607
+ for chunk in final_chunks:
608
+ yield chunk
604
609
 
605
610
  except httpx.TimeoutException:
606
611
  raise Exception(f"Streaming timeout after {self.stream_timeout}s")
@@ -10,6 +10,7 @@ import uuid
10
10
  from typing import Any, Optional
11
11
 
12
12
  from ..shared.content_extractor import ContentExtractor
13
+ from ..shared.sse_parser import SSEParser
13
14
  from .async_mcp_client import AsyncMCPClient
14
15
 
15
16
  logger = logging.getLogger(__name__)
@@ -113,25 +114,10 @@ class MCPClientProxy:
113
114
  with urllib.request.urlopen(req, timeout=30.0) as response:
114
115
  response_data = response.read().decode("utf-8")
115
116
 
116
- # Handle Server-Sent Events format from FastMCP
117
- if response_data.startswith("event:"):
118
- # Parse SSE format: extract JSON from "data:" lines
119
- json_data = None
120
- for line in response_data.split("\n"):
121
- if line.startswith("data:"):
122
- json_str = line[5:].strip() # Remove 'data:' prefix
123
- try:
124
- json_data = json.loads(json_str)
125
- break
126
- except json.JSONDecodeError:
127
- continue
128
-
129
- if json_data is None:
130
- raise RuntimeError("Could not parse SSE response from FastMCP")
131
- data = json_data
132
- else:
133
- # Plain JSON response
134
- data = json.loads(response_data)
117
+ # Use shared SSE parser
118
+ data = SSEParser.parse_sse_response(
119
+ response_data, f"MCPClientProxy.{self.function_name}"
120
+ )
135
121
 
136
122
  # Check for JSON-RPC error
137
123
  if "error" in data:
@@ -2,6 +2,7 @@ mcp_mesh_registry_client/__init__.py
2
2
  mcp_mesh_registry_client/api/__init__.py
3
3
  mcp_mesh_registry_client/api/agents_api.py
4
4
  mcp_mesh_registry_client/api/health_api.py
5
+ mcp_mesh_registry_client/api/tracing_api.py
5
6
  mcp_mesh_registry_client/api_client.py
6
7
  mcp_mesh_registry_client/api_response.py
7
8
  mcp_mesh_registry_client/configuration.py
@@ -35,5 +36,6 @@ mcp_mesh_registry_client/models/registration_response.py
35
36
  mcp_mesh_registry_client/models/rich_dependency.py
36
37
  mcp_mesh_registry_client/models/root_response.py
37
38
  mcp_mesh_registry_client/models/standardized_dependency.py
39
+ mcp_mesh_registry_client/models/trace_event.py
38
40
  mcp_mesh_registry_client/py.typed
39
41
  mcp_mesh_registry_client/rest.py
@@ -36,6 +36,7 @@ __version__ = "1.0.0"
36
36
  # import apis into sdk package
37
37
  from _mcp_mesh.generated.mcp_mesh_registry_client.api.agents_api import AgentsApi
38
38
  from _mcp_mesh.generated.mcp_mesh_registry_client.api.health_api import HealthApi
39
+ from _mcp_mesh.generated.mcp_mesh_registry_client.api.tracing_api import TracingApi
39
40
 
40
41
  # import ApiClient
41
42
  from _mcp_mesh.generated.mcp_mesh_registry_client.api_response import ApiResponse
@@ -77,3 +78,4 @@ from _mcp_mesh.generated.mcp_mesh_registry_client.models.registration_response i
77
78
  from _mcp_mesh.generated.mcp_mesh_registry_client.models.rich_dependency import RichDependency
78
79
  from _mcp_mesh.generated.mcp_mesh_registry_client.models.root_response import RootResponse
79
80
  from _mcp_mesh.generated.mcp_mesh_registry_client.models.standardized_dependency import StandardizedDependency
81
+ from _mcp_mesh.generated.mcp_mesh_registry_client.models.trace_event import TraceEvent
@@ -3,4 +3,5 @@
3
3
  # import apis into api package
4
4
  from _mcp_mesh.generated.mcp_mesh_registry_client.api.agents_api import AgentsApi
5
5
  from _mcp_mesh.generated.mcp_mesh_registry_client.api.health_api import HealthApi
6
+ from _mcp_mesh.generated.mcp_mesh_registry_client.api.tracing_api import TracingApi
6
7