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.
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/.gitignore +0 -8
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/PKG-INFO +6 -7
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/README.md +4 -4
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/__init__.py +1 -1
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/dependency_injector.py +9 -9
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/mesh_llm_agent.py +14 -36
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/mesh_llm_agent_injector.py +43 -78
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/signature_analyzer.py +68 -58
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/unified_mcp_proxy.py +1 -1
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/api_heartbeat/rust_api_heartbeat.py +1 -5
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/mcp_heartbeat/rust_heartbeat.py +6 -21
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/mcp_startup/fastapiserver_setup.py +13 -37
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/mcp_startup/startup_orchestrator.py +9 -9
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/utils/fastmcp_schema_extractor.py +3 -3
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/mesh/__init__.py +2 -12
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/mesh/decorators.py +51 -182
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/mesh/helpers.py +0 -52
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/mesh/types.py +13 -40
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/pyproject.toml +5 -6
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/LICENSE +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/__init__.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/async_mcp_client.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/base_injector.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/decorator_registry.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/http_wrapper.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/llm_config.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/llm_errors.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/provider_handlers/__init__.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/provider_handlers/base_provider_handler.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/provider_handlers/claude_handler.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/provider_handlers/gemini_handler.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/provider_handlers/generic_handler.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/provider_handlers/openai_handler.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/provider_handlers/provider_handler_registry.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/response_parser.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/self_dependency_proxy.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/session_aware_client.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/session_manager.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/tool_executor.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/engine/tool_schema_builder.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/__init__.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/api_heartbeat/__init__.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/api_heartbeat/api_lifespan_integration.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/api_startup/__init__.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/api_startup/api_pipeline.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/api_startup/api_server_setup.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/api_startup/fastapi_discovery.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/api_startup/middleware_integration.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/api_startup/route_collection.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/api_startup/route_integration.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/mcp_heartbeat/__init__.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/mcp_startup/__init__.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/mcp_startup/configuration.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/mcp_startup/decorator_collection.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/mcp_startup/fastmcpserver_discovery.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/mcp_startup/heartbeat_loop.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/mcp_startup/heartbeat_preparation.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/mcp_startup/lifespan_factory.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/mcp_startup/server_discovery.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/mcp_startup/startup_pipeline.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/shared/__init__.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/shared/base_step.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/shared/mesh_pipeline.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/pipeline/shared/pipeline_types.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/reload.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/reload_runner.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/shared/__init__.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/shared/config_resolver.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/shared/content_extractor.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/shared/defaults.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/shared/fast_heartbeat_status.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/shared/fastapi_middleware_manager.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/shared/health_check_manager.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/shared/host_resolver.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/shared/logging_config.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/shared/server_discovery.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/shared/simple_shutdown.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/shared/sse_parser.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/shared/support_types.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/tracing/agent_context_helper.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/tracing/context.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/tracing/execution_tracer.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/tracing/fastapi_tracing_middleware.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/tracing/redis_metadata_publisher.py +0 -0
- {mcp_mesh-0.8.0 → mcp_mesh-0.8.0b1}/_mcp_mesh/tracing/trace_context_helper.py +0 -0
- {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.
|
|
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.
|
|
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
|
|
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",
|
|
95
|
+
def greet(name: str = "World", systemDate: McpMeshAgent = None) -> str:
|
|
97
96
|
"""Greeting function with automatic dependency injection."""
|
|
98
|
-
if
|
|
97
|
+
if systemDate is not None:
|
|
99
98
|
try:
|
|
100
|
-
current_date =
|
|
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
|
|
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",
|
|
31
|
+
def greet(name: str = "World", systemDate: McpMeshAgent = None) -> str:
|
|
32
32
|
"""Greeting function with automatic dependency injection."""
|
|
33
|
-
if
|
|
33
|
+
if systemDate is not None:
|
|
34
34
|
try:
|
|
35
|
-
current_date =
|
|
35
|
+
current_date = systemDate()
|
|
36
36
|
return f"Hello, {name}! Today is {current_date}"
|
|
37
37
|
except Exception:
|
|
38
38
|
pass
|
|
@@ -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
|
|
31
|
-
2. Multiple parameters: only inject into
|
|
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
|
|
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
|
|
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
|
|
73
|
-
f"Consider typing dependency parameters as
|
|
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)}
|
|
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)}
|
|
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 (
|
|
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
|
-
|
|
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,
|
|
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
|
-
#
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
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
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
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
|
|
165
|
-
|
|
166
|
-
|
|
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
|
-
|
|
170
|
-
|
|
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"✅
|
|
173
|
+
f"✅ Set provider proxy for '{function_id}': {provider_proxy.function_name} at {provider_proxy.endpoint} (vendor={vendor})"
|
|
211
174
|
)
|
|
212
175
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
-
#
|
|
311
|
-
#
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
self._llm_agents[function_id]
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
19
|
+
List of parameter positions (0-indexed) that are McpMeshAgent types
|
|
66
20
|
|
|
67
21
|
Example:
|
|
68
|
-
def greet(name: str, date_svc:
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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)}
|
|
152
|
+
f"Function {func.__name__} has {len(mesh_positions)} McpMeshAgent parameters "
|
|
143
153
|
f"but {len(dependencies)} dependencies declared. "
|
|
144
|
-
f"Each
|
|
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
|
|
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=
|
|
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 = (
|