mcp-mesh 0.8.0b5__tar.gz → 0.8.0b7__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.0b5 → mcp_mesh-0.8.0b7}/.gitignore +1 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/PKG-INFO +6 -6
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/README.md +4 -4
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/__init__.py +1 -1
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/engine/dependency_injector.py +9 -9
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/engine/mesh_llm_agent_injector.py +1 -1
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/engine/signature_analyzer.py +58 -68
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/engine/unified_mcp_proxy.py +1 -1
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/mcp_heartbeat/rust_heartbeat.py +17 -5
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/mcp_startup/fastapiserver_setup.py +37 -13
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/mcp_startup/startup_orchestrator.py +9 -9
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/utils/fastmcp_schema_extractor.py +3 -3
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/mesh/__init__.py +12 -2
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/mesh/decorators.py +55 -6
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/mesh/types.py +40 -13
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/pyproject.toml +5 -5
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/LICENSE +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/engine/__init__.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/engine/async_mcp_client.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/engine/base_injector.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/engine/decorator_registry.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/engine/http_wrapper.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/engine/llm_config.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/engine/llm_errors.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/engine/mesh_llm_agent.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/engine/provider_handlers/__init__.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/engine/provider_handlers/base_provider_handler.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/engine/provider_handlers/claude_handler.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/engine/provider_handlers/gemini_handler.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/engine/provider_handlers/generic_handler.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/engine/provider_handlers/openai_handler.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/engine/provider_handlers/provider_handler_registry.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/engine/response_parser.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/engine/self_dependency_proxy.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/engine/session_aware_client.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/engine/session_manager.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/engine/tool_executor.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/engine/tool_schema_builder.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/__init__.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/api_heartbeat/__init__.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/api_heartbeat/api_lifespan_integration.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/api_heartbeat/rust_api_heartbeat.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/api_startup/__init__.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/api_startup/api_pipeline.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/api_startup/api_server_setup.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/api_startup/fastapi_discovery.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/api_startup/middleware_integration.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/api_startup/route_collection.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/api_startup/route_integration.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/mcp_heartbeat/__init__.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/mcp_startup/__init__.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/mcp_startup/configuration.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/mcp_startup/decorator_collection.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/mcp_startup/fastmcpserver_discovery.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/mcp_startup/heartbeat_loop.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/mcp_startup/heartbeat_preparation.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/mcp_startup/lifespan_factory.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/mcp_startup/server_discovery.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/mcp_startup/startup_pipeline.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/shared/__init__.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/shared/base_step.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/shared/mesh_pipeline.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/shared/pipeline_types.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/reload.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/reload_runner.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/shared/__init__.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/shared/config_resolver.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/shared/content_extractor.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/shared/defaults.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/shared/fast_heartbeat_status.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/shared/fastapi_middleware_manager.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/shared/health_check_manager.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/shared/host_resolver.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/shared/logging_config.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/shared/server_discovery.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/shared/simple_shutdown.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/shared/sse_parser.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/shared/support_types.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/tracing/agent_context_helper.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/tracing/context.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/tracing/execution_tracer.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/tracing/fastapi_tracing_middleware.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/tracing/redis_metadata_publisher.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/tracing/trace_context_helper.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/tracing/utils.py +0 -0
- {mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/mesh/helpers.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcp-mesh
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.0b7
|
|
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
|
|
@@ -30,7 +30,7 @@ Requires-Dist: fastmcp<3.0.0,>=2.8.0
|
|
|
30
30
|
Requires-Dist: httpx<1.0.0,>=0.25.0
|
|
31
31
|
Requires-Dist: jinja2>=3.1.0
|
|
32
32
|
Requires-Dist: litellm>=1.30.0
|
|
33
|
-
Requires-Dist: mcp-mesh-core>=0.8.
|
|
33
|
+
Requires-Dist: mcp-mesh-core>=0.8.0b7
|
|
34
34
|
Requires-Dist: mcp<2.0.0,>=1.9.0
|
|
35
35
|
Requires-Dist: prometheus-client<1.0.0,>=0.19.0
|
|
36
36
|
Requires-Dist: pydantic<3.0.0,>=2.4.0
|
|
@@ -78,7 +78,7 @@ pip install mcp-mesh
|
|
|
78
78
|
import mesh
|
|
79
79
|
|
|
80
80
|
# Import types from public API
|
|
81
|
-
from mesh.types import
|
|
81
|
+
from mesh.types import McpMeshTool
|
|
82
82
|
|
|
83
83
|
# Define your agent
|
|
84
84
|
@mesh.agent(name="hello-world", http_port=9090)
|
|
@@ -92,11 +92,11 @@ class HelloWorldAgent:
|
|
|
92
92
|
dependencies=["date_service"],
|
|
93
93
|
description="Greeting function with date dependency injection"
|
|
94
94
|
)
|
|
95
|
-
def greet(name: str = "World",
|
|
95
|
+
def greet(name: str = "World", date_tool: McpMeshTool = None) -> str:
|
|
96
96
|
"""Greeting function with automatic dependency injection."""
|
|
97
|
-
if
|
|
97
|
+
if date_tool is not None:
|
|
98
98
|
try:
|
|
99
|
-
current_date =
|
|
99
|
+
current_date = date_tool()
|
|
100
100
|
return f"Hello, {name}! Today is {current_date}"
|
|
101
101
|
except Exception:
|
|
102
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 McpMeshTool
|
|
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", date_tool: McpMeshTool = None) -> str:
|
|
32
32
|
"""Greeting function with automatic dependency injection."""
|
|
33
|
-
if
|
|
33
|
+
if date_tool is not None:
|
|
34
34
|
try:
|
|
35
|
-
current_date =
|
|
35
|
+
current_date = date_tool()
|
|
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 McpMeshTool)
|
|
31
|
+
2. Multiple parameters: only inject into McpMeshTool 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 McpMeshTool 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 McpMeshTool 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 McpMeshTool. Skipping injection of {len(dependencies)} dependencies. "
|
|
73
|
+
f"Consider typing dependency parameters as McpMeshTool."
|
|
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)} McpMeshTool 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)} McpMeshTool 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 (McpMeshTool)
|
|
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
|
|
@@ -380,7 +380,7 @@ class MeshLlmAgentInjector(BaseInjector):
|
|
|
380
380
|
"""
|
|
381
381
|
Create wrapper that injects MeshLlmAgent into function parameters.
|
|
382
382
|
|
|
383
|
-
Like
|
|
383
|
+
Like McpMeshTool injection, this creates a wrapper at decorator time with llm_agent=None,
|
|
384
384
|
which gets updated during heartbeat when tools arrive from registry.
|
|
385
385
|
|
|
386
386
|
Args:
|
|
@@ -5,21 +5,67 @@ 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
|
|
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
|
|
9
55
|
|
|
10
56
|
|
|
11
57
|
def get_mesh_agent_positions(func: Any) -> list[int]:
|
|
12
58
|
"""
|
|
13
|
-
Get positions of
|
|
59
|
+
Get positions of McpMeshTool parameters in function signature.
|
|
14
60
|
|
|
15
61
|
Args:
|
|
16
62
|
func: Function to analyze
|
|
17
63
|
|
|
18
64
|
Returns:
|
|
19
|
-
List of parameter positions (0-indexed) that are
|
|
65
|
+
List of parameter positions (0-indexed) that are McpMeshTool types
|
|
20
66
|
|
|
21
67
|
Example:
|
|
22
|
-
def greet(name: str, date_svc:
|
|
68
|
+
def greet(name: str, date_svc: McpMeshTool, file_svc: McpMeshTool):
|
|
23
69
|
pass
|
|
24
70
|
|
|
25
71
|
get_mesh_agent_positions(greet) → [1, 2]
|
|
@@ -32,40 +78,12 @@ def get_mesh_agent_positions(func: Any) -> list[int]:
|
|
|
32
78
|
sig = inspect.signature(func)
|
|
33
79
|
param_names = list(sig.parameters.keys())
|
|
34
80
|
|
|
35
|
-
# Find positions of
|
|
81
|
+
# Find positions of McpMeshTool parameters
|
|
36
82
|
mesh_positions = []
|
|
37
83
|
for i, param_name in enumerate(param_names):
|
|
38
84
|
if param_name in type_hints:
|
|
39
85
|
param_type = type_hints[param_name]
|
|
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:
|
|
86
|
+
if _is_mesh_tool_type(param_type):
|
|
69
87
|
mesh_positions.append(i)
|
|
70
88
|
|
|
71
89
|
return mesh_positions
|
|
@@ -81,13 +99,13 @@ def get_mesh_agent_positions(func: Any) -> list[int]:
|
|
|
81
99
|
|
|
82
100
|
def get_mesh_agent_parameter_names(func: Any) -> list[str]:
|
|
83
101
|
"""
|
|
84
|
-
Get names of
|
|
102
|
+
Get names of McpMeshTool parameters in function signature.
|
|
85
103
|
|
|
86
104
|
Args:
|
|
87
105
|
func: Function to analyze
|
|
88
106
|
|
|
89
107
|
Returns:
|
|
90
|
-
List of parameter names that are
|
|
108
|
+
List of parameter names that are McpMeshTool types
|
|
91
109
|
"""
|
|
92
110
|
try:
|
|
93
111
|
type_hints = get_type_hints(func)
|
|
@@ -97,35 +115,7 @@ def get_mesh_agent_parameter_names(func: Any) -> list[str]:
|
|
|
97
115
|
for param_name, param in sig.parameters.items():
|
|
98
116
|
if param_name in type_hints:
|
|
99
117
|
param_type = type_hints[param_name]
|
|
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:
|
|
118
|
+
if _is_mesh_tool_type(param_type):
|
|
129
119
|
mesh_param_names.append(param_name)
|
|
130
120
|
|
|
131
121
|
return mesh_param_names
|
|
@@ -136,7 +126,7 @@ def get_mesh_agent_parameter_names(func: Any) -> list[str]:
|
|
|
136
126
|
|
|
137
127
|
def validate_mesh_dependencies(func: Any, dependencies: list[dict]) -> tuple[bool, str]:
|
|
138
128
|
"""
|
|
139
|
-
Validate that the number of dependencies matches
|
|
129
|
+
Validate that the number of dependencies matches McpMeshTool parameters.
|
|
140
130
|
|
|
141
131
|
Args:
|
|
142
132
|
func: Function to validate
|
|
@@ -149,9 +139,9 @@ def validate_mesh_dependencies(func: Any, dependencies: list[dict]) -> tuple[boo
|
|
|
149
139
|
|
|
150
140
|
if len(dependencies) != len(mesh_positions):
|
|
151
141
|
return False, (
|
|
152
|
-
f"Function {func.__name__} has {len(mesh_positions)}
|
|
142
|
+
f"Function {func.__name__} has {len(mesh_positions)} McpMeshTool parameters "
|
|
153
143
|
f"but {len(dependencies)} dependencies declared. "
|
|
154
|
-
f"Each
|
|
144
|
+
f"Each McpMeshTool parameter needs a corresponding dependency."
|
|
155
145
|
)
|
|
156
146
|
|
|
157
147
|
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 McpMeshTool type parameters,
|
|
29
29
|
offering all MCP protocol features using FastMCP's superior client.
|
|
30
30
|
|
|
31
31
|
Features:
|
|
@@ -73,6 +73,18 @@ def _build_agent_spec(context: dict[str, Any]) -> Any:
|
|
|
73
73
|
# Get HTTP config
|
|
74
74
|
http_host = agent_config.get("http_host", "localhost")
|
|
75
75
|
http_port = agent_config.get("http_port", 0)
|
|
76
|
+
|
|
77
|
+
# If port=0 (auto-assign), check for detected port from server discovery
|
|
78
|
+
if http_port == 0:
|
|
79
|
+
existing_server = context.get("existing_server")
|
|
80
|
+
if existing_server:
|
|
81
|
+
detected_port = existing_server.get("port", 0)
|
|
82
|
+
if detected_port > 0:
|
|
83
|
+
logger.info(
|
|
84
|
+
f"Using detected port {detected_port} (agent_config had port=0)"
|
|
85
|
+
)
|
|
86
|
+
http_port = detected_port
|
|
87
|
+
|
|
76
88
|
namespace = agent_config.get("namespace", "default")
|
|
77
89
|
version = agent_config.get("version", "1.0.0")
|
|
78
90
|
description = agent_config.get("description", "")
|
|
@@ -335,13 +347,13 @@ async def _handle_mesh_event(event: Any, context: dict[str, Any]) -> None:
|
|
|
335
347
|
|
|
336
348
|
async def _handle_dependency_change(
|
|
337
349
|
capability: str,
|
|
338
|
-
endpoint:
|
|
339
|
-
function_name:
|
|
340
|
-
agent_id:
|
|
350
|
+
endpoint: str | None,
|
|
351
|
+
function_name: str | None,
|
|
352
|
+
agent_id: str | None,
|
|
341
353
|
available: bool,
|
|
342
354
|
context: dict[str, Any],
|
|
343
|
-
requesting_function:
|
|
344
|
-
dep_index:
|
|
355
|
+
requesting_function: str | None = None,
|
|
356
|
+
dep_index: int | None = None,
|
|
345
357
|
) -> None:
|
|
346
358
|
"""
|
|
347
359
|
Handle dependency availability change.
|
|
@@ -198,10 +198,15 @@ class FastAPIServerSetupStep(PipelineStep):
|
|
|
198
198
|
# Use centralized binding host resolution (always 0.0.0.0 for all interfaces)
|
|
199
199
|
bind_host = HostResolver.get_binding_host()
|
|
200
200
|
|
|
201
|
-
# Port from agent config
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
201
|
+
# Port from environment or agent config
|
|
202
|
+
# Note: port=0 means auto-assign, so we must not treat it as falsy
|
|
203
|
+
env_port = os.getenv("MCP_MESH_HTTP_PORT")
|
|
204
|
+
if env_port is not None:
|
|
205
|
+
bind_port = int(env_port)
|
|
206
|
+
elif "http_port" in agent_config:
|
|
207
|
+
bind_port = agent_config["http_port"]
|
|
208
|
+
else:
|
|
209
|
+
bind_port = 8080 # Default only if nothing specified
|
|
205
210
|
|
|
206
211
|
return {
|
|
207
212
|
"bind_host": bind_host,
|
|
@@ -236,9 +241,11 @@ class FastAPIServerSetupStep(PipelineStep):
|
|
|
236
241
|
try:
|
|
237
242
|
from fastapi import FastAPI
|
|
238
243
|
|
|
239
|
-
from .lifespan_factory import (
|
|
240
|
-
|
|
241
|
-
|
|
244
|
+
from .lifespan_factory import (
|
|
245
|
+
create_minimal_lifespan,
|
|
246
|
+
create_multiple_fastmcp_lifespan,
|
|
247
|
+
create_single_fastmcp_lifespan,
|
|
248
|
+
)
|
|
242
249
|
|
|
243
250
|
agent_name = agent_config.get("name", "mcp-mesh-agent")
|
|
244
251
|
agent_description = agent_config.get(
|
|
@@ -328,8 +335,7 @@ class FastAPIServerSetupStep(PipelineStep):
|
|
|
328
335
|
if health_check_fn:
|
|
329
336
|
# Use health check cache if configured
|
|
330
337
|
from ...engine.decorator_registry import DecoratorRegistry
|
|
331
|
-
from ...shared.health_check_manager import
|
|
332
|
-
get_health_status_with_cache
|
|
338
|
+
from ...shared.health_check_manager import get_health_status_with_cache
|
|
333
339
|
|
|
334
340
|
health_status = await get_health_status_with_cache(
|
|
335
341
|
agent_id=agent_name,
|
|
@@ -567,11 +573,29 @@ mcp_mesh_up{{agent="{agent_name}"}} 1
|
|
|
567
573
|
|
|
568
574
|
server_task = asyncio.create_task(run_server())
|
|
569
575
|
|
|
570
|
-
#
|
|
571
|
-
|
|
576
|
+
# Wait for server to start and get actual port
|
|
577
|
+
# uvicorn sets server.started = True when ready
|
|
578
|
+
for _ in range(50): # Max 5 seconds
|
|
579
|
+
await asyncio.sleep(0.1)
|
|
580
|
+
if server.started:
|
|
581
|
+
break
|
|
572
582
|
|
|
573
|
-
#
|
|
574
|
-
actual_port = bind_port
|
|
583
|
+
# Get actual port from uvicorn sockets (critical for port=0 auto-assign)
|
|
584
|
+
actual_port = bind_port
|
|
585
|
+
if server.started and server.servers:
|
|
586
|
+
try:
|
|
587
|
+
sock = server.servers[0].sockets[0]
|
|
588
|
+
actual_port = sock.getsockname()[1]
|
|
589
|
+
if actual_port != bind_port:
|
|
590
|
+
self.logger.info(
|
|
591
|
+
f"Auto-assigned port {actual_port} (requested: {bind_port})"
|
|
592
|
+
)
|
|
593
|
+
except (IndexError, AttributeError, OSError) as e:
|
|
594
|
+
self.logger.warning(f"Could not get actual port from uvicorn: {e}")
|
|
595
|
+
actual_port = bind_port if bind_port != 0 else 8080
|
|
596
|
+
elif bind_port == 0:
|
|
597
|
+
self.logger.warning("Server not started, falling back to port 8080")
|
|
598
|
+
actual_port = 8080
|
|
575
599
|
|
|
576
600
|
# Build external endpoint
|
|
577
601
|
final_external_endpoint = (
|
{mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/mcp_startup/startup_orchestrator.py
RENAMED
|
@@ -38,8 +38,8 @@ class DebounceCoordinator:
|
|
|
38
38
|
import threading
|
|
39
39
|
|
|
40
40
|
self.delay_seconds = delay_seconds
|
|
41
|
-
self._pending_timer:
|
|
42
|
-
self._orchestrator:
|
|
41
|
+
self._pending_timer: threading.Timer | None = None
|
|
42
|
+
self._orchestrator: MeshOrchestrator | None = None
|
|
43
43
|
self._lock = threading.Lock()
|
|
44
44
|
self.logger = logging.getLogger(f"{__name__}.DebounceCoordinator")
|
|
45
45
|
|
|
@@ -181,8 +181,9 @@ class DebounceCoordinator:
|
|
|
181
181
|
# For API services, ONLY do dependency injection - user controls their FastAPI server
|
|
182
182
|
# Dependency injection is already complete from pipeline execution
|
|
183
183
|
# Optionally start heartbeat in background (non-blocking)
|
|
184
|
-
from ..api_heartbeat.api_lifespan_integration import
|
|
185
|
-
api_heartbeat_lifespan_task
|
|
184
|
+
from ..api_heartbeat.api_lifespan_integration import (
|
|
185
|
+
api_heartbeat_lifespan_task,
|
|
186
|
+
)
|
|
186
187
|
|
|
187
188
|
self._setup_heartbeat_background(
|
|
188
189
|
heartbeat_config,
|
|
@@ -207,8 +208,7 @@ class DebounceCoordinator:
|
|
|
207
208
|
f"heartbeat_task_fn from config is not callable: {type(heartbeat_task_fn)}, using Rust heartbeat"
|
|
208
209
|
)
|
|
209
210
|
# Rust heartbeat is required - no Python fallback
|
|
210
|
-
from ..mcp_heartbeat.rust_heartbeat import
|
|
211
|
-
rust_heartbeat_task
|
|
211
|
+
from ..mcp_heartbeat.rust_heartbeat import rust_heartbeat_task
|
|
212
212
|
|
|
213
213
|
heartbeat_task_fn = rust_heartbeat_task
|
|
214
214
|
|
|
@@ -422,7 +422,7 @@ class DebounceCoordinator:
|
|
|
422
422
|
|
|
423
423
|
|
|
424
424
|
# Global debounce coordinator instance
|
|
425
|
-
_debounce_coordinator:
|
|
425
|
+
_debounce_coordinator: DebounceCoordinator | None = None
|
|
426
426
|
|
|
427
427
|
|
|
428
428
|
def get_debounce_coordinator() -> DebounceCoordinator:
|
|
@@ -520,7 +520,7 @@ class MeshOrchestrator:
|
|
|
520
520
|
"timestamp": "unknown",
|
|
521
521
|
}
|
|
522
522
|
|
|
523
|
-
async def start_service(self, auto_run_config:
|
|
523
|
+
async def start_service(self, auto_run_config: dict | None = None) -> None:
|
|
524
524
|
"""
|
|
525
525
|
Start the service with optional auto-run behavior.
|
|
526
526
|
|
|
@@ -582,7 +582,7 @@ class MeshOrchestrator:
|
|
|
582
582
|
|
|
583
583
|
|
|
584
584
|
# Global orchestrator instance
|
|
585
|
-
_global_orchestrator:
|
|
585
|
+
_global_orchestrator: MeshOrchestrator | None = None
|
|
586
586
|
|
|
587
587
|
|
|
588
588
|
def get_global_orchestrator() -> MeshOrchestrator:
|
|
@@ -24,7 +24,7 @@ class FastMCPSchemaExtractor:
|
|
|
24
24
|
This class extracts those schemas so they can be sent to the registry
|
|
25
25
|
for LLM tool discovery.
|
|
26
26
|
|
|
27
|
-
Phase 2.5: Filters out
|
|
27
|
+
Phase 2.5: Filters out McpMeshTool parameters from schemas since
|
|
28
28
|
they are dependency injection parameters and should not be visible to LLMs.
|
|
29
29
|
|
|
30
30
|
Phase 0: Enhances schemas with MeshContextModel Field descriptions for
|
|
@@ -239,7 +239,7 @@ class FastMCPSchemaExtractor:
|
|
|
239
239
|
schema: dict[str, Any], function: Any
|
|
240
240
|
) -> dict[str, Any]:
|
|
241
241
|
"""
|
|
242
|
-
Filter out
|
|
242
|
+
Filter out McpMeshTool dependency injection parameters from schema.
|
|
243
243
|
|
|
244
244
|
Phase 2.5: Remove dependency injection parameters from LLM-facing schemas.
|
|
245
245
|
These parameters are injected at runtime by MCP Mesh and should not be
|
|
@@ -273,7 +273,7 @@ class FastMCPSchemaExtractor:
|
|
|
273
273
|
if not schema or not isinstance(schema, dict):
|
|
274
274
|
return schema
|
|
275
275
|
|
|
276
|
-
# Get
|
|
276
|
+
# Get McpMeshTool parameter names from signature analysis
|
|
277
277
|
from _mcp_mesh.engine.signature_analyzer import \
|
|
278
278
|
get_mesh_agent_parameter_names
|
|
279
279
|
|
|
@@ -19,8 +19,8 @@ Use 'import mesh' and then '@mesh.tool()' for consistency with MCP patterns.
|
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
21
|
from . import decorators
|
|
22
|
-
from .types import (LlmMeta, McpMeshAgent,
|
|
23
|
-
MeshLlmRequest)
|
|
22
|
+
from .types import (LlmMeta, McpMeshAgent, McpMeshTool, MeshContextModel,
|
|
23
|
+
MeshLlmAgent, MeshLlmRequest)
|
|
24
24
|
|
|
25
25
|
# Note: helpers.llm_provider is imported lazily in __getattr__ to avoid
|
|
26
26
|
# initialization timing issues with @mesh.agent auto_run in tests
|
|
@@ -93,6 +93,8 @@ def create_server(name: str | None = None) -> "FastMCP":
|
|
|
93
93
|
|
|
94
94
|
# Make decorators available as mesh.tool, mesh.agent, mesh.route, mesh.llm, and mesh.llm_provider
|
|
95
95
|
def __getattr__(name):
|
|
96
|
+
import warnings
|
|
97
|
+
|
|
96
98
|
if name == "tool":
|
|
97
99
|
return decorators.tool
|
|
98
100
|
elif name == "agent":
|
|
@@ -106,7 +108,15 @@ def __getattr__(name):
|
|
|
106
108
|
from .helpers import llm_provider
|
|
107
109
|
|
|
108
110
|
return llm_provider
|
|
111
|
+
elif name == "McpMeshTool":
|
|
112
|
+
return McpMeshTool
|
|
109
113
|
elif name == "McpMeshAgent":
|
|
114
|
+
warnings.warn(
|
|
115
|
+
"McpMeshAgent is deprecated, use McpMeshTool instead. "
|
|
116
|
+
"McpMeshAgent will be removed in a future version.",
|
|
117
|
+
DeprecationWarning,
|
|
118
|
+
stacklevel=2,
|
|
119
|
+
)
|
|
110
120
|
return McpMeshAgent
|
|
111
121
|
elif name == "MeshContextModel":
|
|
112
122
|
return MeshContextModel
|
|
@@ -299,8 +299,12 @@ def _start_uvicorn_immediately(http_host: str, http_port: int):
|
|
|
299
299
|
|
|
300
300
|
logger.debug("📦 IMMEDIATE UVICORN: Added status endpoints")
|
|
301
301
|
|
|
302
|
-
#
|
|
303
|
-
|
|
302
|
+
# Port handling:
|
|
303
|
+
# - http_port=0 explicitly means auto-assign (let uvicorn choose)
|
|
304
|
+
# - http_port>0 means use that specific port
|
|
305
|
+
# Note: The default is 8080 only if http_port was never specified,
|
|
306
|
+
# which is handled upstream in the @mesh.agent decorator
|
|
307
|
+
port = http_port
|
|
304
308
|
|
|
305
309
|
logger.debug(
|
|
306
310
|
f"🚀 IMMEDIATE UVICORN: Starting uvicorn server on {http_host}:{port}"
|
|
@@ -366,8 +370,53 @@ def _start_uvicorn_immediately(http_host: str, http_port: int):
|
|
|
366
370
|
# Give server a moment to start
|
|
367
371
|
time.sleep(1)
|
|
368
372
|
|
|
373
|
+
# Detect actual port if port=0 (auto-assign)
|
|
374
|
+
actual_port = port
|
|
375
|
+
if port == 0:
|
|
376
|
+
import socket
|
|
377
|
+
|
|
378
|
+
# Try to detect actual port by scanning for listening sockets
|
|
379
|
+
try:
|
|
380
|
+
import subprocess
|
|
381
|
+
|
|
382
|
+
# Use lsof to find the port bound by this process
|
|
383
|
+
result = subprocess.run(
|
|
384
|
+
["lsof", "-i", "-P", "-n", f"-p{os.getpid()}"],
|
|
385
|
+
capture_output=True,
|
|
386
|
+
text=True,
|
|
387
|
+
timeout=5,
|
|
388
|
+
)
|
|
389
|
+
for line in result.stdout.split("\n"):
|
|
390
|
+
if "LISTEN" in line and "python" in line.lower():
|
|
391
|
+
# Parse port from line like "python 1234 user 5u IPv4 ... TCP *:54321 (LISTEN)"
|
|
392
|
+
parts = line.split()
|
|
393
|
+
for part in parts:
|
|
394
|
+
if ":" in part and "(LISTEN)" not in part:
|
|
395
|
+
try:
|
|
396
|
+
port_str = part.split(":")[-1]
|
|
397
|
+
detected_port = int(port_str)
|
|
398
|
+
if detected_port > 0:
|
|
399
|
+
actual_port = detected_port
|
|
400
|
+
logger.info(
|
|
401
|
+
f"🎯 IMMEDIATE UVICORN: Detected auto-assigned port {actual_port}"
|
|
402
|
+
)
|
|
403
|
+
# Update server_info with actual port
|
|
404
|
+
server_info["port"] = actual_port
|
|
405
|
+
server_info["requested_port"] = (
|
|
406
|
+
0 # Remember original request
|
|
407
|
+
)
|
|
408
|
+
break
|
|
409
|
+
except (ValueError, IndexError):
|
|
410
|
+
pass
|
|
411
|
+
if actual_port > 0:
|
|
412
|
+
break
|
|
413
|
+
except Exception as e:
|
|
414
|
+
logger.warning(
|
|
415
|
+
f"⚠️ IMMEDIATE UVICORN: Could not detect auto-assigned port: {e}"
|
|
416
|
+
)
|
|
417
|
+
|
|
369
418
|
logger.debug(
|
|
370
|
-
f"✅ IMMEDIATE UVICORN: Uvicorn server running on {http_host}:{
|
|
419
|
+
f"✅ IMMEDIATE UVICORN: Uvicorn server running on {http_host}:{actual_port} (daemon thread)"
|
|
371
420
|
)
|
|
372
421
|
|
|
373
422
|
# Set up registry context for shutdown cleanup (use defaults initially)
|
|
@@ -982,10 +1031,10 @@ def route(
|
|
|
982
1031
|
async def upload_resume(
|
|
983
1032
|
request: Request,
|
|
984
1033
|
file: UploadFile = File(...),
|
|
985
|
-
|
|
986
|
-
user_service: mesh.
|
|
1034
|
+
pdf_tool: mesh.McpMeshTool = None, # Injected by MCP Mesh
|
|
1035
|
+
user_service: mesh.McpMeshTool = None # Injected by MCP Mesh
|
|
987
1036
|
):
|
|
988
|
-
result = await
|
|
1037
|
+
result = await pdf_tool.extract_text_from_pdf(file)
|
|
989
1038
|
await user_service.update_profile(user_data, result)
|
|
990
1039
|
return {"success": True}
|
|
991
1040
|
"""
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
MCP Mesh type definitions for dependency injection.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
import warnings
|
|
5
6
|
from collections.abc import AsyncIterator
|
|
6
7
|
from dataclasses import dataclass
|
|
7
8
|
from typing import Any, Dict, List, Optional, Protocol
|
|
@@ -14,24 +15,24 @@ except ImportError:
|
|
|
14
15
|
PYDANTIC_AVAILABLE = False
|
|
15
16
|
|
|
16
17
|
|
|
17
|
-
class
|
|
18
|
+
class McpMeshTool(Protocol):
|
|
18
19
|
"""
|
|
19
|
-
|
|
20
|
+
MCP Mesh tool proxy for dependency injection.
|
|
20
21
|
|
|
21
|
-
This protocol
|
|
22
|
-
|
|
22
|
+
This protocol defines the interface for injected tool dependencies. When you declare
|
|
23
|
+
a dependency on a remote tool, MCP Mesh injects a proxy that implements this interface.
|
|
23
24
|
|
|
24
25
|
Features:
|
|
25
|
-
-
|
|
26
|
+
- Simple callable interface for tool invocation
|
|
27
|
+
- Full MCP protocol methods (tools, resources, prompts)
|
|
26
28
|
- Streaming support with FastMCP's StreamableHttpTransport
|
|
27
29
|
- Session management with notifications
|
|
28
|
-
- Automatic redirect handling
|
|
30
|
+
- Automatic redirect handling
|
|
29
31
|
- CallToolResult objects with structured content parsing
|
|
30
|
-
- Enhanced proxy configuration via kwargs
|
|
31
32
|
|
|
32
33
|
Usage Examples:
|
|
33
34
|
@mesh.tool(dependencies=["date-service"])
|
|
34
|
-
def greet(name: str, date_service:
|
|
35
|
+
def greet(name: str, date_service: McpMeshTool) -> str:
|
|
35
36
|
# Simple call - proxy knows which remote function to invoke
|
|
36
37
|
current_date = date_service()
|
|
37
38
|
|
|
@@ -44,7 +45,7 @@ class McpMeshAgent(Protocol):
|
|
|
44
45
|
return f"Hello {name}, today is {current_date}"
|
|
45
46
|
|
|
46
47
|
@mesh.tool(dependencies=["file-service"])
|
|
47
|
-
async def process_files(file_service:
|
|
48
|
+
async def process_files(file_service: McpMeshTool) -> str:
|
|
48
49
|
# Full MCP Protocol usage
|
|
49
50
|
tools = await file_service.list_tools()
|
|
50
51
|
resources = await file_service.list_resources()
|
|
@@ -62,7 +63,7 @@ class McpMeshAgent(Protocol):
|
|
|
62
63
|
|
|
63
64
|
return "Processing complete"
|
|
64
65
|
|
|
65
|
-
The
|
|
66
|
+
The proxy provides all MCP protocol features while maintaining a simple callable interface.
|
|
66
67
|
"""
|
|
67
68
|
|
|
68
69
|
def __call__(self, arguments: Optional[dict[str, Any]] = None) -> Any:
|
|
@@ -156,15 +157,15 @@ class McpMeshAgent(Protocol):
|
|
|
156
157
|
handler: Any,
|
|
157
158
|
) -> core_schema.CoreSchema:
|
|
158
159
|
"""
|
|
159
|
-
Custom Pydantic core schema for
|
|
160
|
+
Custom Pydantic core schema for McpMeshTool.
|
|
160
161
|
|
|
161
|
-
This makes
|
|
162
|
+
This makes McpMeshTool parameters appear as optional/nullable in MCP schemas,
|
|
162
163
|
preventing serialization errors while maintaining type safety for dependency injection.
|
|
163
164
|
|
|
164
165
|
The dependency injection system will replace None values with actual proxy objects
|
|
165
166
|
at runtime, so MCP callers never need to provide these parameters.
|
|
166
167
|
"""
|
|
167
|
-
# Treat
|
|
168
|
+
# Treat McpMeshTool as an optional Any type for MCP serialization
|
|
168
169
|
return core_schema.with_default_schema(
|
|
169
170
|
core_schema.nullable_schema(core_schema.any_schema()),
|
|
170
171
|
default=None,
|
|
@@ -181,6 +182,32 @@ class McpMeshAgent(Protocol):
|
|
|
181
182
|
}
|
|
182
183
|
|
|
183
184
|
|
|
185
|
+
def _create_deprecated_mcpmeshagent():
|
|
186
|
+
"""Create McpMeshAgent as a deprecated alias for McpMeshTool."""
|
|
187
|
+
|
|
188
|
+
class McpMeshAgent(McpMeshTool, Protocol):
|
|
189
|
+
"""
|
|
190
|
+
Deprecated: Use McpMeshTool instead.
|
|
191
|
+
|
|
192
|
+
This is a backwards-compatible alias that will be removed in a future version.
|
|
193
|
+
"""
|
|
194
|
+
|
|
195
|
+
def __init_subclass__(cls, **kwargs):
|
|
196
|
+
warnings.warn(
|
|
197
|
+
"McpMeshAgent is deprecated, use McpMeshTool instead. "
|
|
198
|
+
"McpMeshAgent will be removed in a future version.",
|
|
199
|
+
DeprecationWarning,
|
|
200
|
+
stacklevel=2,
|
|
201
|
+
)
|
|
202
|
+
super().__init_subclass__(**kwargs)
|
|
203
|
+
|
|
204
|
+
return McpMeshAgent
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
# Deprecated alias for backwards compatibility
|
|
208
|
+
McpMeshAgent = _create_deprecated_mcpmeshagent()
|
|
209
|
+
|
|
210
|
+
|
|
184
211
|
class MeshLlmAgent(Protocol):
|
|
185
212
|
"""
|
|
186
213
|
LLM agent proxy with automatic agentic loop.
|
|
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "mcp-mesh"
|
|
9
|
-
version = "0.8.
|
|
9
|
+
version = "0.8.0b7"
|
|
10
10
|
description = "Kubernetes-native platform for distributed MCP applications"
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
license = { text = "MIT" }
|
|
@@ -37,7 +37,7 @@ classifiers = [
|
|
|
37
37
|
requires-python = ">=3.11"
|
|
38
38
|
dependencies = [
|
|
39
39
|
# Rust core runtime (required - no Python fallback)
|
|
40
|
-
"mcp-mesh-core>=0.8.
|
|
40
|
+
"mcp-mesh-core>=0.8.0b7",
|
|
41
41
|
"fastapi>=0.104.0,<1.0.0",
|
|
42
42
|
"uvicorn>=0.24.0,<1.0.0",
|
|
43
43
|
"httpx>=0.25.0,<1.0.0",
|
|
@@ -123,7 +123,7 @@ extend-exclude = '''
|
|
|
123
123
|
'''
|
|
124
124
|
|
|
125
125
|
[tool.ruff]
|
|
126
|
-
target-version = "0.8.
|
|
126
|
+
target-version = "0.8.0b7"
|
|
127
127
|
line-length = 88
|
|
128
128
|
|
|
129
129
|
[tool.ruff.lint]
|
|
@@ -160,7 +160,7 @@ ignore = [
|
|
|
160
160
|
"tests/**" = ["E712", "F841", "B007", "C401", "F401"] # Relax style requirements for test files
|
|
161
161
|
|
|
162
162
|
[tool.mypy]
|
|
163
|
-
python_version = "0.8.
|
|
163
|
+
python_version = "0.8.0b7"
|
|
164
164
|
check_untyped_defs = false # Temporarily relaxed
|
|
165
165
|
disallow_any_generics = false # Temporarily relaxed
|
|
166
166
|
disallow_incomplete_defs = false # Temporarily relaxed
|
|
@@ -172,7 +172,7 @@ warn_return_any = false # Temporarily relaxed
|
|
|
172
172
|
exclude = ["tests/", ".*agent_server_generated.*", ".*registry_client_generated.*"] # Skip type checking for test and generated files
|
|
173
173
|
|
|
174
174
|
[tool.pytest.ini_options]
|
|
175
|
-
minversion = "0.8.
|
|
175
|
+
minversion = "0.8.0b7"
|
|
176
176
|
addopts = "-ra -q --strict-markers --strict-config"
|
|
177
177
|
testpaths = [
|
|
178
178
|
"tests",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/engine/provider_handlers/base_provider_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/api_heartbeat/api_lifespan_integration.py
RENAMED
|
File without changes
|
{mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/api_heartbeat/rust_api_heartbeat.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/api_startup/middleware_integration.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/mcp_startup/decorator_collection.py
RENAMED
|
File without changes
|
{mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/mcp_startup/fastmcpserver_discovery.py
RENAMED
|
File without changes
|
|
File without changes
|
{mcp_mesh-0.8.0b5 → mcp_mesh-0.8.0b7}/_mcp_mesh/pipeline/mcp_startup/heartbeat_preparation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|