mcp-mesh 0.8.0__tar.gz → 0.8.0b1__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 (86) hide show
  1. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/.gitignore +0 -8
  2. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/PKG-INFO +6 -7
  3. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/README.md +4 -4
  4. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/__init__.py +1 -1
  5. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/dependency_injector.py +9 -9
  6. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/mesh_llm_agent.py +14 -36
  7. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/mesh_llm_agent_injector.py +43 -78
  8. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/signature_analyzer.py +68 -58
  9. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/unified_mcp_proxy.py +1 -1
  10. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/api_heartbeat/rust_api_heartbeat.py +1 -5
  11. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/mcp_heartbeat/rust_heartbeat.py +6 -21
  12. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/mcp_startup/fastapiserver_setup.py +13 -37
  13. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/mcp_startup/startup_orchestrator.py +9 -9
  14. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/utils/fastmcp_schema_extractor.py +3 -3
  15. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/mesh/__init__.py +2 -12
  16. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/mesh/decorators.py +51 -182
  17. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/mesh/helpers.py +0 -52
  18. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/mesh/types.py +13 -40
  19. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/pyproject.toml +5 -6
  20. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/LICENSE +0 -0
  21. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/__init__.py +0 -0
  22. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/async_mcp_client.py +0 -0
  23. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/base_injector.py +0 -0
  24. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/decorator_registry.py +0 -0
  25. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/http_wrapper.py +0 -0
  26. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/llm_config.py +0 -0
  27. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/llm_errors.py +0 -0
  28. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/provider_handlers/__init__.py +0 -0
  29. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/provider_handlers/base_provider_handler.py +0 -0
  30. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/provider_handlers/claude_handler.py +0 -0
  31. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/provider_handlers/gemini_handler.py +0 -0
  32. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/provider_handlers/generic_handler.py +0 -0
  33. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/provider_handlers/openai_handler.py +0 -0
  34. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/provider_handlers/provider_handler_registry.py +0 -0
  35. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/response_parser.py +0 -0
  36. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/self_dependency_proxy.py +0 -0
  37. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/session_aware_client.py +0 -0
  38. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/session_manager.py +0 -0
  39. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/tool_executor.py +0 -0
  40. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/tool_schema_builder.py +0 -0
  41. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/__init__.py +0 -0
  42. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/api_heartbeat/__init__.py +0 -0
  43. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/api_heartbeat/api_lifespan_integration.py +0 -0
  44. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/api_startup/__init__.py +0 -0
  45. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/api_startup/api_pipeline.py +0 -0
  46. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/api_startup/api_server_setup.py +0 -0
  47. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/api_startup/fastapi_discovery.py +0 -0
  48. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/api_startup/middleware_integration.py +0 -0
  49. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/api_startup/route_collection.py +0 -0
  50. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/api_startup/route_integration.py +0 -0
  51. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/mcp_heartbeat/__init__.py +0 -0
  52. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/mcp_startup/__init__.py +0 -0
  53. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/mcp_startup/configuration.py +0 -0
  54. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/mcp_startup/decorator_collection.py +0 -0
  55. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/mcp_startup/fastmcpserver_discovery.py +0 -0
  56. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/mcp_startup/heartbeat_loop.py +0 -0
  57. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/mcp_startup/heartbeat_preparation.py +0 -0
  58. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/mcp_startup/lifespan_factory.py +0 -0
  59. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/mcp_startup/server_discovery.py +0 -0
  60. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/mcp_startup/startup_pipeline.py +0 -0
  61. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/shared/__init__.py +0 -0
  62. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/shared/base_step.py +0 -0
  63. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/shared/mesh_pipeline.py +0 -0
  64. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/shared/pipeline_types.py +0 -0
  65. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/reload.py +0 -0
  66. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/reload_runner.py +0 -0
  67. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/shared/__init__.py +0 -0
  68. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/shared/config_resolver.py +0 -0
  69. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/shared/content_extractor.py +0 -0
  70. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/shared/defaults.py +0 -0
  71. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/shared/fast_heartbeat_status.py +0 -0
  72. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/shared/fastapi_middleware_manager.py +0 -0
  73. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/shared/health_check_manager.py +0 -0
  74. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/shared/host_resolver.py +0 -0
  75. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/shared/logging_config.py +0 -0
  76. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/shared/server_discovery.py +0 -0
  77. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/shared/simple_shutdown.py +0 -0
  78. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/shared/sse_parser.py +0 -0
  79. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/shared/support_types.py +0 -0
  80. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/tracing/agent_context_helper.py +0 -0
  81. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/tracing/context.py +0 -0
  82. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/tracing/execution_tracer.py +0 -0
  83. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/tracing/fastapi_tracing_middleware.py +0 -0
  84. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/tracing/redis_metadata_publisher.py +0 -0
  85. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/tracing/trace_context_helper.py +0 -0
  86. {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/tracing/utils.py +0 -0
@@ -255,11 +255,3 @@ rust-core-implementation.org
255
255
  src/runtime/core/index.js
256
256
  src/runtime/core/index.d.ts
257
257
  test/
258
- scaffold-test/
259
-
260
- # Test suite build outputs
261
- tests/src-tests/out/
262
- tests/lib-tests/out/
263
-
264
- # Allow test suite build artifacts
265
- !tests/**/build/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-mesh
3
- Version: 0.8.0
3
+ Version: 0.8.0b1
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
@@ -18,7 +18,6 @@ Classifier: Operating System :: OS Independent
18
18
  Classifier: Programming Language :: Python :: 3
19
19
  Classifier: Programming Language :: Python :: 3.11
20
20
  Classifier: Programming Language :: Python :: 3.12
21
- Classifier: Programming Language :: Python :: 3.13
22
21
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
22
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
23
  Classifier: Topic :: System :: Distributed Computing
@@ -31,7 +30,7 @@ Requires-Dist: fastmcp<3.0.0,>=2.8.0
31
30
  Requires-Dist: httpx<1.0.0,>=0.25.0
32
31
  Requires-Dist: jinja2>=3.1.0
33
32
  Requires-Dist: litellm>=1.30.0
34
- Requires-Dist: mcp-mesh-core>=0.8.0
33
+ Requires-Dist: mcp-mesh-core>=0.8.0b1
35
34
  Requires-Dist: mcp<2.0.0,>=1.9.0
36
35
  Requires-Dist: prometheus-client<1.0.0,>=0.19.0
37
36
  Requires-Dist: pydantic<3.0.0,>=2.4.0
@@ -79,7 +78,7 @@ pip install mcp-mesh
79
78
  import mesh
80
79
 
81
80
  # Import types from public API
82
- from mesh.types import McpMeshTool
81
+ from mesh.types import McpMeshAgent
83
82
 
84
83
  # Define your agent
85
84
  @mesh.agent(name="hello-world", http_port=9090)
@@ -93,11 +92,11 @@ class HelloWorldAgent:
93
92
  dependencies=["date_service"],
94
93
  description="Greeting function with date dependency injection"
95
94
  )
96
- def greet(name: str = "World", date_tool: McpMeshTool = None) -> str:
95
+ def greet(name: str = "World", systemDate: McpMeshAgent = None) -> str:
97
96
  """Greeting function with automatic dependency injection."""
98
- if date_tool is not None:
97
+ if systemDate is not None:
99
98
  try:
100
- current_date = date_tool()
99
+ current_date = systemDate()
101
100
  return f"Hello, {name}! Today is {current_date}"
102
101
  except Exception:
103
102
  pass
@@ -14,7 +14,7 @@ pip install mcp-mesh
14
14
  import mesh
15
15
 
16
16
  # Import types from public API
17
- from mesh.types import McpMeshTool
17
+ from mesh.types import McpMeshAgent
18
18
 
19
19
  # Define your agent
20
20
  @mesh.agent(name="hello-world", http_port=9090)
@@ -28,11 +28,11 @@ class HelloWorldAgent:
28
28
  dependencies=["date_service"],
29
29
  description="Greeting function with date dependency injection"
30
30
  )
31
- def greet(name: str = "World", date_tool: McpMeshTool = None) -> str:
31
+ def greet(name: str = "World", systemDate: McpMeshAgent = None) -> str:
32
32
  """Greeting function with automatic dependency injection."""
33
- if date_tool is not None:
33
+ if systemDate is not None:
34
34
  try:
35
- current_date = date_tool()
35
+ current_date = systemDate()
36
36
  return f"Hello, {name}! Today is {current_date}"
37
37
  except Exception:
38
38
  pass
@@ -31,7 +31,7 @@ from .engine.decorator_registry import (
31
31
  get_decorator_stats,
32
32
  )
33
33
 
34
- __version__ = "0.8.0"
34
+ __version__ = "0.8.0b1"
35
35
 
36
36
  # Store reference to runtime processor if initialized
37
37
  _runtime_processor = None
@@ -27,8 +27,8 @@ def analyze_injection_strategy(func: Callable, dependencies: list[str]) -> list[
27
27
  Analyze function signature and determine injection strategy.
28
28
 
29
29
  Rules:
30
- 1. Single parameter: inject regardless of typing (with warning if not McpMeshTool)
31
- 2. Multiple parameters: only inject into McpMeshTool typed parameters
30
+ 1. Single parameter: inject regardless of typing (with warning if not McpMeshAgent)
31
+ 2. Multiple parameters: only inject into McpMeshAgent typed parameters
32
32
  3. Log warnings for mismatches and edge cases
33
33
 
34
34
  Args:
@@ -60,17 +60,17 @@ def analyze_injection_strategy(func: Callable, dependencies: list[str]) -> list[
60
60
  logger.warning(
61
61
  f"Single parameter '{param_name}' in function '{func_name}' found, "
62
62
  f"injecting {dependencies[0] if dependencies else 'dependency'} proxy "
63
- f"(consider typing as McpMeshTool for clarity)"
63
+ f"(consider typing as McpMeshAgent for clarity)"
64
64
  )
65
65
  return [0] # Inject into the single parameter
66
66
 
67
- # Multiple parameters rule: only inject into McpMeshTool typed parameters
67
+ # Multiple parameters rule: only inject into McpMeshAgent typed parameters
68
68
  if param_count > 1:
69
69
  if not mesh_positions:
70
70
  logger.warning(
71
71
  f"⚠️ Function '{func_name}' has {param_count} parameters but none are "
72
- f"typed as McpMeshTool. Skipping injection of {len(dependencies)} dependencies. "
73
- f"Consider typing dependency parameters as McpMeshTool."
72
+ f"typed as McpMeshAgent. Skipping injection of {len(dependencies)} dependencies. "
73
+ f"Consider typing dependency parameters as McpMeshAgent."
74
74
  )
75
75
  return []
76
76
 
@@ -80,7 +80,7 @@ def analyze_injection_strategy(func: Callable, dependencies: list[str]) -> list[
80
80
  excess_deps = dependencies[len(mesh_positions) :]
81
81
  logger.warning(
82
82
  f"Function '{func_name}' has {len(dependencies)} dependencies "
83
- f"but only {len(mesh_positions)} McpMeshTool parameters. "
83
+ f"but only {len(mesh_positions)} McpMeshAgent parameters. "
84
84
  f"Dependencies {excess_deps} will not be injected."
85
85
  )
86
86
  else:
@@ -88,7 +88,7 @@ def analyze_injection_strategy(func: Callable, dependencies: list[str]) -> list[
88
88
  params[pos].name for pos in mesh_positions[len(dependencies) :]
89
89
  ]
90
90
  logger.warning(
91
- f"Function '{func_name}' has {len(mesh_positions)} McpMeshTool parameters "
91
+ f"Function '{func_name}' has {len(mesh_positions)} McpMeshAgent parameters "
92
92
  f"but only {len(dependencies)} dependencies declared. "
93
93
  f"Parameters {excess_params} will remain None."
94
94
  )
@@ -104,7 +104,7 @@ class DependencyInjector:
104
104
  Manages dynamic dependency injection for mesh agents.
105
105
 
106
106
  This class:
107
- 1. Maintains a registry of available dependencies (McpMeshTool)
107
+ 1. Maintains a registry of available dependencies (McpMeshAgent)
108
108
  2. Coordinates with MeshLlmAgentInjector for LLM agent injection
109
109
  3. Tracks which functions depend on which services
110
110
  4. Updates function bindings when topology changes
@@ -14,12 +14,8 @@ from typing import Any, Dict, List, Literal, Optional, Union
14
14
  from pydantic import BaseModel
15
15
 
16
16
  from .llm_config import LLMConfig
17
- from .llm_errors import (
18
- LLMAPIError,
19
- MaxIterationsError,
20
- ResponseParseError,
21
- ToolExecutionError,
22
- )
17
+ from .llm_errors import (LLMAPIError, MaxIterationsError, ResponseParseError,
18
+ ToolExecutionError)
23
19
  from .provider_handlers import ProviderHandlerRegistry
24
20
  from .response_parser import ResponseParser
25
21
  from .tool_executor import ToolExecutor
@@ -27,7 +23,8 @@ from .tool_schema_builder import ToolSchemaBuilder
27
23
 
28
24
  # Import Jinja2 for template rendering
29
25
  try:
30
- from jinja2 import Environment, FileSystemLoader, Template, TemplateSyntaxError
26
+ from jinja2 import (Environment, FileSystemLoader, Template,
27
+ TemplateSyntaxError)
31
28
  except ImportError:
32
29
  Environment = None
33
30
  FileSystemLoader = None
@@ -636,14 +633,12 @@ IMPORTANT TOOL CALLING RULES:
636
633
  # Multi-turn conversation - use provided messages array
637
634
  messages = message.copy()
638
635
 
639
- # Only add/update system message if we have non-empty content
640
- # (Claude API rejects empty system messages - though decorator provides default)
641
- if system_content:
642
- if not messages or messages[0].get("role") != "system":
643
- messages.insert(0, {"role": "system", "content": system_content})
644
- else:
645
- # Replace existing system message with our constructed one
646
- messages[0] = {"role": "system", "content": system_content}
636
+ # Ensure system prompt is prepended if not already present
637
+ if not messages or messages[0].get("role") != "system":
638
+ messages.insert(0, {"role": "system", "content": system_content})
639
+ else:
640
+ # Replace existing system message with our constructed one
641
+ messages[0] = {"role": "system", "content": system_content}
647
642
 
648
643
  # Log conversation history
649
644
  logger.info(
@@ -651,17 +646,10 @@ IMPORTANT TOOL CALLING RULES:
651
646
  )
652
647
  else:
653
648
  # Single-turn - build messages array from string
654
- # Only include system message if non-empty (Claude API rejects empty system messages)
655
- if system_content:
656
- messages = [
657
- {"role": "system", "content": system_content},
658
- {"role": "user", "content": message},
659
- ]
660
- else:
661
- # Fallback for edge case where system_content is explicitly empty
662
- messages = [
663
- {"role": "user", "content": message},
664
- ]
649
+ messages = [
650
+ {"role": "system", "content": system_content},
651
+ {"role": "user", "content": message},
652
+ ]
665
653
 
666
654
  logger.info(f"🚀 Starting agentic loop for message: {message[:100]}...")
667
655
 
@@ -717,16 +705,6 @@ IMPORTANT TOOL CALLING RULES:
717
705
  if self.model:
718
706
  model_params["model"] = self.model
719
707
 
720
- # Issue #459: Include output_schema for provider to apply vendor-specific handling
721
- # (e.g., OpenAI needs response_format, not prompt-based JSON instructions)
722
- if self.output_type is not str and hasattr(
723
- self.output_type, "model_json_schema"
724
- ):
725
- model_params["output_schema"] = (
726
- self.output_type.model_json_schema()
727
- )
728
- model_params["output_type_name"] = self.output_type.__name__
729
-
730
708
  logger.debug(
731
709
  f"📤 Delegating to mesh provider with handler-prepared params: "
732
710
  f"keys={list(model_params.keys())}"
@@ -161,70 +161,36 @@ class MeshLlmAgentInjector(BaseInjector):
161
161
  # Create UnifiedMCPProxy for the provider
162
162
  provider_proxy = self._create_provider_proxy(provider_data)
163
163
 
164
- # Update only provider-related fields, preserving tool data if already set.
165
- # This avoids race conditions where provider and tools updates can arrive in any order.
166
- if function_id not in self._llm_agents:
167
- self._llm_agents[function_id] = {}
164
+ # Update llm_agents data with provider_proxy and vendor (Phase 2)
165
+ if function_id in self._llm_agents:
166
+ self._llm_agents[function_id]["provider_proxy"] = provider_proxy
168
167
 
169
- # Phase 2: Extract vendor from provider_data for handler selection
170
- vendor = provider_data.get("vendor", "unknown")
168
+ # Phase 2: Extract vendor from provider_data for handler selection
169
+ vendor = provider_data.get("vendor", "unknown")
170
+ self._llm_agents[function_id]["vendor"] = vendor
171
171
 
172
- self._llm_agents[function_id]["provider_proxy"] = provider_proxy
173
- self._llm_agents[function_id]["vendor"] = vendor
174
-
175
- logger.info(
176
- f"✅ Set provider proxy for '{function_id}': {provider_proxy.function_name} at {provider_proxy.endpoint} (vendor={vendor})"
177
- )
178
-
179
- # Re-create and update MeshLlmAgent with new provider
180
- # Get the function wrapper and metadata from DecoratorRegistry
181
- llm_agents = DecoratorRegistry.get_mesh_llm_agents()
182
- wrapper = None
183
- llm_metadata = None
184
- for agent_func_id, metadata in llm_agents.items():
185
- if metadata.function_id == function_id:
186
- wrapper = metadata.function
187
- llm_metadata = metadata
188
- break
189
-
190
- # Check if tools are required (filter is specified)
191
- has_filter = False
192
- if llm_metadata and llm_metadata.config:
193
- filter_config = llm_metadata.config.get("filter")
194
- has_filter = filter_config is not None and len(filter_config) > 0
195
-
196
- # If no filter specified, initialize empty tools data so we can create LLM agent without tools
197
- # This supports simple LLM calls (text generation) that don't need tool calling
198
- if not has_filter and "tools_metadata" not in self._llm_agents[function_id]:
199
- self._llm_agents[function_id].update(
200
- {
201
- "config": llm_metadata.config if llm_metadata else {},
202
- "output_type": llm_metadata.output_type if llm_metadata else None,
203
- "param_name": llm_metadata.param_name if llm_metadata else "llm",
204
- "tools_metadata": [], # No tools for simple LLM calls
205
- "tools_proxies": {}, # No tool proxies needed
206
- "function": llm_metadata.function if llm_metadata else None,
207
- }
208
- )
209
172
  logger.info(
210
- f"✅ Initialized empty tools for '{function_id}' (no filter specified - simple LLM mode)"
173
+ f"✅ Set provider proxy for '{function_id}': {provider_proxy.function_name} at {provider_proxy.endpoint} (vendor={vendor})"
211
174
  )
212
175
 
213
- # Update wrapper if we have tools data (either from filter matching or initialized empty)
214
- if (
215
- wrapper
216
- and hasattr(wrapper, "_mesh_update_llm_agent")
217
- and "tools_metadata" in self._llm_agents[function_id]
218
- ):
219
- llm_agent = self._create_llm_agent(function_id)
220
- wrapper._mesh_update_llm_agent(llm_agent)
221
- logger.info(
222
- f"🔄 Updated wrapper with MeshLlmAgent for '{function_id}'"
223
- + (" (with tools)" if has_filter else " (simple LLM mode)")
224
- )
225
- elif wrapper and hasattr(wrapper, "_mesh_update_llm_agent") and has_filter:
226
- logger.debug(
227
- f"⏳ Provider set for '{function_id}', waiting for tools before updating wrapper"
176
+ # Re-create and update MeshLlmAgent with new provider
177
+ # Get the function wrapper from DecoratorRegistry
178
+ llm_agents = DecoratorRegistry.get_mesh_llm_agents()
179
+ wrapper = None
180
+ for agent_func_id, metadata in llm_agents.items():
181
+ if metadata.function_id == function_id:
182
+ wrapper = metadata.function
183
+ break
184
+
185
+ if wrapper and hasattr(wrapper, "_mesh_update_llm_agent"):
186
+ llm_agent = self._create_llm_agent(function_id)
187
+ wrapper._mesh_update_llm_agent(llm_agent)
188
+ logger.info(
189
+ f"🔄 Updated wrapper with new MeshLlmAgent (with provider) for '{function_id}'"
190
+ )
191
+ else:
192
+ logger.warning(
193
+ f"⚠️ Function '{function_id}' not found in _llm_agents, cannot set provider proxy"
228
194
  )
229
195
 
230
196
  def _create_provider_proxy(self, provider_data: dict[str, Any]) -> UnifiedMCPProxy:
@@ -307,23 +273,21 @@ class MeshLlmAgentInjector(BaseInjector):
307
273
  logger.error(f"❌ Error creating proxy for tool {tool_name}: {e}")
308
274
  # Continue processing other tools
309
275
 
310
- # Update only tool-related fields, preserving provider_proxy if already set.
311
- # Provider proxy is managed separately by process_llm_providers().
312
- # This avoids race conditions where tools update wipes out provider resolution.
313
- if function_id not in self._llm_agents:
314
- self._llm_agents[function_id] = {}
315
-
316
- self._llm_agents[function_id].update(
317
- {
318
- "config": llm_metadata.config,
319
- "output_type": llm_metadata.output_type,
320
- "param_name": llm_metadata.param_name,
321
- "tools_metadata": tools, # Original metadata for schema building
322
- "tools_proxies": tool_proxies_map, # Proxies for execution
323
- "function": llm_metadata.function,
324
- # Note: provider_proxy is NOT set here - managed by _process_function_provider
325
- }
326
- )
276
+ # Provider proxy will be set separately via process_llm_providers()
277
+ # (v0.6.1 - providers come from llm_providers field, not dependencies)
278
+ provider_proxy = None
279
+
280
+ # Store LLM agent data with both metadata and proxies
281
+ # Keep original tool metadata for schema building
282
+ self._llm_agents[function_id] = {
283
+ "config": llm_metadata.config,
284
+ "output_type": llm_metadata.output_type,
285
+ "param_name": llm_metadata.param_name,
286
+ "tools_metadata": tools, # Original metadata for schema building
287
+ "tools_proxies": tool_proxies_map, # Proxies for execution
288
+ "function": llm_metadata.function,
289
+ "provider_proxy": provider_proxy, # Provider proxy for mesh delegation
290
+ }
327
291
 
328
292
  logger.info(
329
293
  f"✅ Processed {len(tool_proxies_map)} tools for LLM function '{function_id}'"
@@ -416,7 +380,7 @@ class MeshLlmAgentInjector(BaseInjector):
416
380
  """
417
381
  Create wrapper that injects MeshLlmAgent into function parameters.
418
382
 
419
- Like McpMeshTool injection, this creates a wrapper at decorator time with llm_agent=None,
383
+ Like McpMeshAgent injection, this creates a wrapper at decorator time with llm_agent=None,
420
384
  which gets updated during heartbeat when tools arrive from registry.
421
385
 
422
386
  Args:
@@ -467,7 +431,8 @@ class MeshLlmAgentInjector(BaseInjector):
467
431
  if is_template:
468
432
  # Templates enabled - create per-call agent with context
469
433
  # Import signature analyzer for context detection
470
- from .signature_analyzer import get_context_parameter_name
434
+ from .signature_analyzer import \
435
+ get_context_parameter_name
471
436
 
472
437
  # Detect context parameter
473
438
  context_param_name = config_dict.get("context_param")
@@ -5,67 +5,21 @@ Function signature analysis for MCP Mesh dependency injection.
5
5
  import inspect
6
6
  from typing import Any, get_type_hints
7
7
 
8
- from mesh.types import McpMeshTool, MeshLlmAgent
9
-
10
- # Also support deprecated McpMeshAgent for backwards compatibility
11
- try:
12
- from mesh.types import McpMeshAgent
13
- except ImportError:
14
- McpMeshAgent = McpMeshTool # type: ignore
15
-
16
-
17
- def _is_mesh_tool_type(param_type: Any) -> bool:
18
- """Check if a type is McpMeshTool or deprecated McpMeshAgent."""
19
- # Direct McpMeshTool type
20
- if (
21
- param_type == McpMeshTool
22
- or (hasattr(param_type, "__name__") and param_type.__name__ == "McpMeshTool")
23
- or (
24
- hasattr(param_type, "__origin__")
25
- and param_type.__origin__ == type(McpMeshTool)
26
- )
27
- ):
28
- return True
29
-
30
- # Support deprecated McpMeshAgent
31
- if (
32
- param_type == McpMeshAgent
33
- or (hasattr(param_type, "__name__") and param_type.__name__ == "McpMeshAgent")
34
- or (
35
- hasattr(param_type, "__origin__")
36
- and param_type.__origin__ == type(McpMeshAgent)
37
- )
38
- ):
39
- return True
40
-
41
- # Union type (e.g., McpMeshTool | None)
42
- if hasattr(param_type, "__args__"):
43
- for arg in param_type.__args__:
44
- if arg == McpMeshTool or (
45
- hasattr(arg, "__name__") and arg.__name__ == "McpMeshTool"
46
- ):
47
- return True
48
- # Support deprecated McpMeshAgent in unions
49
- if arg == McpMeshAgent or (
50
- hasattr(arg, "__name__") and arg.__name__ == "McpMeshAgent"
51
- ):
52
- return True
53
-
54
- return False
8
+ from mesh.types import McpMeshAgent, MeshLlmAgent
55
9
 
56
10
 
57
11
  def get_mesh_agent_positions(func: Any) -> list[int]:
58
12
  """
59
- Get positions of McpMeshTool parameters in function signature.
13
+ Get positions of McpMeshAgent parameters in function signature.
60
14
 
61
15
  Args:
62
16
  func: Function to analyze
63
17
 
64
18
  Returns:
65
- List of parameter positions (0-indexed) that are McpMeshTool types
19
+ List of parameter positions (0-indexed) that are McpMeshAgent types
66
20
 
67
21
  Example:
68
- def greet(name: str, date_svc: McpMeshTool, file_svc: McpMeshTool):
22
+ def greet(name: str, date_svc: McpMeshAgent, file_svc: McpMeshAgent):
69
23
  pass
70
24
 
71
25
  get_mesh_agent_positions(greet) → [1, 2]
@@ -78,12 +32,40 @@ def get_mesh_agent_positions(func: Any) -> list[int]:
78
32
  sig = inspect.signature(func)
79
33
  param_names = list(sig.parameters.keys())
80
34
 
81
- # Find positions of McpMeshTool parameters
35
+ # Find positions of McpMeshAgent parameters
82
36
  mesh_positions = []
83
37
  for i, param_name in enumerate(param_names):
84
38
  if param_name in type_hints:
85
39
  param_type = type_hints[param_name]
86
- if _is_mesh_tool_type(param_type):
40
+
41
+ # Check if it's McpMeshAgent type (handle different import paths and Union types)
42
+ is_agent = False
43
+
44
+ # Direct McpMeshAgent type
45
+ if (
46
+ param_type == McpMeshAgent
47
+ or (
48
+ hasattr(param_type, "__name__")
49
+ and param_type.__name__ == "McpMeshAgent"
50
+ )
51
+ or (
52
+ hasattr(param_type, "__origin__")
53
+ and param_type.__origin__ == type(McpMeshAgent)
54
+ )
55
+ ):
56
+ is_agent = True
57
+
58
+ # Union type (e.g., McpMeshAgent | None)
59
+ elif hasattr(param_type, "__args__"):
60
+ # Check if any arg in the union is McpMeshAgent
61
+ for arg in param_type.__args__:
62
+ if arg == McpMeshAgent or (
63
+ hasattr(arg, "__name__") and arg.__name__ == "McpMeshAgent"
64
+ ):
65
+ is_agent = True
66
+ break
67
+
68
+ if is_agent:
87
69
  mesh_positions.append(i)
88
70
 
89
71
  return mesh_positions
@@ -99,13 +81,13 @@ def get_mesh_agent_positions(func: Any) -> list[int]:
99
81
 
100
82
  def get_mesh_agent_parameter_names(func: Any) -> list[str]:
101
83
  """
102
- Get names of McpMeshTool parameters in function signature.
84
+ Get names of McpMeshAgent parameters in function signature.
103
85
 
104
86
  Args:
105
87
  func: Function to analyze
106
88
 
107
89
  Returns:
108
- List of parameter names that are McpMeshTool types
90
+ List of parameter names that are McpMeshAgent types
109
91
  """
110
92
  try:
111
93
  type_hints = get_type_hints(func)
@@ -115,7 +97,35 @@ def get_mesh_agent_parameter_names(func: Any) -> list[str]:
115
97
  for param_name, param in sig.parameters.items():
116
98
  if param_name in type_hints:
117
99
  param_type = type_hints[param_name]
118
- if _is_mesh_tool_type(param_type):
100
+
101
+ # Check if it's McpMeshAgent type (handle different import paths and Union types)
102
+ is_mesh_agent = False
103
+
104
+ # Direct McpMeshAgent type
105
+ if (
106
+ param_type == McpMeshAgent
107
+ or (
108
+ hasattr(param_type, "__name__")
109
+ and param_type.__name__ == "McpMeshAgent"
110
+ )
111
+ or (
112
+ hasattr(param_type, "__origin__")
113
+ and param_type.__origin__ == type(McpMeshAgent)
114
+ )
115
+ ):
116
+ is_mesh_agent = True
117
+
118
+ # Union type (e.g., McpMeshAgent | None)
119
+ elif hasattr(param_type, "__args__"):
120
+ # Check if any arg in the union is McpMeshAgent
121
+ for arg in param_type.__args__:
122
+ if arg == McpMeshAgent or (
123
+ hasattr(arg, "__name__") and arg.__name__ == "McpMeshAgent"
124
+ ):
125
+ is_mesh_agent = True
126
+ break
127
+
128
+ if is_mesh_agent:
119
129
  mesh_param_names.append(param_name)
120
130
 
121
131
  return mesh_param_names
@@ -126,7 +136,7 @@ def get_mesh_agent_parameter_names(func: Any) -> list[str]:
126
136
 
127
137
  def validate_mesh_dependencies(func: Any, dependencies: list[dict]) -> tuple[bool, str]:
128
138
  """
129
- Validate that the number of dependencies matches McpMeshTool parameters.
139
+ Validate that the number of dependencies matches McpMeshAgent parameters.
130
140
 
131
141
  Args:
132
142
  func: Function to validate
@@ -139,9 +149,9 @@ def validate_mesh_dependencies(func: Any, dependencies: list[dict]) -> tuple[boo
139
149
 
140
150
  if len(dependencies) != len(mesh_positions):
141
151
  return False, (
142
- f"Function {func.__name__} has {len(mesh_positions)} McpMeshTool parameters "
152
+ f"Function {func.__name__} has {len(mesh_positions)} McpMeshAgent parameters "
143
153
  f"but {len(dependencies)} dependencies declared. "
144
- f"Each McpMeshTool parameter needs a corresponding dependency."
154
+ f"Each McpMeshAgent parameter needs a corresponding dependency."
145
155
  )
146
156
 
147
157
  return True, ""
@@ -25,7 +25,7 @@ logger = logging.getLogger(__name__)
25
25
  class UnifiedMCPProxy:
26
26
  """Unified MCP proxy using FastMCP's built-in client.
27
27
 
28
- This provides the implementation for McpMeshTool type parameters,
28
+ This provides the implementation for McpMeshAgent type parameters,
29
29
  offering all MCP protocol features using FastMCP's superior client.
30
30
 
31
31
  Features:
@@ -104,10 +104,9 @@ def _build_api_agent_spec(context: dict[str, Any], service_id: str = None) -> An
104
104
  # Build dependency specs
105
105
  deps = []
106
106
  for dep_cap in dependencies:
107
- # Tags must be serialized to JSON string (Rust core expects string, not list)
108
107
  dep_spec = core.DependencySpec(
109
108
  capability=dep_cap,
110
- tags=json.dumps([]),
109
+ tags=[],
111
110
  version=None,
112
111
  )
113
112
  deps.append(dep_spec)
@@ -137,7 +136,6 @@ def _build_api_agent_spec(context: dict[str, Any], service_id: str = None) -> An
137
136
  http_port=http_port,
138
137
  http_host=http_host,
139
138
  namespace=namespace,
140
- agent_type="api", # API services only consume capabilities, not provide them
141
139
  tools=tools if tools else None,
142
140
  llm_agents=None, # API services don't have LLM agents
143
141
  heartbeat_interval=heartbeat_interval,
@@ -274,8 +272,6 @@ async def _handle_api_dependency_change(
274
272
  )
275
273
  if not current_service_id:
276
274
  # Use config resolver for consistent env var handling
277
- from ...shared.config_resolver import get_config_value
278
-
279
275
  current_service_id = get_config_value("MCP_MESH_AGENT_ID")
280
276
 
281
277
  is_self_dependency = (