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 +1 -1
- _mcp_mesh/engine/dependency_injector.py +122 -57
- _mcp_mesh/engine/signature_analyzer.py +23 -14
- _mcp_mesh/pipeline/api_heartbeat/api_dependency_resolution.py +107 -58
- _mcp_mesh/pipeline/mcp_heartbeat/dependency_resolution.py +58 -30
- {mcp_mesh-0.5.6.dist-info → mcp_mesh-0.5.7.dist-info}/METADATA +1 -1
- {mcp_mesh-0.5.6.dist-info → mcp_mesh-0.5.7.dist-info}/RECORD +9 -9
- {mcp_mesh-0.5.6.dist-info → mcp_mesh-0.5.7.dist-info}/WHEEL +0 -0
- {mcp_mesh-0.5.6.dist-info → mcp_mesh-0.5.7.dist-info}/licenses/LICENSE +0 -0
_mcp_mesh/__init__.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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 =
|
|
384
|
-
|
|
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
|
-
|
|
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 =
|
|
477
|
-
|
|
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
|
-
|
|
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(
|
|
510
|
-
"""Called when a dependency changes."""
|
|
511
|
-
if
|
|
512
|
-
dependency_wrapper._mesh_injected_deps
|
|
513
|
-
|
|
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
|
-
|
|
516
|
-
|
|
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
|
|
176
|
-
|
|
177
|
-
|
|
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
|
|
186
|
-
hasattr(arg, "__name__")
|
|
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(
|
|
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 =
|
|
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(
|
|
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,
|
|
87
|
-
func_name = original_func.__name__ if original_func else
|
|
88
|
-
dependencies = getattr(wrapper_func,
|
|
89
|
-
self.logger.debug(
|
|
90
|
-
|
|
91
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
128
|
-
state[function_name]
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
-
|
|
260
|
+
existing_dependency_keys = (
|
|
232
261
|
set(injector._dependencies.keys())
|
|
233
262
|
if hasattr(injector, "_dependencies")
|
|
234
263
|
else set()
|
|
235
264
|
)
|
|
236
|
-
|
|
265
|
+
keys_to_remove = existing_dependency_keys - target_dependency_keys
|
|
237
266
|
|
|
238
267
|
unwired_count = 0
|
|
239
|
-
for
|
|
240
|
-
await injector.unregister_dependency(
|
|
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 '{
|
|
272
|
+
f"🗑️ Unwired API dependency '{dep_key}' (no longer reported by registry)"
|
|
244
273
|
)
|
|
245
274
|
|
|
246
|
-
# Step 3: Apply all dependency updates
|
|
275
|
+
# Step 3: Apply all dependency updates using positional indexing
|
|
247
276
|
updated_count = 0
|
|
248
|
-
for function_name,
|
|
249
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
319
|
-
|
|
320
|
-
|
|
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(
|
|
325
|
-
|
|
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(
|
|
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,
|
|
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,
|
|
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
|
|
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
|
-
|
|
107
|
-
state[function_name]
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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
|
-
|
|
225
|
+
existing_dependency_keys = (
|
|
206
226
|
set(injector._dependencies.keys())
|
|
207
227
|
if hasattr(injector, "_dependencies")
|
|
208
228
|
else set()
|
|
209
229
|
)
|
|
210
|
-
|
|
230
|
+
keys_to_remove = existing_dependency_keys - target_dependency_keys
|
|
211
231
|
|
|
212
232
|
unwired_count = 0
|
|
213
|
-
for
|
|
214
|
-
await injector.unregister_dependency(
|
|
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 '{
|
|
237
|
+
f"🗑️ Unwired dependency '{dep_key}' (no longer reported by registry)"
|
|
218
238
|
)
|
|
219
239
|
|
|
220
|
-
# Step 3: Apply all dependency updates
|
|
240
|
+
# Step 3: Apply all dependency updates using positional indexing
|
|
221
241
|
updated_count = 0
|
|
222
|
-
for function_name,
|
|
223
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
315
|
-
|
|
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.
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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.
|
|
122
|
-
mcp_mesh-0.5.
|
|
123
|
-
mcp_mesh-0.5.
|
|
124
|
-
mcp_mesh-0.5.
|
|
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,,
|
|
File without changes
|
|
File without changes
|