mcp-mesh 0.5.6__py3-none-any.whl → 0.5.7__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.
_mcp_mesh/__init__.py CHANGED
@@ -31,7 +31,7 @@ from .engine.decorator_registry import (
31
31
  get_decorator_stats,
32
32
  )
33
33
 
34
- __version__ = "0.5.6"
34
+ __version__ = "0.5.7"
35
35
 
36
36
  # Store reference to runtime processor if initialized
37
37
  _runtime_processor = None
@@ -2,7 +2,7 @@
2
2
  Dynamic dependency injection system for MCP Mesh.
3
3
 
4
4
  Handles both initial injection and runtime updates when topology changes.
5
- Focused purely on dependency injection - telemetry/tracing is handled at
5
+ Focused purely on dependency injection - telemetry/tracing is handled at
6
6
  the HTTP middleware layer for unified approach across MCP agents and FastAPI apps.
7
7
  """
8
8
 
@@ -57,17 +57,17 @@ def analyze_injection_strategy(func: Callable, dependencies: list[str]) -> list[
57
57
  logger.warning(
58
58
  f"Single parameter '{param_name}' in function '{func_name}' found, "
59
59
  f"injecting {dependencies[0] if dependencies else 'dependency'} proxy "
60
- f"(consider typing as McpMeshAgent for clarity)"
60
+ f"(consider typing as McpMeshAgent or McpAgent for clarity)"
61
61
  )
62
62
  return [0] # Inject into the single parameter
63
63
 
64
- # Multiple parameters rule: only inject into McpMeshAgent typed parameters
64
+ # Multiple parameters rule: only inject into McpMeshAgent or McpAgent typed parameters
65
65
  if param_count > 1:
66
66
  if not mesh_positions:
67
67
  logger.warning(
68
68
  f"⚠️ Function '{func_name}' has {param_count} parameters but none are "
69
- f"typed as McpMeshAgent. Skipping injection of {len(dependencies)} dependencies. "
70
- f"Consider typing dependency parameters as McpMeshAgent."
69
+ f"typed as McpMeshAgent or McpAgent. Skipping injection of {len(dependencies)} dependencies. "
70
+ f"Consider typing dependency parameters as McpMeshAgent or McpAgent."
71
71
  )
72
72
  return []
73
73
 
@@ -77,7 +77,7 @@ def analyze_injection_strategy(func: Callable, dependencies: list[str]) -> list[
77
77
  excess_deps = dependencies[len(mesh_positions) :]
78
78
  logger.warning(
79
79
  f"Function '{func_name}' has {len(dependencies)} dependencies "
80
- f"but only {len(mesh_positions)} McpMeshAgent parameters. "
80
+ f"but only {len(mesh_positions)} McpMeshAgent/McpAgent parameters. "
81
81
  f"Dependencies {excess_deps} will not be injected."
82
82
  )
83
83
  else:
@@ -85,7 +85,7 @@ def analyze_injection_strategy(func: Callable, dependencies: list[str]) -> list[
85
85
  params[pos].name for pos in mesh_positions[len(dependencies) :]
86
86
  ]
87
87
  logger.warning(
88
- f"Function '{func_name}' has {len(mesh_positions)} McpMeshAgent parameters "
88
+ f"Function '{func_name}' has {len(mesh_positions)} McpMeshAgent/McpAgent parameters "
89
89
  f"but only {len(dependencies)} dependencies declared. "
90
90
  f"Parameters {excess_params} will remain None."
91
91
  )
@@ -118,12 +118,17 @@ class DependencyInjector:
118
118
  self._lock = asyncio.Lock()
119
119
 
120
120
  async def register_dependency(self, name: str, instance: Any) -> None:
121
- """Register a new dependency or update existing one."""
121
+ """Register a new dependency or update existing one.
122
+
123
+ Args:
124
+ name: Composite key in format "function_id:dep_N" or legacy capability name
125
+ instance: Proxy instance to register
126
+ """
122
127
  async with self._lock:
123
128
  logger.info(f"📦 Registering dependency: {name}")
124
129
  self._dependencies[name] = instance
125
130
 
126
- # Notify all functions that depend on this
131
+ # Notify all functions that depend on this (using composite keys)
127
132
  if name in self._dependency_mapping:
128
133
  for func_id in self._dependency_mapping[name]:
129
134
  if func_id in self._function_registry:
@@ -132,10 +137,28 @@ class DependencyInjector:
132
137
  f"🔄 UPDATING dependency '{name}' for {func_id} -> {func} at {hex(id(func))}"
133
138
  )
134
139
  if hasattr(func, "_mesh_update_dependency"):
135
- func._mesh_update_dependency(name, instance)
140
+ # Extract dep_index from composite key (format: "function_id:dep_N")
141
+ if ":dep_" in name:
142
+ dep_index_str = name.split(":dep_")[-1]
143
+ try:
144
+ dep_index = int(dep_index_str)
145
+ func._mesh_update_dependency(dep_index, instance)
146
+ except ValueError:
147
+ logger.warning(
148
+ f"⚠️ Invalid dep_index in key '{name}', skipping update"
149
+ )
150
+ else:
151
+ # Legacy format (shouldn't happen with new code)
152
+ logger.warning(
153
+ f"⚠️ Legacy dependency key format '{name}' not supported in array-based injection"
154
+ )
136
155
 
137
156
  async def unregister_dependency(self, name: str) -> None:
138
- """Remove a dependency (e.g., service went down)."""
157
+ """Remove a dependency (e.g., service went down).
158
+
159
+ Args:
160
+ name: Composite key in format "function_id:dep_N" or legacy capability name
161
+ """
139
162
  async with self._lock:
140
163
  logger.info(f"🗑️ INJECTOR: Unregistering dependency: {name}")
141
164
  if name in self._dependencies:
@@ -156,7 +179,21 @@ class DependencyInjector:
156
179
  logger.info(
157
180
  f"🗑️ INJECTOR: Removing {name} from function {func_id}"
158
181
  )
159
- func._mesh_update_dependency(name, None)
182
+ # Extract dep_index from composite key
183
+ if ":dep_" in name:
184
+ dep_index_str = name.split(":dep_")[-1]
185
+ try:
186
+ dep_index = int(dep_index_str)
187
+ func._mesh_update_dependency(dep_index, None)
188
+ except ValueError:
189
+ logger.warning(
190
+ f"⚠️ Invalid dep_index in key '{name}', skipping removal"
191
+ )
192
+ else:
193
+ # Legacy format
194
+ logger.warning(
195
+ f"⚠️ Legacy dependency key format '{name}' not supported in array-based injection"
196
+ )
160
197
  else:
161
198
  logger.warning(
162
199
  f"🗑️ INJECTOR: Function {func_id} has no _mesh_update_dependency method"
@@ -266,15 +303,16 @@ class DependencyInjector:
266
303
  # Get parameter type information for proxy selection
267
304
  parameter_types = get_agent_parameter_types(func)
268
305
 
269
- # Track which dependencies this function needs
270
- for dep in dependencies:
271
- if dep not in self._dependency_mapping:
272
- self._dependency_mapping[dep] = set()
273
- self._dependency_mapping[dep].add(func_id)
306
+ # Track which dependencies this function needs (using composite keys)
307
+ for dep_index, dep in enumerate(dependencies):
308
+ dep_key = f"{func_id}:dep_{dep_index}"
309
+ if dep_key not in self._dependency_mapping:
310
+ self._dependency_mapping[dep_key] = set()
311
+ self._dependency_mapping[dep_key].add(func_id)
274
312
 
275
- # Store current dependency values on the function itself
313
+ # Store current dependency values as array (indexed by position)
276
314
  if not hasattr(func, "_mesh_injected_deps"):
277
- func._mesh_injected_deps = {}
315
+ func._mesh_injected_deps = [None] * len(dependencies)
278
316
 
279
317
  # Store original implementation if not already stored
280
318
  if not hasattr(func, "_mesh_original_func"):
@@ -292,34 +330,45 @@ class DependencyInjector:
292
330
 
293
331
  # Check if we need async wrapper for minimal case
294
332
  if inspect.iscoroutinefunction(func):
333
+
295
334
  @functools.wraps(func)
296
335
  async def minimal_wrapper(*args, **kwargs):
297
336
  # Use ExecutionTracer for functions without dependencies (v0.4.0 style)
298
337
  from ..tracing.execution_tracer import ExecutionTracer
299
- wrapper_logger.debug(f"🔧 DI: Executing async function {func.__name__} (no dependencies)")
300
-
338
+
339
+ wrapper_logger.debug(
340
+ f"🔧 DI: Executing async function {func.__name__} (no dependencies)"
341
+ )
342
+
301
343
  # For async functions without dependencies, use the async tracer
302
344
  return await ExecutionTracer.trace_function_execution_async(
303
345
  func, args, kwargs, [], [], 0, wrapper_logger
304
346
  )
347
+
305
348
  else:
349
+
306
350
  @functools.wraps(func)
307
351
  def minimal_wrapper(*args, **kwargs):
308
352
  # Use ExecutionTracer for functions without dependencies (v0.4.0 style)
309
353
  from ..tracing.execution_tracer import ExecutionTracer
310
- wrapper_logger.debug(f"🔧 DI: Executing sync function {func.__name__} (no dependencies)")
311
-
354
+
355
+ wrapper_logger.debug(
356
+ f"🔧 DI: Executing sync function {func.__name__} (no dependencies)"
357
+ )
358
+
312
359
  # Use original function tracer for functions without dependencies
313
- return ExecutionTracer.trace_original_function(func, args, kwargs, wrapper_logger)
360
+ return ExecutionTracer.trace_original_function(
361
+ func, args, kwargs, wrapper_logger
362
+ )
314
363
 
315
- # Add minimal metadata for compatibility
316
- minimal_wrapper._mesh_injected_deps = {}
364
+ # Add minimal metadata for compatibility (use array for consistency)
365
+ minimal_wrapper._mesh_injected_deps = [None] * len(dependencies)
317
366
  minimal_wrapper._mesh_dependencies = dependencies
318
367
  minimal_wrapper._mesh_positions = mesh_positions
319
368
  minimal_wrapper._mesh_parameter_types = get_agent_parameter_types(func)
320
369
  minimal_wrapper._mesh_original_func = func
321
370
 
322
- def update_dependency(name: str, instance: Any | None) -> None:
371
+ def update_dependency(dep_index: int, instance: Any | None) -> None:
323
372
  """No-op update for functions without injection positions."""
324
373
  pass
325
374
 
@@ -363,7 +412,7 @@ class DependencyInjector:
363
412
  wrapper_logger.debug(f"🔧 DEPENDENCY_WRAPPER: params={params}")
364
413
  wrapper_logger.debug(f"🔧 DEPENDENCY_WRAPPER: original kwargs={kwargs}")
365
414
 
366
- # Inject dependencies as kwargs
415
+ # Inject dependencies as kwargs (using array-based lookup)
367
416
  injected_count = 0
368
417
  for dep_index, param_position in enumerate(mesh_positions):
369
418
  if dep_index < len(dependencies):
@@ -379,24 +428,28 @@ class DependencyInjector:
379
428
  param_name not in final_kwargs
380
429
  or final_kwargs.get(param_name) is None
381
430
  ):
382
- # Get the dependency from wrapper's storage
383
- dependency = dependency_wrapper._mesh_injected_deps.get(
384
- dep_name
385
- )
431
+ # Get the dependency from wrapper's array storage (by index)
432
+ dependency = None
433
+ if dep_index < len(dependency_wrapper._mesh_injected_deps):
434
+ dependency = dependency_wrapper._mesh_injected_deps[
435
+ dep_index
436
+ ]
386
437
  wrapper_logger.debug(
387
- f"🔧 DEPENDENCY_WRAPPER: From wrapper storage: {dependency}"
438
+ f"🔧 DEPENDENCY_WRAPPER: From wrapper storage[{dep_index}]: {dependency}"
388
439
  )
389
440
 
390
441
  if dependency is None:
391
- dependency = self.get_dependency(dep_name)
442
+ # Fallback to global storage with composite key
443
+ dep_key = f"{func.__module__}.{func.__qualname__}:dep_{dep_index}"
444
+ dependency = self.get_dependency(dep_key)
392
445
  wrapper_logger.debug(
393
- f"🔧 DEPENDENCY_WRAPPER: From global storage: {dependency}"
446
+ f"🔧 DEPENDENCY_WRAPPER: From global storage[{dep_key}]: {dependency}"
394
447
  )
395
448
 
396
449
  final_kwargs[param_name] = dependency
397
450
  injected_count += 1
398
451
  wrapper_logger.debug(
399
- f"🔧 DEPENDENCY_WRAPPER: Injected {dep_name} as {param_name}"
452
+ f"🔧 DEPENDENCY_WRAPPER: Injected {dep_name} from index {dep_index} as {param_name}"
400
453
  )
401
454
  else:
402
455
  wrapper_logger.debug(
@@ -413,7 +466,7 @@ class DependencyInjector:
413
466
  # ===== EXECUTE WITH DEPENDENCY INJECTION AND TRACING =====
414
467
  # Use ExecutionTracer for comprehensive execution logging (v0.4.0 style)
415
468
  from ..tracing.execution_tracer import ExecutionTracer
416
-
469
+
417
470
  original_func = func._mesh_original_func
418
471
 
419
472
  wrapper_logger.debug(
@@ -460,7 +513,7 @@ class DependencyInjector:
460
513
  params = list(sig.parameters.keys())
461
514
  final_kwargs = kwargs.copy()
462
515
 
463
- # Inject dependencies as kwargs
516
+ # Inject dependencies as kwargs (using array-based lookup)
464
517
  injected_count = 0
465
518
  for dep_index, param_position in enumerate(mesh_positions):
466
519
  if dep_index < len(dependencies):
@@ -472,13 +525,17 @@ class DependencyInjector:
472
525
  param_name not in final_kwargs
473
526
  or final_kwargs.get(param_name) is None
474
527
  ):
475
- # Get the dependency from wrapper's storage
476
- dependency = dependency_wrapper._mesh_injected_deps.get(
477
- dep_name
478
- )
528
+ # Get the dependency from wrapper's array storage (by index)
529
+ dependency = None
530
+ if dep_index < len(dependency_wrapper._mesh_injected_deps):
531
+ dependency = dependency_wrapper._mesh_injected_deps[
532
+ dep_index
533
+ ]
479
534
 
480
535
  if dependency is None:
481
- dependency = self.get_dependency(dep_name)
536
+ # Fallback to global storage with composite key
537
+ dep_key = f"{func.__module__}.{func.__qualname__}:dep_{dep_index}"
538
+ dependency = self.get_dependency(dep_key)
482
539
 
483
540
  final_kwargs[param_name] = dependency
484
541
  injected_count += 1
@@ -486,7 +543,7 @@ class DependencyInjector:
486
543
  # ===== EXECUTE WITH DEPENDENCY INJECTION AND TRACING =====
487
544
  # Use ExecutionTracer for comprehensive execution logging (v0.4.0 style)
488
545
  from ..tracing.execution_tracer import ExecutionTracer
489
-
546
+
490
547
  wrapper_logger.debug(
491
548
  f"🔧 DI: Executing sync function {func._mesh_original_func.__name__} with {injected_count} injected dependencies"
492
549
  )
@@ -502,20 +559,28 @@ class DependencyInjector:
502
559
  wrapper_logger,
503
560
  )
504
561
 
505
- # Store dependency state on wrapper
506
- dependency_wrapper._mesh_injected_deps = {}
507
-
508
- # Add update method to wrapper
509
- def update_dependency(name: str, instance: Any | None) -> None:
510
- """Called when a dependency changes."""
511
- if instance is None:
512
- dependency_wrapper._mesh_injected_deps.pop(name, None)
513
- wrapper_logger.debug(f"Removed {name} from {func_id}")
562
+ # Store dependency state on wrapper as array (indexed by position)
563
+ dependency_wrapper._mesh_injected_deps = [None] * len(dependencies)
564
+
565
+ # Add update method to wrapper (now uses index-based updates)
566
+ def update_dependency(dep_index: int, instance: Any | None) -> None:
567
+ """Called when a dependency changes (index-based for duplicate capability support)."""
568
+ if dep_index < len(dependency_wrapper._mesh_injected_deps):
569
+ dependency_wrapper._mesh_injected_deps[dep_index] = instance
570
+ if instance is None:
571
+ wrapper_logger.debug(
572
+ f"Removed dependency at index {dep_index} from {func_id}"
573
+ )
574
+ else:
575
+ wrapper_logger.debug(
576
+ f"Updated dependency at index {dep_index} for {func_id}"
577
+ )
578
+ wrapper_logger.debug(
579
+ f"🔗 Wrapper pointer receiving dependency: {dependency_wrapper} at {hex(id(dependency_wrapper))}"
580
+ )
514
581
  else:
515
- dependency_wrapper._mesh_injected_deps[name] = instance
516
- wrapper_logger.debug(f"Updated {name} for {func_id}")
517
- wrapper_logger.debug(
518
- f"🔗 Wrapper pointer receiving dependency: {dependency_wrapper} at {hex(id(dependency_wrapper))}"
582
+ wrapper_logger.warning(
583
+ f"⚠️ Attempted to update dependency at index {dep_index} but wrapper only has {len(dependency_wrapper._mesh_injected_deps)} dependencies"
519
584
  )
520
585
 
521
586
  # Store update method on wrapper
@@ -151,13 +151,13 @@ def get_mesh_agent_positions(func: Any) -> list[int]:
151
151
 
152
152
  def get_mesh_agent_parameter_names(func: Any) -> list[str]:
153
153
  """
154
- Get names of McpMeshAgent parameters in function signature.
154
+ Get names of McpMeshAgent/McpAgent parameters in function signature.
155
155
 
156
156
  Args:
157
157
  func: Function to analyze
158
158
 
159
159
  Returns:
160
- List of parameter names that are McpMeshAgent types
160
+ List of parameter names that are McpMeshAgent or McpAgent types
161
161
  """
162
162
  try:
163
163
  type_hints = get_type_hints(func)
@@ -168,22 +168,31 @@ def get_mesh_agent_parameter_names(func: Any) -> list[str]:
168
168
  if param_name in type_hints:
169
169
  param_type = type_hints[param_name]
170
170
 
171
- # Check if it's McpMeshAgent type (handle different import paths and Union types)
171
+ # Check if it's McpAgent or McpMeshAgent type (handle different import paths and Union types)
172
172
  is_mesh_agent = False
173
173
 
174
- # Direct McpMeshAgent type
175
- if param_type == McpMeshAgent or (
176
- hasattr(param_type, "__origin__")
177
- and param_type.__origin__ is type(McpMeshAgent)
174
+ # Direct McpAgent or McpMeshAgent type
175
+ if (
176
+ param_type in (McpAgent, McpMeshAgent)
177
+ or (
178
+ hasattr(param_type, "__name__")
179
+ and param_type.__name__ in ("McpAgent", "McpMeshAgent")
180
+ )
181
+ or (
182
+ hasattr(param_type, "__origin__")
183
+ and param_type.__origin__
184
+ in (type(McpAgent), type(McpMeshAgent))
185
+ )
178
186
  ):
179
187
  is_mesh_agent = True
180
188
 
181
- # Union type (e.g., McpMeshAgent | None)
189
+ # Union type (e.g., McpAgent | None, McpMeshAgent | None)
182
190
  elif hasattr(param_type, "__args__"):
183
- # Check if any arg in the union is McpMeshAgent
191
+ # Check if any arg in the union is McpAgent or McpMeshAgent
184
192
  for arg in param_type.__args__:
185
- if arg == McpMeshAgent or (
186
- hasattr(arg, "__name__") and arg.__name__ == "McpMeshAgent"
193
+ if arg in (McpAgent, McpMeshAgent) or (
194
+ hasattr(arg, "__name__")
195
+ and arg.__name__ in ("McpAgent", "McpMeshAgent")
187
196
  ):
188
197
  is_mesh_agent = True
189
198
  break
@@ -199,7 +208,7 @@ def get_mesh_agent_parameter_names(func: Any) -> list[str]:
199
208
 
200
209
  def validate_mesh_dependencies(func: Any, dependencies: list[dict]) -> tuple[bool, str]:
201
210
  """
202
- Validate that the number of dependencies matches McpMeshAgent parameters.
211
+ Validate that the number of dependencies matches McpMeshAgent/McpAgent parameters.
203
212
 
204
213
  Args:
205
214
  func: Function to validate
@@ -212,9 +221,9 @@ def validate_mesh_dependencies(func: Any, dependencies: list[dict]) -> tuple[boo
212
221
 
213
222
  if len(dependencies) != len(mesh_positions):
214
223
  return False, (
215
- f"Function {func.__name__} has {len(mesh_positions)} McpMeshAgent parameters "
224
+ f"Function {func.__name__} has {len(mesh_positions)} McpMeshAgent/McpAgent parameters "
216
225
  f"but {len(dependencies)} dependencies declared. "
217
- f"Each McpMeshAgent parameter needs a corresponding dependency."
226
+ f"Each McpMeshAgent/McpAgent parameter needs a corresponding dependency."
218
227
  )
219
228
 
220
229
  return True, ""
@@ -14,7 +14,7 @@ from ..shared import PipelineResult, PipelineStatus, PipelineStep
14
14
 
15
15
  logger = logging.getLogger(__name__)
16
16
 
17
- # Global state for dependency hash tracking across heartbeat cycles
17
+ # Global state for dependency hash tracking across heartbeat cycles
18
18
  _last_api_dependency_hash = None
19
19
 
20
20
 
@@ -24,7 +24,7 @@ class APIDependencyResolutionStep(PipelineStep):
24
24
 
25
25
  Takes the dependencies_resolved data from the heartbeat response
26
26
  and updates dependency injection for FastAPI route handlers.
27
-
27
+
28
28
  Similar to MCP dependency resolution but adapted for:
29
29
  - FastAPI route handlers instead of MCP tools
30
30
  - Single "api_endpoint_handler" function instead of multiple tools
@@ -54,7 +54,9 @@ class APIDependencyResolutionStep(PipelineStep):
54
54
  result.message = (
55
55
  "No heartbeat response or registry wrapper - completed successfully"
56
56
  )
57
- self.logger.info("ℹ️ No heartbeat response to process - this is normal for API services")
57
+ self.logger.info(
58
+ "ℹ️ No heartbeat response to process - this is normal for API services"
59
+ )
58
60
  return result
59
61
 
60
62
  # Use the same hash-based change detection pattern as MCP
@@ -72,23 +74,31 @@ class APIDependencyResolutionStep(PipelineStep):
72
74
  # Store processed dependencies info for context
73
75
  result.add_context("dependency_count", dependency_count)
74
76
  result.add_context("dependencies_resolved", dependencies_resolved)
75
-
76
- result.message = "API dependency resolution completed (efficient hash-based)"
77
-
77
+
78
+ result.message = (
79
+ "API dependency resolution completed (efficient hash-based)"
80
+ )
81
+
78
82
  if dependency_count > 0:
79
83
  self.logger.info(f"🔗 Dependencies resolved: {dependency_count} items")
80
-
84
+
81
85
  # Log function registry status for debugging
82
86
  injector = get_global_injector()
83
87
  function_count = len(injector._function_registry)
84
- self.logger.debug(f"🔍 Function registry contains {function_count} functions:")
88
+ self.logger.debug(
89
+ f"🔍 Function registry contains {function_count} functions:"
90
+ )
85
91
  for func_id, wrapper_func in injector._function_registry.items():
86
- original_func = getattr(wrapper_func, '_mesh_original_func', None)
87
- func_name = original_func.__name__ if original_func else 'unknown'
88
- dependencies = getattr(wrapper_func, '_mesh_dependencies', [])
89
- self.logger.debug(f" 📋 {func_id} -> {func_name} (deps: {dependencies})")
90
-
91
- self.logger.debug("🔗 API dependency resolution step completed using hash-based change detection")
92
+ original_func = getattr(wrapper_func, "_mesh_original_func", None)
93
+ func_name = original_func.__name__ if original_func else "unknown"
94
+ dependencies = getattr(wrapper_func, "_mesh_dependencies", [])
95
+ self.logger.debug(
96
+ f" 📋 {func_id} -> {func_name} (deps: {dependencies})"
97
+ )
98
+
99
+ self.logger.debug(
100
+ "🔗 API dependency resolution step completed using hash-based change detection"
101
+ )
92
102
 
93
103
  except Exception as e:
94
104
  result.status = PipelineStatus.FAILED
@@ -100,14 +110,17 @@ class APIDependencyResolutionStep(PipelineStep):
100
110
 
101
111
  def _extract_dependency_state(
102
112
  self, heartbeat_response: dict[str, Any]
103
- ) -> dict[str, dict[str, dict[str, str]]]:
113
+ ) -> dict[str, list[dict[str, Any]]]:
104
114
  """Extract dependency state structure from heartbeat response.
105
115
 
116
+ Preserves array structure and order from registry to support multiple
117
+ dependencies with the same capability name (e.g., different versions/tags).
118
+
106
119
  For API services, dependencies are typically under a single function
107
120
  (usually "api_endpoint_handler") but we still follow the same pattern.
108
121
 
109
122
  Returns:
110
- {function_name: {capability: {endpoint, function_name, status}}}
123
+ {function_name: [{capability, endpoint, function_name, status, agent_id, kwargs}, ...]}
111
124
  """
112
125
  state = {}
113
126
  dependencies_resolved = heartbeat_response.get("dependencies_resolved", {})
@@ -116,7 +129,7 @@ class APIDependencyResolutionStep(PipelineStep):
116
129
  if not isinstance(dependency_list, list):
117
130
  continue
118
131
 
119
- state[function_name] = {}
132
+ state[function_name] = []
120
133
  for dep_resolution in dependency_list:
121
134
  if (
122
135
  not isinstance(dep_resolution, dict)
@@ -124,14 +137,17 @@ class APIDependencyResolutionStep(PipelineStep):
124
137
  ):
125
138
  continue
126
139
 
127
- capability = dep_resolution["capability"]
128
- state[function_name][capability] = {
129
- "endpoint": dep_resolution.get("endpoint", ""),
130
- "function_name": dep_resolution.get("function_name", ""),
131
- "status": dep_resolution.get("status", ""),
132
- "agent_id": dep_resolution.get("agent_id", ""),
133
- "kwargs": dep_resolution.get("kwargs", {}), # Include kwargs config
134
- }
140
+ # Preserve array structure to maintain order and support duplicate capabilities
141
+ state[function_name].append(
142
+ {
143
+ "capability": dep_resolution["capability"],
144
+ "endpoint": dep_resolution.get("endpoint", ""),
145
+ "function_name": dep_resolution.get("function_name", ""),
146
+ "status": dep_resolution.get("status", ""),
147
+ "agent_id": dep_resolution.get("agent_id", ""),
148
+ "kwargs": dep_resolution.get("kwargs", {}),
149
+ }
150
+ )
135
151
 
136
152
  return state
137
153
 
@@ -220,44 +236,61 @@ class APIDependencyResolutionStep(PipelineStep):
220
236
 
221
237
  injector = get_global_injector()
222
238
 
223
- # Step 1: Collect all capabilities that should exist according to registry
224
- target_capabilities = set()
225
- for function_name, dependencies in current_state.items():
226
- for capability in dependencies.keys():
227
- target_capabilities.add(capability)
228
-
229
- # Step 2: Find existing capabilities that need to be removed (unwired)
239
+ # Step 1: Collect all dependency keys (func_id:dep_index) that should exist
240
+ # Map tool names to func_ids first
241
+ from ...engine.decorator_registry import DecoratorRegistry
242
+
243
+ tool_name_to_func_id = {}
244
+ mesh_tools = DecoratorRegistry.get_mesh_tools()
245
+ for tool_name, decorated_func in mesh_tools.items():
246
+ func = decorated_func.function
247
+ func_id = f"{func.__module__}.{func.__qualname__}"
248
+ tool_name_to_func_id[tool_name] = func_id
249
+
250
+ target_dependency_keys = set()
251
+ for function_name, dependency_list in current_state.items():
252
+ # Map tool name to func_id
253
+ func_id = tool_name_to_func_id.get(function_name, function_name)
254
+ for dep_index in range(len(dependency_list)):
255
+ dep_key = f"{func_id}:dep_{dep_index}"
256
+ target_dependency_keys.add(dep_key)
257
+
258
+ # Step 2: Find existing dependency keys that need to be removed (unwired)
230
259
  # This handles the case where registry stops reporting some dependencies
231
- existing_capabilities = (
260
+ existing_dependency_keys = (
232
261
  set(injector._dependencies.keys())
233
262
  if hasattr(injector, "_dependencies")
234
263
  else set()
235
264
  )
236
- capabilities_to_remove = existing_capabilities - target_capabilities
265
+ keys_to_remove = existing_dependency_keys - target_dependency_keys
237
266
 
238
267
  unwired_count = 0
239
- for capability in capabilities_to_remove:
240
- await injector.unregister_dependency(capability)
268
+ for dep_key in keys_to_remove:
269
+ await injector.unregister_dependency(dep_key)
241
270
  unwired_count += 1
242
271
  self.logger.info(
243
- f"🗑️ Unwired API dependency '{capability}' (no longer reported by registry)"
272
+ f"🗑️ Unwired API dependency '{dep_key}' (no longer reported by registry)"
244
273
  )
245
274
 
246
- # Step 3: Apply all dependency updates for capabilities that should exist
275
+ # Step 3: Apply all dependency updates using positional indexing
247
276
  updated_count = 0
248
- for function_name, dependencies in current_state.items():
249
- for capability, dep_info in dependencies.items():
277
+ for function_name, dependency_list in current_state.items():
278
+ # Map tool name to func_id (using mapping from Step 1)
279
+ func_id = tool_name_to_func_id.get(function_name, function_name)
280
+
281
+ for dep_index, dep_info in enumerate(dependency_list):
250
282
  status = dep_info["status"]
251
283
  endpoint = dep_info["endpoint"]
252
284
  dep_function_name = dep_info["function_name"]
253
- kwargs_config = dep_info.get("kwargs", {}) # Extract kwargs config
285
+ capability = dep_info["capability"]
286
+ kwargs_config = dep_info.get("kwargs", {})
254
287
 
255
288
  if status == "available" and endpoint and dep_function_name:
256
289
  # Import here to avoid circular imports
257
290
  import os
258
291
 
259
- from ...engine.unified_mcp_proxy import EnhancedUnifiedMCPProxy
260
292
  from ...engine.self_dependency_proxy import SelfDependencyProxy
293
+ from ...engine.unified_mcp_proxy import EnhancedUnifiedMCPProxy
261
294
 
262
295
  # Get current agent ID for self-dependency detection
263
296
  current_agent_id = None
@@ -315,27 +348,37 @@ class APIDependencyResolutionStep(PipelineStep):
315
348
  kwargs_config=kwargs_config,
316
349
  )
317
350
 
318
- # Update in injector (this will update ALL route handlers that depend on this capability)
319
- self.logger.debug(f"🔄 Before update: registering {capability} = {type(new_proxy).__name__}")
320
- await injector.register_dependency(capability, new_proxy)
351
+ # Register with composite key using func_id (not tool name) to match injector lookup
352
+ dep_key = f"{func_id}:dep_{dep_index}"
353
+ self.logger.debug(
354
+ f"🔄 Before update: registering {dep_key} = {type(new_proxy).__name__}"
355
+ )
356
+ await injector.register_dependency(dep_key, new_proxy)
321
357
  updated_count += 1
322
-
358
+
323
359
  # Log which functions will be affected
324
- affected_functions = injector._dependency_mapping.get(capability, set())
325
- self.logger.debug(f"🎯 Functions affected by '{capability}' update: {list(affected_functions)}")
326
-
360
+ affected_functions = injector._dependency_mapping.get(
361
+ dep_key, set()
362
+ )
363
+ self.logger.debug(
364
+ f"🎯 Functions affected by '{capability}' at position {dep_index}: {list(affected_functions)}"
365
+ )
366
+
327
367
  self.logger.info(
328
- f"🔄 Updated API dependency '{capability}' → {endpoint}/{dep_function_name} "
368
+ f"🔄 Updated API dependency '{capability}' at position {dep_index} → {endpoint}/{dep_function_name} "
329
369
  f"(proxy: EnhancedUnifiedMCPProxy - consistent with MCP pipeline)"
330
370
  )
371
+ self.logger.debug(
372
+ f"🔗 Registered dependency '{capability}' at position {dep_index} with key '{dep_key}' (func_id: {func_id})"
373
+ )
331
374
  else:
332
375
  if status != "available":
333
376
  self.logger.debug(
334
- f"⚠️ API dependency '{capability}' not available: {status}"
377
+ f"⚠️ API dependency '{capability}' at position {dep_index} not available: {status}"
335
378
  )
336
379
  else:
337
380
  self.logger.warning(
338
- f"⚠️ Cannot update API dependency '{capability}': missing endpoint or function_name"
381
+ f"⚠️ Cannot update API dependency '{capability}' at position {dep_index}: missing endpoint or function_name"
339
382
  )
340
383
 
341
384
  # Store new hash for next comparison (use global variable)
@@ -364,10 +407,12 @@ class APIDependencyResolutionStep(PipelineStep):
364
407
  )
365
408
  # Don't raise - this should not break the heartbeat loop
366
409
 
367
- def _determine_api_proxy_type_for_capability(self, capability: str, injector) -> str:
410
+ def _determine_api_proxy_type_for_capability(
411
+ self, capability: str, injector
412
+ ) -> str:
368
413
  """
369
414
  Determine which proxy type to use for API route handlers.
370
-
415
+
371
416
  For API services, we need to check the parameter types used in FastAPI route handlers
372
417
  that depend on this capability. This is different from MCP tools because route handlers
373
418
  are wrapped differently.
@@ -449,17 +494,21 @@ class APIDependencyResolutionStep(PipelineStep):
449
494
  return "MCPClientProxy" # Safe default
450
495
 
451
496
  def _create_proxy_for_api(
452
- self, proxy_type: str, endpoint: str, dep_function_name: str, kwargs_config: dict
497
+ self,
498
+ proxy_type: str,
499
+ endpoint: str,
500
+ dep_function_name: str,
501
+ kwargs_config: dict,
453
502
  ):
454
503
  """
455
504
  Create the appropriate proxy instance for API route handlers.
456
-
505
+
457
506
  Args:
458
507
  proxy_type: "FullMCPProxy" or "MCPClientProxy"
459
508
  endpoint: Target endpoint URL
460
509
  dep_function_name: Target function name
461
510
  kwargs_config: Additional configuration (timeout, retry, etc.)
462
-
511
+
463
512
  Returns:
464
513
  Proxy instance
465
514
  """
@@ -503,4 +552,4 @@ class APIDependencyResolutionStep(PipelineStep):
503
552
  kwargs_config=kwargs_config,
504
553
  )
505
554
  self.logger.debug("🔧 Created MCPClientProxy for API (no kwargs)")
506
- return proxy
555
+ return proxy
@@ -82,11 +82,14 @@ class DependencyResolutionStep(PipelineStep):
82
82
 
83
83
  def _extract_dependency_state(
84
84
  self, heartbeat_response: dict[str, Any]
85
- ) -> dict[str, dict[str, dict[str, str]]]:
85
+ ) -> dict[str, list[dict[str, Any]]]:
86
86
  """Extract dependency state structure from heartbeat response.
87
87
 
88
+ Preserves array structure and order from registry to support multiple
89
+ dependencies with the same capability name (e.g., different versions/tags).
90
+
88
91
  Returns:
89
- {function_name: {capability: {endpoint, function_name, status}}}
92
+ {function_name: [{capability, endpoint, function_name, status, agent_id, kwargs}, ...]}
90
93
  """
91
94
  state = {}
92
95
  dependencies_resolved = heartbeat_response.get("dependencies_resolved", {})
@@ -95,7 +98,7 @@ class DependencyResolutionStep(PipelineStep):
95
98
  if not isinstance(dependency_list, list):
96
99
  continue
97
100
 
98
- state[function_name] = {}
101
+ state[function_name] = []
99
102
  for dep_resolution in dependency_list:
100
103
  if (
101
104
  not isinstance(dep_resolution, dict)
@@ -103,13 +106,17 @@ class DependencyResolutionStep(PipelineStep):
103
106
  ):
104
107
  continue
105
108
 
106
- capability = dep_resolution["capability"]
107
- state[function_name][capability] = {
108
- "endpoint": dep_resolution.get("endpoint", ""),
109
- "function_name": dep_resolution.get("function_name", ""),
110
- "status": dep_resolution.get("status", ""),
111
- "agent_id": dep_resolution.get("agent_id", ""),
112
- }
109
+ # Preserve array structure to maintain order and support duplicate capabilities
110
+ state[function_name].append(
111
+ {
112
+ "capability": dep_resolution["capability"],
113
+ "endpoint": dep_resolution.get("endpoint", ""),
114
+ "function_name": dep_resolution.get("function_name", ""),
115
+ "status": dep_resolution.get("status", ""),
116
+ "agent_id": dep_resolution.get("agent_id", ""),
117
+ "kwargs": dep_resolution.get("kwargs", {}),
118
+ }
119
+ )
113
120
 
114
121
  return state
115
122
 
@@ -194,37 +201,54 @@ class DependencyResolutionStep(PipelineStep):
194
201
 
195
202
  injector = get_global_injector()
196
203
 
197
- # Step 1: Collect all capabilities that should exist according to registry
198
- target_capabilities = set()
199
- for function_name, dependencies in current_state.items():
200
- for capability in dependencies.keys():
201
- target_capabilities.add(capability)
202
-
203
- # Step 2: Find existing capabilities that need to be removed (unwired)
204
+ # Step 1: Collect all dependency keys (func_id:dep_index) that should exist
205
+ # Map tool names to func_ids first
206
+ from ...engine.decorator_registry import DecoratorRegistry
207
+
208
+ tool_name_to_func_id = {}
209
+ mesh_tools = DecoratorRegistry.get_mesh_tools()
210
+ for tool_name, decorated_func in mesh_tools.items():
211
+ func = decorated_func.function
212
+ func_id = f"{func.__module__}.{func.__qualname__}"
213
+ tool_name_to_func_id[tool_name] = func_id
214
+
215
+ target_dependency_keys = set()
216
+ for function_name, dependency_list in current_state.items():
217
+ # Map tool name to func_id
218
+ func_id = tool_name_to_func_id.get(function_name, function_name)
219
+ for dep_index in range(len(dependency_list)):
220
+ dep_key = f"{func_id}:dep_{dep_index}"
221
+ target_dependency_keys.add(dep_key)
222
+
223
+ # Step 2: Find existing dependency keys that need to be removed (unwired)
204
224
  # This handles the case where registry stops reporting some dependencies
205
- existing_capabilities = (
225
+ existing_dependency_keys = (
206
226
  set(injector._dependencies.keys())
207
227
  if hasattr(injector, "_dependencies")
208
228
  else set()
209
229
  )
210
- capabilities_to_remove = existing_capabilities - target_capabilities
230
+ keys_to_remove = existing_dependency_keys - target_dependency_keys
211
231
 
212
232
  unwired_count = 0
213
- for capability in capabilities_to_remove:
214
- await injector.unregister_dependency(capability)
233
+ for dep_key in keys_to_remove:
234
+ await injector.unregister_dependency(dep_key)
215
235
  unwired_count += 1
216
236
  self.logger.info(
217
- f"🗑️ Unwired dependency '{capability}' (no longer reported by registry)"
237
+ f"🗑️ Unwired dependency '{dep_key}' (no longer reported by registry)"
218
238
  )
219
239
 
220
- # Step 3: Apply all dependency updates for capabilities that should exist
240
+ # Step 3: Apply all dependency updates using positional indexing
221
241
  updated_count = 0
222
- for function_name, dependencies in current_state.items():
223
- for capability, dep_info in dependencies.items():
242
+ for function_name, dependency_list in current_state.items():
243
+ # Map tool name to func_id (using mapping from Step 1)
244
+ func_id = tool_name_to_func_id.get(function_name, function_name)
245
+
246
+ for dep_index, dep_info in enumerate(dependency_list):
224
247
  status = dep_info["status"]
225
248
  endpoint = dep_info["endpoint"]
226
249
  dep_function_name = dep_info["function_name"]
227
- kwargs_config = dep_info.get("kwargs", {}) # NEW: Extract kwargs
250
+ capability = dep_info["capability"]
251
+ kwargs_config = dep_info.get("kwargs", {})
228
252
 
229
253
  if status == "available" and endpoint and dep_function_name:
230
254
  # Import here to avoid circular imports
@@ -311,17 +335,21 @@ class DependencyResolutionStep(PipelineStep):
311
335
  f"timeout={kwargs_config.get('timeout', 30)}s, streaming={kwargs_config.get('streaming', False)}"
312
336
  )
313
337
 
314
- # Update in injector (this will update ALL functions that depend on this capability)
315
- await injector.register_dependency(capability, new_proxy)
338
+ # Register with composite key using func_id (not tool name) to match injector lookup
339
+ dep_key = f"{func_id}:dep_{dep_index}"
340
+ await injector.register_dependency(dep_key, new_proxy)
316
341
  updated_count += 1
342
+ self.logger.debug(
343
+ f"🔗 Registered dependency '{capability}' at position {dep_index} with key '{dep_key}' (func_id: {func_id})"
344
+ )
317
345
  else:
318
346
  if status != "available":
319
347
  self.logger.debug(
320
- f"⚠️ Dependency '{capability}' not available: {status}"
348
+ f"⚠️ Dependency '{capability}' at position {dep_index} not available: {status}"
321
349
  )
322
350
  else:
323
351
  self.logger.warning(
324
- f"⚠️ Cannot update dependency '{capability}': missing endpoint or function_name"
352
+ f"⚠️ Cannot update dependency '{capability}' at position {dep_index}: missing endpoint or function_name"
325
353
  )
326
354
 
327
355
  # Store new hash for next comparison (use global variable)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-mesh
3
- Version: 0.5.6
3
+ Version: 0.5.7
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,15 +1,15 @@
1
- _mcp_mesh/__init__.py,sha256=uw4Yj9HoBfp-_CyH8izKGYKrrCI-dF9uwMLMNgkIMN0,2719
1
+ _mcp_mesh/__init__.py,sha256=CTRyqemXkVmKL85NBmkaOB9AN-FOZxseOKEnV_s7AMk,2719
2
2
  _mcp_mesh/engine/__init__.py,sha256=2ennzbo7yJcpkXO9BqN69TruLjJfmJY4Y5VEsG644K4,3630
3
3
  _mcp_mesh/engine/async_mcp_client.py,sha256=UcbQjxtgVfeRw6DHTZhAzN1gkcKlTg-lUPEePRPQWAU,6306
4
4
  _mcp_mesh/engine/decorator_registry.py,sha256=0-GI7DILpbB6GRLIYhZu0DcdHEA62d89jDYxkK1ywiM,22422
5
- _mcp_mesh/engine/dependency_injector.py,sha256=B1iRxtIXq_z4LnJ6NTs0ZPdEIlijrMJv5KbDpQbq9iw,23752
5
+ _mcp_mesh/engine/dependency_injector.py,sha256=k6PzG9te1J5DbuDV2OrErXM3AcV1X6_ZvCy5KFYo1nk,27496
6
6
  _mcp_mesh/engine/full_mcp_proxy.py,sha256=PlRv7GSKqn5riOCqeCVulVdtq3z1Ug76mOkwMsOFHXw,25297
7
7
  _mcp_mesh/engine/http_wrapper.py,sha256=PeYR7HkJVX7Hb0W-uYSMO-Y14VqCyrPlWzA6vnwf1Tw,21640
8
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
- _mcp_mesh/engine/signature_analyzer.py,sha256=AxBuFq8_TCT2afw_XDF4AjWPqdG6ST5jfbw2BDdp9Mo,7482
12
+ _mcp_mesh/engine/signature_analyzer.py,sha256=w5mvnRo1cc31the6OZln5keOVJphL26V6jDAHVNA29A,7946
13
13
  _mcp_mesh/engine/unified_mcp_proxy.py,sha256=33xm-jABTYUwr_uIB6mx4yNlNJrsvstCHJOMTdcm-pg,36365
14
14
  _mcp_mesh/generated/.openapi-generator-ignore,sha256=-d-Y-RVAZRrHw36jO0b79oDXpfA8rZdBGPCG4Vs_rUs,227
15
15
  _mcp_mesh/generated/.openapi-generator/FILES,sha256=BXFXHe4FTV5GFUTNjMhmvOrVcYVlHJc5O-3UXyp8OCY,2169
@@ -57,7 +57,7 @@ _mcp_mesh/generated/mcp_mesh_registry_client/models/standardized_dependency.py,s
57
57
  _mcp_mesh/generated/mcp_mesh_registry_client/models/trace_event.py,sha256=9Q_8WaVl0MxswRnHpkqq9GKnvOW54HW4tkrTM9oda14,4461
58
58
  _mcp_mesh/pipeline/__init__.py,sha256=9Aplh4m1z-rYTQys0JQLYlq9wTPdI72eSOhUPqcnvpA,1557
59
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
60
+ _mcp_mesh/pipeline/api_heartbeat/api_dependency_resolution.py,sha256=uVv7KjEfVO_5Ny3fgC_DD9B8dg_IIUdJmtGVrTaSStU,25312
61
61
  _mcp_mesh/pipeline/api_heartbeat/api_fast_heartbeat_check.py,sha256=PY4bbuZgxy3r0ccuBl-OuJvcPSMhyGz4FomxwYFhuvM,4821
62
62
  _mcp_mesh/pipeline/api_heartbeat/api_health_check.py,sha256=kDmFeOG_4tyqyJSBZjPcc7xTzGpP4vq6ObW_WBqXvzM,5130
63
63
  _mcp_mesh/pipeline/api_heartbeat/api_heartbeat_orchestrator.py,sha256=uBswzWOBzU8p_C0AE2DF8UwIWG4rP2zecHfPqKzNuC0,10367
@@ -73,7 +73,7 @@ _mcp_mesh/pipeline/api_startup/middleware_integration.py,sha256=ybImXZlmIR6yA-wY
73
73
  _mcp_mesh/pipeline/api_startup/route_collection.py,sha256=UjA-F5_RbGVU5TfDT19Np5_x2PtYkNn2mGFyivDsk24,2031
74
74
  _mcp_mesh/pipeline/api_startup/route_integration.py,sha256=aMT7p7cwK8N3tZBRqeGQF8upc7tU-Exj6Dz0a4cSBhU,13441
75
75
  _mcp_mesh/pipeline/mcp_heartbeat/__init__.py,sha256=nRNjZ3VD_9bPLQuJ6Nc02gE7KSLcMP7TMquB0hP6hHs,844
76
- _mcp_mesh/pipeline/mcp_heartbeat/dependency_resolution.py,sha256=nHjKSM4Yyn05wnfw1dI4VGroHZy8lRkDihnZfFHeF8U,16217
76
+ _mcp_mesh/pipeline/mcp_heartbeat/dependency_resolution.py,sha256=qQ1KhrRYroYL2nnjLufXsXaaAO593GnuvJrRy_Ws97k,17764
77
77
  _mcp_mesh/pipeline/mcp_heartbeat/fast_heartbeat_check.py,sha256=QTzYdL81WERkOaBVOgNbFQh1ddTn70urNtyIgtFTudA,4465
78
78
  _mcp_mesh/pipeline/mcp_heartbeat/heartbeat_orchestrator.py,sha256=uB9o298X7GbOaKUw4ij_fUAOCpK0n2brx_oWqWGTXFY,11296
79
79
  _mcp_mesh/pipeline/mcp_heartbeat/heartbeat_pipeline.py,sha256=Jb7EVJO13trUVO3aCSgzGqAtoc4vie5kDrYLZtOkiXg,11601
@@ -118,7 +118,7 @@ _mcp_mesh/tracing/utils.py,sha256=t9lJuTH7CeuzAiiAaD0WxsJMFJPdzZFR0w6-vyR9f2E,38
118
118
  mesh/__init__.py,sha256=l5RSMV8Kx0h7cvku8YkZPbTHjEPWciGT0bcEB2O_eNU,3242
119
119
  mesh/decorators.py,sha256=oBWoRE-FA3qUGygAUtk3-eAYBckwTGfTzvXOgCag4ys,36678
120
120
  mesh/types.py,sha256=g37DXAzya-xGPa1_WKlW3T3_VqyTn8ZVepIDSrhBTkc,10815
121
- mcp_mesh-0.5.6.dist-info/METADATA,sha256=Mh8nwL5wjm53WMFPOqcVHCRmcvbPa4sgMmX7bQV-yiw,4879
122
- mcp_mesh-0.5.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
123
- mcp_mesh-0.5.6.dist-info/licenses/LICENSE,sha256=_EBQHRQThv9FPOLc5eFOUdeeRO0mYwChC7cx60dM1tM,1078
124
- mcp_mesh-0.5.6.dist-info/RECORD,,
121
+ mcp_mesh-0.5.7.dist-info/METADATA,sha256=mouhZmaO4Ce2rGSiCRXJXVNA-aRxBc-seygYUCKmJhI,4879
122
+ mcp_mesh-0.5.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
123
+ mcp_mesh-0.5.7.dist-info/licenses/LICENSE,sha256=_EBQHRQThv9FPOLc5eFOUdeeRO0mYwChC7cx60dM1tM,1078
124
+ mcp_mesh-0.5.7.dist-info/RECORD,,