mcp-mesh 0.8.0b6__py3-none-any.whl → 0.8.0b8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
_mcp_mesh/__init__.py CHANGED
@@ -31,7 +31,7 @@ from .engine.decorator_registry import (
31
31
  get_decorator_stats,
32
32
  )
33
33
 
34
- __version__ = "0.8.0b6"
34
+ __version__ = "0.8.0b8"
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 McpMeshAgent)
31
- 2. Multiple parameters: only inject into McpMeshAgent typed parameters
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 McpMeshAgent for clarity)"
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 McpMeshAgent typed parameters
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 McpMeshAgent. Skipping injection of {len(dependencies)} dependencies. "
73
- f"Consider typing dependency parameters as McpMeshAgent."
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)} McpMeshAgent parameters. "
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)} McpMeshAgent parameters "
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 (McpMeshAgent)
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 McpMeshAgent injection, this creates a wrapper at decorator time with llm_agent=None,
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 McpMeshAgent, MeshLlmAgent
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 McpMeshAgent parameters in function signature.
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 McpMeshAgent types
65
+ List of parameter positions (0-indexed) that are McpMeshTool types
20
66
 
21
67
  Example:
22
- def greet(name: str, date_svc: McpMeshAgent, file_svc: McpMeshAgent):
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 McpMeshAgent parameters
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 McpMeshAgent parameters in function signature.
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 McpMeshAgent types
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 McpMeshAgent parameters.
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)} McpMeshAgent parameters "
142
+ f"Function {func.__name__} has {len(mesh_positions)} McpMeshTool parameters "
153
143
  f"but {len(dependencies)} dependencies declared. "
154
- f"Each McpMeshAgent parameter needs a corresponding dependency."
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 McpMeshAgent type parameters,
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: Optional[str],
339
- function_name: Optional[str],
340
- agent_id: Optional[str],
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: Optional[str] = None,
344
- dep_index: Optional[int] = None,
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 or environment
202
- bind_port = int(os.getenv("MCP_MESH_HTTP_PORT", 0)) or agent_config.get(
203
- "http_port", 8080
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 (create_minimal_lifespan,
240
- create_multiple_fastmcp_lifespan,
241
- create_single_fastmcp_lifespan)
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
- # Give server a moment to start up
571
- await asyncio.sleep(0.2)
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
- # Determine actual port (for now, assume it started on requested port)
574
- actual_port = bind_port if bind_port != 0 else 8080
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 = (
@@ -38,8 +38,8 @@ class DebounceCoordinator:
38
38
  import threading
39
39
 
40
40
  self.delay_seconds = delay_seconds
41
- self._pending_timer: Optional[threading.Timer] = None
42
- self._orchestrator: Optional[MeshOrchestrator] = None
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: Optional[DebounceCoordinator] = None
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: Optional[dict] = None) -> None:
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: Optional[MeshOrchestrator] = None
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 McpMeshAgent parameters from schemas since
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 McpMeshAgent dependency injection parameters from schema.
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 McpMeshAgent parameter names from signature analysis
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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-mesh
3
- Version: 0.8.0b6
3
+ Version: 0.8.0b8
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.0b6
33
+ Requires-Dist: mcp-mesh-core>=0.8.0b8
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 McpMeshAgent
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", systemDate: McpMeshAgent = None) -> str:
95
+ def greet(name: str = "World", date_tool: McpMeshTool = None) -> str:
96
96
  """Greeting function with automatic dependency injection."""
97
- if systemDate is not None:
97
+ if date_tool is not None:
98
98
  try:
99
- current_date = systemDate()
99
+ current_date = date_tool()
100
100
  return f"Hello, {name}! Today is {current_date}"
101
101
  except Exception:
102
102
  pass
@@ -1,24 +1,24 @@
1
- _mcp_mesh/__init__.py,sha256=UC7BTZ96eZtB35l3HTks3C38jXgzlvmlDOzU1GBmcFk,2721
1
+ _mcp_mesh/__init__.py,sha256=a-p4Q5qId7rCYu62r7r43IgKGZq0hqAlBcosNOsxzGc,2721
2
2
  _mcp_mesh/reload.py,sha256=5Yll9n0bqxM7pmTjfAaKWg-WT_Vi0YTh0_UNWbCNCIQ,6217
3
3
  _mcp_mesh/reload_runner.py,sha256=SgQKzzO2yHfSUBq8s3SpAnovWA0rveimVNaxeLCEo_0,1310
4
4
  _mcp_mesh/engine/__init__.py,sha256=U_6Kw3vA_3RiNK0Oln5c5C7WvA9lSONV22wWzfxYHNw,2975
5
5
  _mcp_mesh/engine/async_mcp_client.py,sha256=Sz-rXTkb1Mng_f0SpLqLuOdPJ8vZjv3DFy0i8yYOqYk,8792
6
6
  _mcp_mesh/engine/base_injector.py,sha256=qzRLZqFP2VvEFagVovkpdldvDmm3VwPHm6tHwV58a2k,5648
7
7
  _mcp_mesh/engine/decorator_registry.py,sha256=cch2QdQ6bKjHKEGi1XWp1YcLLO3uI2YlxwWBO7Np65E,28229
8
- _mcp_mesh/engine/dependency_injector.py,sha256=-CeIGvB-zqrGxkRpFE53C6_zEmmBoyBXftvE-H9OyAY,31593
8
+ _mcp_mesh/engine/dependency_injector.py,sha256=kjRUA4Lyj9zfYJ67NuFjx9YSdZEc3QtxTTpy3ww8YTg,31584
9
9
  _mcp_mesh/engine/http_wrapper.py,sha256=Simd6IEsLO2FXQOuf1WEx57SBN6DSr5RzphXnk0asHM,24152
10
10
  _mcp_mesh/engine/llm_config.py,sha256=95bOsGWro5E1JGq7oZtEYhVdrzcIJqjht_r5vEdJVz4,2049
11
11
  _mcp_mesh/engine/llm_errors.py,sha256=h7BiI14u-jL8vtvBfFbFDDrN7gIw8PQjXIl5AP1SBuA,3276
12
12
  _mcp_mesh/engine/mesh_llm_agent.py,sha256=sVh7lPnvixDVJ-p1ONzbeakiEzhsl0HmdmrLPZA2FzQ,34237
13
- _mcp_mesh/engine/mesh_llm_agent_injector.py,sha256=Y6KkqqUAyP7QsCU2DDX_UIg9RhF4xu9p731jbfMMN-A,28373
13
+ _mcp_mesh/engine/mesh_llm_agent_injector.py,sha256=4yp8Lmc-QtBUUirDy1Fmbtl2myybYv6HtnRaaTXcBg0,28372
14
14
  _mcp_mesh/engine/response_parser.py,sha256=g3VNoFJotaLrOAS0pL_OTCrv9t9XQe9Iiz1plsm28bQ,10280
15
15
  _mcp_mesh/engine/self_dependency_proxy.py,sha256=OkKt0-B_ADnJlWtHiHItoZCBZ7Su0iz2unEPFfXvrs4,3302
16
16
  _mcp_mesh/engine/session_aware_client.py,sha256=QejKag5zYNos5BVffQvNXFMECHFMLNOv78By4e_JzQE,10589
17
17
  _mcp_mesh/engine/session_manager.py,sha256=MCr0_fXBaUjXM51WU5EhDkiGvBdfzYQFVNb9DCXXL0A,10418
18
- _mcp_mesh/engine/signature_analyzer.py,sha256=ftn9XsX0ZHWIaACdjgBVtCuIdqVU_4ST8cvcpzu4HTk,12339
18
+ _mcp_mesh/engine/signature_analyzer.py,sha256=bG9HEsDtJlzeS2ueypLpcp7qD4_zso4DH1SS_jWOHXA,11561
19
19
  _mcp_mesh/engine/tool_executor.py,sha256=Bf_9d02EEY9_yHm1p1-5YZ4rY6MPxn4SVpI6-3sm1uo,5456
20
20
  _mcp_mesh/engine/tool_schema_builder.py,sha256=SQCxQIrSfdLu9-dLqiFurQLK7dhl0dc0xa0ibaxU-iE,3644
21
- _mcp_mesh/engine/unified_mcp_proxy.py,sha256=Ee11K5HXuPXvdjqB7fmv0sMEo9ML-SMtQx2EUtYpfrY,37847
21
+ _mcp_mesh/engine/unified_mcp_proxy.py,sha256=SZXlgdeNzlEGynwLLmmTi5R1-OrBaw4P8Izqhfn-zmI,37846
22
22
  _mcp_mesh/engine/provider_handlers/__init__.py,sha256=8hEc4CheKfXWU3ny4YDktxNxLCWxgfMtyDW9CblPOvs,888
23
23
  _mcp_mesh/engine/provider_handlers/base_provider_handler.py,sha256=Lb0U6gAEseU7Ix1eeV4T0WP1ClmeXUz87Nx_iplUYSI,8077
24
24
  _mcp_mesh/engine/provider_handlers/claude_handler.py,sha256=iYAmllL5rTWFFTAjbR62Ra9eMWNZjA72a02tppxjgOQ,14343
@@ -38,17 +38,17 @@ _mcp_mesh/pipeline/api_startup/middleware_integration.py,sha256=J7Ux_nJ1VsMqVzl5
38
38
  _mcp_mesh/pipeline/api_startup/route_collection.py,sha256=WPr4hRPLIWnNIJCoRHZ141ph9tAa_-Pm_j2TiCuWS4k,2002
39
39
  _mcp_mesh/pipeline/api_startup/route_integration.py,sha256=qq1AVaWna-CWEXyehyDL3EyeYKgo5aMtei8uBNdvkZ8,12448
40
40
  _mcp_mesh/pipeline/mcp_heartbeat/__init__.py,sha256=mhDcSquoHkhRItqgbM8iFfAKC2m7qMW_0smqtUgSl-w,389
41
- _mcp_mesh/pipeline/mcp_heartbeat/rust_heartbeat.py,sha256=cB7IO0au3097MGpC6JwvYtPt4cVnl_spCkHfJIgD6Ks,25953
41
+ _mcp_mesh/pipeline/mcp_heartbeat/rust_heartbeat.py,sha256=Y0VWtgmfM_PINHoA5abjUAlyXrMdTkfr4c05NMWtA9M,26392
42
42
  _mcp_mesh/pipeline/mcp_startup/__init__.py,sha256=qy960dnAoHLXMcL_y_rcro9Km2AoCVzC7_CxMwao564,1166
43
43
  _mcp_mesh/pipeline/mcp_startup/configuration.py,sha256=OnumIPRVBTne2OEU2VWLZovLKvWcNF9iJVQtlVwuim0,2805
44
44
  _mcp_mesh/pipeline/mcp_startup/decorator_collection.py,sha256=RHC6MHtfP9aP0hZ-IJjISZu72e0Pml3LU0qr7dc284w,2294
45
- _mcp_mesh/pipeline/mcp_startup/fastapiserver_setup.py,sha256=y0xsM6no-yH9OhiYjg0LJN4hTW0vw15iuVK_4DNMxFQ,34032
45
+ _mcp_mesh/pipeline/mcp_startup/fastapiserver_setup.py,sha256=1Ylxv3zXBUyOHe05M8TqeiLEeg28R1U1zJRg18QQef0,35164
46
46
  _mcp_mesh/pipeline/mcp_startup/fastmcpserver_discovery.py,sha256=Pm24wrSuRGsgeUrHvMPDnNh6RhIZoznnMAUwAkllohk,10661
47
47
  _mcp_mesh/pipeline/mcp_startup/heartbeat_loop.py,sha256=4Fgp0_68tlSicyLHkJGB-41Av0jl5sqUeriuu-StNJU,3812
48
48
  _mcp_mesh/pipeline/mcp_startup/heartbeat_preparation.py,sha256=sOpzxRc0kYiXwSW9lvv8DSjliT85oZCWPODeJRuiqgg,15635
49
49
  _mcp_mesh/pipeline/mcp_startup/lifespan_factory.py,sha256=Hu7IvrhVH9sM7-XQDyWAGA3rgOnNIRyWFBtobkUQ5Es,4404
50
50
  _mcp_mesh/pipeline/mcp_startup/server_discovery.py,sha256=VuqqaBE00h6AerPjk-Ab-g51x6jODCbMX4nemLRQIIQ,8375
51
- _mcp_mesh/pipeline/mcp_startup/startup_orchestrator.py,sha256=bo2LeQlIsktbMFfYhX3xlnjAGT9Yj7wG96JYVT5nsyw,25893
51
+ _mcp_mesh/pipeline/mcp_startup/startup_orchestrator.py,sha256=bnWeF90G3XGzfYwritlcE-EAbCZ9ThEqJfMfzRjOYVA,25871
52
52
  _mcp_mesh/pipeline/mcp_startup/startup_pipeline.py,sha256=56lZzuCo23y3bZkDir05Ip9QlZ7uzt_gbOR32V4tAvo,2350
53
53
  _mcp_mesh/pipeline/shared/__init__.py,sha256=s9xmdf6LkoetrVRGd7Zp3NUxcJCW6YZ_yNKzUBcnYys,352
54
54
  _mcp_mesh/pipeline/shared/base_step.py,sha256=kyPbNUX79NyGrE_0Q-e-Aek7m1J0TW036njWfv0iZ0I,1080
@@ -74,12 +74,12 @@ _mcp_mesh/tracing/fastapi_tracing_middleware.py,sha256=FXjhA1A1Krk-ngyuOZPc1Ic4L
74
74
  _mcp_mesh/tracing/redis_metadata_publisher.py,sha256=DeFrMt0ZX7k6393dH-xoRS2V5apPR-k80X8ZjrKBHMU,2890
75
75
  _mcp_mesh/tracing/trace_context_helper.py,sha256=A0UipvDExePaX-E-4SAp4M8n8uwed9PMo8gibXt1v_Q,6513
76
76
  _mcp_mesh/tracing/utils.py,sha256=GWwfvab0tYGr9QAe_zgZjZxgDKTTs0p5Mf8w6WJeWC0,4486
77
- _mcp_mesh/utils/fastmcp_schema_extractor.py,sha256=NOz3dht21JRKVb_kCTrUhT2MZMmZJz04G6ARcNatVzk,17267
78
- mesh/__init__.py,sha256=NDQXXD7uuL8Ph48w-Xf7ntEUx5HkB1oZMu3hjGdntTY,3890
79
- mesh/decorators.py,sha256=sflOWhuJooqIs1tH7DqjOEaGMaVRj4GEDz7R3IQ4fPM,65163
77
+ _mcp_mesh/utils/fastmcp_schema_extractor.py,sha256=fttO1EABbf4GWKjE9V5DimwbhzGY9DbfGWQ2ak4SRnE,17264
78
+ mesh/__init__.py,sha256=avMnUHkNAK7VgON2OhXkrFB290gr1HErghmTZpOXr-U,4207
79
+ mesh/decorators.py,sha256=3h3tEhKWrlxBDYjP7vVM7iMPy7nx1oVSWVqzugC6WPM,67580
80
80
  mesh/helpers.py,sha256=1Y7V6aQvpV8BKfEeeKfjwPJ5g9FjMCzSNifs3se1jkA,12935
81
- mesh/types.py,sha256=n0MxrBYZJ84xyQWGf_X2ZbVWSAaIcEBkRV7qaCmX6Ac,17008
82
- mcp_mesh-0.8.0b6.dist-info/METADATA,sha256=SslS64gfvnRRcIJXnPdFPa-BXzwPq2pzaqMlUgcIGlE,5045
83
- mcp_mesh-0.8.0b6.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
84
- mcp_mesh-0.8.0b6.dist-info/licenses/LICENSE,sha256=_EBQHRQThv9FPOLc5eFOUdeeRO0mYwChC7cx60dM1tM,1078
85
- mcp_mesh-0.8.0b6.dist-info/RECORD,,
81
+ mesh/types.py,sha256=vr0CKyPbP6lHgxj9kh_GMSLo3xkJ66PFPV_opfRb1H4,17772
82
+ mcp_mesh-0.8.0b8.dist-info/METADATA,sha256=9EDlpBlzAltw4rErF99fBYdvmVJst8yHfIx-KFrhZUg,5040
83
+ mcp_mesh-0.8.0b8.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
84
+ mcp_mesh-0.8.0b8.dist-info/licenses/LICENSE,sha256=_EBQHRQThv9FPOLc5eFOUdeeRO0mYwChC7cx60dM1tM,1078
85
+ mcp_mesh-0.8.0b8.dist-info/RECORD,,
mesh/__init__.py CHANGED
@@ -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, MeshContextModel, MeshLlmAgent,
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
mesh/decorators.py CHANGED
@@ -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
- # Determine port (0 means auto-assign)
303
- port = http_port if http_port > 0 else 8080
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}:{port} (daemon thread)"
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
- pdf_agent: mesh.McpMeshAgent = None, # Injected by MCP Mesh
986
- user_service: mesh.McpMeshAgent = None # Injected by MCP 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 pdf_agent.extract_text_from_pdf(file)
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
  """
mesh/types.py CHANGED
@@ -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 McpMeshAgent(Protocol):
18
+ class McpMeshTool(Protocol):
18
19
  """
19
- Unified MCP Mesh agent proxy using FastMCP's built-in client.
20
+ MCP Mesh tool proxy for dependency injection.
20
21
 
21
- This protocol now provides all MCP protocol features using FastMCP's superior client
22
- implementation, replacing both the old basic and advanced proxy types.
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
- - All MCP protocol methods (tools, resources, prompts)
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 (fixes /mcp/ → /mcp issue)
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: McpMeshAgent) -> str:
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: McpMeshAgent) -> str:
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 unified proxy provides all MCP protocol features while maintaining simple callable interface.
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 McpMeshAgent.
160
+ Custom Pydantic core schema for McpMeshTool.
160
161
 
161
- This makes McpMeshAgent parameters appear as optional/nullable in MCP schemas,
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 McpMeshAgent as an optional Any type for MCP serialization
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.