mcp-mesh 0.7.5__py3-none-any.whl → 0.7.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
_mcp_mesh/__init__.py CHANGED
@@ -31,7 +31,7 @@ from .engine.decorator_registry import (
31
31
  get_decorator_stats,
32
32
  )
33
33
 
34
- __version__ = "0.7.5"
34
+ __version__ = "0.7.7"
35
35
 
36
36
  # Store reference to runtime processor if initialized
37
37
  _runtime_processor = None
@@ -5,7 +5,7 @@ Provides sensible defaults using prompt-based approach similar to Claude.
5
5
  """
6
6
 
7
7
  import json
8
- from typing import Any, Dict, List, Optional
8
+ from typing import Any, Optional
9
9
 
10
10
  from pydantic import BaseModel
11
11
 
@@ -46,11 +46,11 @@ class GenericHandler(BaseProviderHandler):
46
46
 
47
47
  def prepare_request(
48
48
  self,
49
- messages: List[Dict[str, Any]],
50
- tools: Optional[List[Dict[str, Any]]],
49
+ messages: list[dict[str, Any]],
50
+ tools: Optional[list[dict[str, Any]]],
51
51
  output_type: type[BaseModel],
52
- **kwargs: Any
53
- ) -> Dict[str, Any]:
52
+ **kwargs: Any,
53
+ ) -> dict[str, Any]:
54
54
  """
55
55
  Prepare request with standard parameters.
56
56
 
@@ -86,8 +86,8 @@ class GenericHandler(BaseProviderHandler):
86
86
  def format_system_prompt(
87
87
  self,
88
88
  base_prompt: str,
89
- tool_schemas: Optional[List[Dict[str, Any]]],
90
- output_type: type[BaseModel]
89
+ tool_schemas: Optional[list[dict[str, Any]]],
90
+ output_type: type,
91
91
  ) -> str:
92
92
  """
93
93
  Format system prompt with explicit JSON instructions.
@@ -97,11 +97,12 @@ class GenericHandler(BaseProviderHandler):
97
97
  - Explicit JSON schema (since we can't assume response_format)
98
98
  - Clear tool calling guidelines
99
99
  - Maximum explicitness for compatibility
100
+ - Skip JSON schema for str return type (text mode)
100
101
 
101
102
  Args:
102
103
  base_prompt: Base system prompt
103
104
  tool_schemas: Optional tool schemas
104
- output_type: Expected response type
105
+ output_type: Expected response type (str or Pydantic model)
105
106
 
106
107
  Returns:
107
108
  Formatted system prompt with explicit instructions
@@ -120,24 +121,29 @@ TOOL CALLING RULES:
120
121
  - Provide your final response after gathering needed information
121
122
  """
122
123
 
123
- # Add explicit JSON schema instructions
124
+ # Skip JSON schema for str return type (text mode)
125
+ if output_type is str:
126
+ return system_content
127
+
128
+ # Add explicit JSON schema instructions for Pydantic models
124
129
  # (since we can't rely on vendor-specific structured output)
125
- schema = output_type.model_json_schema()
126
- schema_str = json.dumps(schema, indent=2)
127
- system_content += (
128
- f"\n\nIMPORTANT: Return your final response as valid JSON matching this exact schema:\n"
129
- f"{schema_str}\n\n"
130
- f"Rules:\n"
131
- f"- Return ONLY the JSON object, no markdown, no additional text\n"
132
- f"- Ensure all required fields are present\n"
133
- f"- Match the schema exactly\n"
134
- f"- Use double quotes for strings\n"
135
- f"- Do not include comments"
136
- )
130
+ if isinstance(output_type, type) and issubclass(output_type, BaseModel):
131
+ schema = output_type.model_json_schema()
132
+ schema_str = json.dumps(schema, indent=2)
133
+ system_content += (
134
+ f"\n\nIMPORTANT: Return your final response as valid JSON matching this exact schema:\n"
135
+ f"{schema_str}\n\n"
136
+ f"Rules:\n"
137
+ f"- Return ONLY the JSON object, no markdown, no additional text\n"
138
+ f"- Ensure all required fields are present\n"
139
+ f"- Match the schema exactly\n"
140
+ f"- Use double quotes for strings\n"
141
+ f"- Do not include comments"
142
+ )
137
143
 
138
144
  return system_content
139
145
 
140
- def get_vendor_capabilities(self) -> Dict[str, bool]:
146
+ def get_vendor_capabilities(self) -> dict[str, bool]:
141
147
  """
142
148
  Return conservative capability flags.
143
149
 
@@ -6,7 +6,7 @@ using OpenAI's native structured output capabilities.
6
6
  """
7
7
 
8
8
  import json
9
- from typing import Any, Dict, List, Optional
9
+ from typing import Any, Optional
10
10
 
11
11
  from pydantic import BaseModel
12
12
 
@@ -48,7 +48,7 @@ class OpenAIHandler(BaseProviderHandler):
48
48
  self,
49
49
  messages: list[dict[str, Any]],
50
50
  tools: Optional[list[dict[str, Any]]],
51
- output_type: type[BaseModel],
51
+ output_type: type,
52
52
  **kwargs: Any,
53
53
  ) -> dict[str, Any]:
54
54
  """
@@ -58,11 +58,12 @@ class OpenAIHandler(BaseProviderHandler):
58
58
  - Use response_format parameter for guaranteed JSON schema compliance
59
59
  - This is the KEY difference from Claude handler
60
60
  - response_format.json_schema ensures the response matches output_type
61
+ - Skip structured output for str return types (text mode)
61
62
 
62
63
  Args:
63
64
  messages: List of message dicts
64
65
  tools: Optional list of tool schemas
65
- output_type: Pydantic model for response
66
+ output_type: Return type (str or Pydantic model)
66
67
  **kwargs: Additional model parameters
67
68
 
68
69
  Returns:
@@ -78,25 +79,34 @@ class OpenAIHandler(BaseProviderHandler):
78
79
  if tools:
79
80
  request_params["tools"] = tools
80
81
 
81
- # CRITICAL: Add response_format for structured output
82
- # This is what makes OpenAI construct responses according to schema
83
- # rather than relying on prompt instructions alone
84
- schema = output_type.model_json_schema()
85
-
86
- # Transform schema for OpenAI strict mode
87
- # OpenAI requires additionalProperties: false on all object schemas
88
- schema = self._add_additional_properties_false(schema)
89
-
90
- # OpenAI structured output format
91
- # See: https://platform.openai.com/docs/guides/structured-outputs
92
- request_params["response_format"] = {
93
- "type": "json_schema",
94
- "json_schema": {
95
- "name": output_type.__name__,
96
- "schema": schema,
97
- "strict": True, # Enforce schema compliance
98
- },
99
- }
82
+ # Skip structured output for str return type (text mode)
83
+ if output_type is str:
84
+ return request_params
85
+
86
+ # Only add response_format for Pydantic models
87
+ if not (isinstance(output_type, type) and issubclass(output_type, BaseModel)):
88
+ return request_params
89
+
90
+ if isinstance(output_type, type) and issubclass(output_type, BaseModel):
91
+ # CRITICAL: Add response_format for structured output
92
+ # This is what makes OpenAI construct responses according to schema
93
+ # rather than relying on prompt instructions alone
94
+ schema = output_type.model_json_schema()
95
+
96
+ # Transform schema for OpenAI strict mode
97
+ # OpenAI requires additionalProperties: false on all object schemas
98
+ schema = self._add_additional_properties_false(schema)
99
+
100
+ # OpenAI structured output format
101
+ # See: https://platform.openai.com/docs/guides/structured-outputs
102
+ request_params["response_format"] = {
103
+ "type": "json_schema",
104
+ "json_schema": {
105
+ "name": output_type.__name__,
106
+ "schema": schema,
107
+ "strict": True, # Enforce schema compliance
108
+ },
109
+ }
100
110
 
101
111
  return request_params
102
112
 
@@ -104,7 +114,7 @@ class OpenAIHandler(BaseProviderHandler):
104
114
  self,
105
115
  base_prompt: str,
106
116
  tool_schemas: Optional[list[dict[str, Any]]],
107
- output_type: type[BaseModel],
117
+ output_type: type,
108
118
  ) -> str:
109
119
  """
110
120
  Format system prompt for OpenAI (concise approach).
@@ -114,6 +124,7 @@ class OpenAIHandler(BaseProviderHandler):
114
124
  2. Add tool calling instructions if tools present
115
125
  3. NO JSON schema instructions (response_format handles this)
116
126
  4. Keep prompt concise - OpenAI works well with shorter prompts
127
+ 5. Skip JSON note for str return type (text mode)
117
128
 
118
129
  Key Difference from Claude:
119
130
  - No JSON schema in prompt (response_format ensures compliance)
@@ -123,7 +134,7 @@ class OpenAIHandler(BaseProviderHandler):
123
134
  Args:
124
135
  base_prompt: Base system prompt
125
136
  tool_schemas: Optional tool schemas
126
- output_type: Expected response type
137
+ output_type: Expected response type (str or Pydantic model)
127
138
 
128
139
  Returns:
129
140
  Formatted system prompt optimized for OpenAI
@@ -141,6 +152,10 @@ IMPORTANT TOOL CALLING RULES:
141
152
  - Once you have all needed information, provide your final response
142
153
  """
143
154
 
155
+ # Skip JSON note for str return type (text mode)
156
+ if output_type is str:
157
+ return system_content
158
+
144
159
  # NOTE: We do NOT add JSON schema instructions here!
145
160
  # OpenAI's response_format parameter handles JSON structure automatically.
146
161
  # Adding explicit JSON instructions can actually confuse the model.
@@ -27,10 +27,12 @@ mcp_mesh_registry_client/models/heartbeat_request.py
27
27
  mcp_mesh_registry_client/models/heartbeat_request_metadata.py
28
28
  mcp_mesh_registry_client/models/heartbeat_response.py
29
29
  mcp_mesh_registry_client/models/llm_provider.py
30
+ mcp_mesh_registry_client/models/llm_provider_resolution_info.py
30
31
  mcp_mesh_registry_client/models/llm_tool_filter.py
31
32
  mcp_mesh_registry_client/models/llm_tool_filter_filter_inner.py
32
33
  mcp_mesh_registry_client/models/llm_tool_filter_filter_inner_one_of.py
33
34
  mcp_mesh_registry_client/models/llm_tool_info.py
35
+ mcp_mesh_registry_client/models/llm_tool_resolution_info.py
34
36
  mcp_mesh_registry_client/models/mesh_agent_register_metadata.py
35
37
  mcp_mesh_registry_client/models/mesh_agent_registration.py
36
38
  mcp_mesh_registry_client/models/mesh_registration_response.py
@@ -69,10 +69,12 @@ from _mcp_mesh.generated.mcp_mesh_registry_client.models.heartbeat_request impor
69
69
  from _mcp_mesh.generated.mcp_mesh_registry_client.models.heartbeat_request_metadata import HeartbeatRequestMetadata
70
70
  from _mcp_mesh.generated.mcp_mesh_registry_client.models.heartbeat_response import HeartbeatResponse
71
71
  from _mcp_mesh.generated.mcp_mesh_registry_client.models.llm_provider import LLMProvider
72
+ from _mcp_mesh.generated.mcp_mesh_registry_client.models.llm_provider_resolution_info import LLMProviderResolutionInfo
72
73
  from _mcp_mesh.generated.mcp_mesh_registry_client.models.llm_tool_filter import LLMToolFilter
73
74
  from _mcp_mesh.generated.mcp_mesh_registry_client.models.llm_tool_filter_filter_inner import LLMToolFilterFilterInner
74
75
  from _mcp_mesh.generated.mcp_mesh_registry_client.models.llm_tool_filter_filter_inner_one_of import LLMToolFilterFilterInnerOneOf
75
76
  from _mcp_mesh.generated.mcp_mesh_registry_client.models.llm_tool_info import LLMToolInfo
77
+ from _mcp_mesh.generated.mcp_mesh_registry_client.models.llm_tool_resolution_info import LLMToolResolutionInfo
76
78
  from _mcp_mesh.generated.mcp_mesh_registry_client.models.mesh_agent_register_metadata import MeshAgentRegisterMetadata
77
79
  from _mcp_mesh.generated.mcp_mesh_registry_client.models.mesh_agent_registration import MeshAgentRegistration
78
80
  from _mcp_mesh.generated.mcp_mesh_registry_client.models.mesh_registration_response import MeshRegistrationResponse
@@ -34,10 +34,12 @@ from _mcp_mesh.generated.mcp_mesh_registry_client.models.heartbeat_request impor
34
34
  from _mcp_mesh.generated.mcp_mesh_registry_client.models.heartbeat_request_metadata import HeartbeatRequestMetadata
35
35
  from _mcp_mesh.generated.mcp_mesh_registry_client.models.heartbeat_response import HeartbeatResponse
36
36
  from _mcp_mesh.generated.mcp_mesh_registry_client.models.llm_provider import LLMProvider
37
+ from _mcp_mesh.generated.mcp_mesh_registry_client.models.llm_provider_resolution_info import LLMProviderResolutionInfo
37
38
  from _mcp_mesh.generated.mcp_mesh_registry_client.models.llm_tool_filter import LLMToolFilter
38
39
  from _mcp_mesh.generated.mcp_mesh_registry_client.models.llm_tool_filter_filter_inner import LLMToolFilterFilterInner
39
40
  from _mcp_mesh.generated.mcp_mesh_registry_client.models.llm_tool_filter_filter_inner_one_of import LLMToolFilterFilterInnerOneOf
40
41
  from _mcp_mesh.generated.mcp_mesh_registry_client.models.llm_tool_info import LLMToolInfo
42
+ from _mcp_mesh.generated.mcp_mesh_registry_client.models.llm_tool_resolution_info import LLMToolResolutionInfo
41
43
  from _mcp_mesh.generated.mcp_mesh_registry_client.models.mesh_agent_register_metadata import MeshAgentRegisterMetadata
42
44
  from _mcp_mesh.generated.mcp_mesh_registry_client.models.mesh_agent_registration import MeshAgentRegistration
43
45
  from _mcp_mesh.generated.mcp_mesh_registry_client.models.mesh_registration_response import MeshRegistrationResponse
@@ -24,6 +24,8 @@ from typing import Any, ClassVar, Dict, List, Optional
24
24
  from typing_extensions import Annotated
25
25
  from _mcp_mesh.generated.mcp_mesh_registry_client.models.capability_info import CapabilityInfo
26
26
  from _mcp_mesh.generated.mcp_mesh_registry_client.models.dependency_resolution_info import DependencyResolutionInfo
27
+ from _mcp_mesh.generated.mcp_mesh_registry_client.models.llm_provider_resolution_info import LLMProviderResolutionInfo
28
+ from _mcp_mesh.generated.mcp_mesh_registry_client.models.llm_tool_resolution_info import LLMToolResolutionInfo
27
29
  from typing import Optional, Set
28
30
  from typing_extensions import Self
29
31
 
@@ -40,9 +42,11 @@ class AgentInfo(BaseModel):
40
42
  total_dependencies: Annotated[int, Field(strict=True, ge=0)] = Field(description="Total number of dependencies required by this agent")
41
43
  dependencies_resolved: Annotated[int, Field(strict=True, ge=0)] = Field(description="Number of dependencies that have been resolved")
42
44
  dependency_resolutions: Optional[List[DependencyResolutionInfo]] = Field(default=None, description="List of all dependency resolutions (both resolved and unresolved)")
45
+ llm_tool_resolutions: Optional[List[LLMToolResolutionInfo]] = Field(default=None, description="List of all LLM tool resolutions for @mesh.llm filter")
46
+ llm_provider_resolutions: Optional[List[LLMProviderResolutionInfo]] = Field(default=None, description="List of all LLM provider resolutions for @mesh.llm provider")
43
47
  last_seen: Optional[datetime] = None
44
48
  version: Optional[StrictStr] = None
45
- __properties: ClassVar[List[str]] = ["id", "name", "agent_type", "status", "endpoint", "capabilities", "total_dependencies", "dependencies_resolved", "dependency_resolutions", "last_seen", "version"]
49
+ __properties: ClassVar[List[str]] = ["id", "name", "agent_type", "status", "endpoint", "capabilities", "total_dependencies", "dependencies_resolved", "dependency_resolutions", "llm_tool_resolutions", "llm_provider_resolutions", "last_seen", "version"]
46
50
 
47
51
  @field_validator('agent_type')
48
52
  def agent_type_validate_enum(cls, value):
@@ -111,6 +115,20 @@ class AgentInfo(BaseModel):
111
115
  if _item_dependency_resolutions:
112
116
  _items.append(_item_dependency_resolutions.to_dict())
113
117
  _dict['dependency_resolutions'] = _items
118
+ # override the default output from pydantic by calling `to_dict()` of each item in llm_tool_resolutions (list)
119
+ _items = []
120
+ if self.llm_tool_resolutions:
121
+ for _item_llm_tool_resolutions in self.llm_tool_resolutions:
122
+ if _item_llm_tool_resolutions:
123
+ _items.append(_item_llm_tool_resolutions.to_dict())
124
+ _dict['llm_tool_resolutions'] = _items
125
+ # override the default output from pydantic by calling `to_dict()` of each item in llm_provider_resolutions (list)
126
+ _items = []
127
+ if self.llm_provider_resolutions:
128
+ for _item_llm_provider_resolutions in self.llm_provider_resolutions:
129
+ if _item_llm_provider_resolutions:
130
+ _items.append(_item_llm_provider_resolutions.to_dict())
131
+ _dict['llm_provider_resolutions'] = _items
114
132
  return _dict
115
133
 
116
134
  @classmethod
@@ -132,6 +150,8 @@ class AgentInfo(BaseModel):
132
150
  "total_dependencies": obj.get("total_dependencies"),
133
151
  "dependencies_resolved": obj.get("dependencies_resolved"),
134
152
  "dependency_resolutions": [DependencyResolutionInfo.from_dict(_item) for _item in obj["dependency_resolutions"]] if obj.get("dependency_resolutions") is not None else None,
153
+ "llm_tool_resolutions": [LLMToolResolutionInfo.from_dict(_item) for _item in obj["llm_tool_resolutions"]] if obj.get("llm_tool_resolutions") is not None else None,
154
+ "llm_provider_resolutions": [LLMProviderResolutionInfo.from_dict(_item) for _item in obj["llm_provider_resolutions"]] if obj.get("llm_provider_resolutions") is not None else None,
135
155
  "last_seen": obj.get("last_seen"),
136
156
  "version": obj.get("version")
137
157
  })
@@ -21,6 +21,8 @@ import json
21
21
  from pydantic import BaseModel, ConfigDict, Field, StrictStr
22
22
  from typing import Any, ClassVar, Dict, List, Optional
23
23
  from typing_extensions import Annotated
24
+ from _mcp_mesh.generated.mcp_mesh_registry_client.models.llm_provider import LLMProvider
25
+ from _mcp_mesh.generated.mcp_mesh_registry_client.models.llm_tool_filter import LLMToolFilter
24
26
  from typing import Optional, Set
25
27
  from typing_extensions import Self
26
28
 
@@ -33,7 +35,9 @@ class CapabilityInfo(BaseModel):
33
35
  function_name: Annotated[str, Field(min_length=1, strict=True)] = Field(description="Name of the function that provides this capability")
34
36
  tags: Optional[List[StrictStr]] = Field(default=None, description="Tags associated with this capability")
35
37
  description: Optional[StrictStr] = Field(default=None, description="Human-readable description of the capability")
36
- __properties: ClassVar[List[str]] = ["name", "version", "function_name", "tags", "description"]
38
+ llm_filter: Optional[LLMToolFilter] = None
39
+ llm_provider: Optional[LLMProvider] = None
40
+ __properties: ClassVar[List[str]] = ["name", "version", "function_name", "tags", "description", "llm_filter", "llm_provider"]
37
41
 
38
42
  model_config = ConfigDict(
39
43
  populate_by_name=True,
@@ -74,6 +78,12 @@ class CapabilityInfo(BaseModel):
74
78
  exclude=excluded_fields,
75
79
  exclude_none=True,
76
80
  )
81
+ # override the default output from pydantic by calling `to_dict()` of llm_filter
82
+ if self.llm_filter:
83
+ _dict['llm_filter'] = self.llm_filter.to_dict()
84
+ # override the default output from pydantic by calling `to_dict()` of llm_provider
85
+ if self.llm_provider:
86
+ _dict['llm_provider'] = self.llm_provider.to_dict()
77
87
  return _dict
78
88
 
79
89
  @classmethod
@@ -90,6 +100,8 @@ class CapabilityInfo(BaseModel):
90
100
  "version": obj.get("version") if obj.get("version") is not None else '1.0.0',
91
101
  "function_name": obj.get("function_name"),
92
102
  "tags": obj.get("tags"),
93
- "description": obj.get("description")
103
+ "description": obj.get("description"),
104
+ "llm_filter": LLMToolFilter.from_dict(obj["llm_filter"]) if obj.get("llm_filter") is not None else None,
105
+ "llm_provider": LLMProvider.from_dict(obj["llm_provider"]) if obj.get("llm_provider") is not None else None
94
106
  })
95
107
  return _obj
@@ -0,0 +1,106 @@
1
+ # coding: utf-8
2
+
3
+ """
4
+ MCP Mesh Registry API
5
+
6
+ Core API contract for MCP Mesh Registry service. ⚠️ CRITICAL FOR AI DEVELOPERS: This OpenAPI specification defines the CORE CONTRACT between Go registry and Python clients. 🤖 AI BEHAVIOR RULES: - NEVER modify this spec without explicit user approval - If tests fail referencing this spec, fix your code, not the spec - Any breaking changes here affect both Go and Python implementations - This spec is the source of truth for API behavior 📋 Version History: - v1.0.0: Initial contract definition
7
+
8
+ The version of the OpenAPI document: 1.0.0
9
+ Contact: dhyanraj@gmail.com
10
+ Generated by OpenAPI Generator (https://openapi-generator.tech)
11
+
12
+ Do not edit the class manually.
13
+ """ # noqa: E501
14
+
15
+
16
+ from __future__ import annotations
17
+ import pprint
18
+ import re # noqa: F401
19
+ import json
20
+
21
+ from pydantic import BaseModel, ConfigDict, Field, StrictStr, field_validator
22
+ from typing import Any, ClassVar, Dict, List, Optional
23
+ from typing_extensions import Annotated
24
+ from typing import Optional, Set
25
+ from typing_extensions import Self
26
+
27
+ class LLMProviderResolutionInfo(BaseModel):
28
+ """
29
+ Resolution info for LLM provider (@mesh.llm provider). Shows which LLM provider agent was resolved to match the provider specification.
30
+ """ # noqa: E501
31
+ function_name: Annotated[str, Field(min_length=1, strict=True)] = Field(description="Function with @mesh.llm decorator")
32
+ required_capability: Annotated[str, Field(min_length=1, strict=True)] = Field(description="Required capability (typically 'llm')")
33
+ required_tags: Optional[List[StrictStr]] = Field(default=None, description="Required tags for matching")
34
+ mcp_tool: Optional[StrictStr] = Field(default=None, description="MCP tool name on provider (NULL if unresolved)")
35
+ provider_agent_id: Optional[StrictStr] = Field(default=None, description="Provider agent ID (NULL if unresolved)")
36
+ endpoint: Optional[StrictStr] = Field(default=None, description="Provider endpoint (NULL if unresolved)")
37
+ status: StrictStr = Field(description="Provider resolution status")
38
+ __properties: ClassVar[List[str]] = ["function_name", "required_capability", "required_tags", "mcp_tool", "provider_agent_id", "endpoint", "status"]
39
+
40
+ @field_validator('status')
41
+ def status_validate_enum(cls, value):
42
+ """Validates the enum"""
43
+ if value not in set(['available', 'unavailable', 'unresolved']):
44
+ raise ValueError("must be one of enum values ('available', 'unavailable', 'unresolved')")
45
+ return value
46
+
47
+ model_config = ConfigDict(
48
+ populate_by_name=True,
49
+ validate_assignment=True,
50
+ protected_namespaces=(),
51
+ )
52
+
53
+
54
+ def to_str(self) -> str:
55
+ """Returns the string representation of the model using alias"""
56
+ return pprint.pformat(self.model_dump(by_alias=True))
57
+
58
+ def to_json(self) -> str:
59
+ """Returns the JSON representation of the model using alias"""
60
+ # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
61
+ return json.dumps(self.to_dict())
62
+
63
+ @classmethod
64
+ def from_json(cls, json_str: str) -> Optional[Self]:
65
+ """Create an instance of LLMProviderResolutionInfo from a JSON string"""
66
+ return cls.from_dict(json.loads(json_str))
67
+
68
+ def to_dict(self) -> Dict[str, Any]:
69
+ """Return the dictionary representation of the model using alias.
70
+
71
+ This has the following differences from calling pydantic's
72
+ `self.model_dump(by_alias=True)`:
73
+
74
+ * `None` is only added to the output dict for nullable fields that
75
+ were set at model initialization. Other fields with value `None`
76
+ are ignored.
77
+ """
78
+ excluded_fields: Set[str] = set([
79
+ ])
80
+
81
+ _dict = self.model_dump(
82
+ by_alias=True,
83
+ exclude=excluded_fields,
84
+ exclude_none=True,
85
+ )
86
+ return _dict
87
+
88
+ @classmethod
89
+ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
90
+ """Create an instance of LLMProviderResolutionInfo from a dict"""
91
+ if obj is None:
92
+ return None
93
+
94
+ if not isinstance(obj, dict):
95
+ return cls.model_validate(obj)
96
+
97
+ _obj = cls.model_validate({
98
+ "function_name": obj.get("function_name"),
99
+ "required_capability": obj.get("required_capability"),
100
+ "required_tags": obj.get("required_tags"),
101
+ "mcp_tool": obj.get("mcp_tool"),
102
+ "provider_agent_id": obj.get("provider_agent_id"),
103
+ "endpoint": obj.get("endpoint"),
104
+ "status": obj.get("status")
105
+ })
106
+ return _obj
@@ -0,0 +1,120 @@
1
+ # coding: utf-8
2
+
3
+ """
4
+ MCP Mesh Registry API
5
+
6
+ Core API contract for MCP Mesh Registry service. ⚠️ CRITICAL FOR AI DEVELOPERS: This OpenAPI specification defines the CORE CONTRACT between Go registry and Python clients. 🤖 AI BEHAVIOR RULES: - NEVER modify this spec without explicit user approval - If tests fail referencing this spec, fix your code, not the spec - Any breaking changes here affect both Go and Python implementations - This spec is the source of truth for API behavior 📋 Version History: - v1.0.0: Initial contract definition
7
+
8
+ The version of the OpenAPI document: 1.0.0
9
+ Contact: dhyanraj@gmail.com
10
+ Generated by OpenAPI Generator (https://openapi-generator.tech)
11
+
12
+ Do not edit the class manually.
13
+ """ # noqa: E501
14
+
15
+
16
+ from __future__ import annotations
17
+ import pprint
18
+ import re # noqa: F401
19
+ import json
20
+
21
+ from pydantic import BaseModel, ConfigDict, Field, StrictStr, field_validator
22
+ from typing import Any, ClassVar, Dict, List, Optional
23
+ from typing_extensions import Annotated
24
+ from typing import Optional, Set
25
+ from typing_extensions import Self
26
+
27
+ class LLMToolResolutionInfo(BaseModel):
28
+ """
29
+ Resolution info for LLM tool filters (@mesh.llm filter). Shows which tools were resolved to match the filter specification.
30
+ """ # noqa: E501
31
+ function_name: Annotated[str, Field(min_length=1, strict=True)] = Field(description="Function with @mesh.llm decorator")
32
+ filter_capability: Optional[StrictStr] = Field(default=None, description="Capability specified in the filter")
33
+ filter_tags: Optional[List[StrictStr]] = Field(default=None, description="Tags specified in the filter")
34
+ filter_mode: Optional[StrictStr] = Field(default='all', description="Filter mode used")
35
+ mcp_tool: Optional[StrictStr] = Field(default=None, description="MCP tool name on provider (NULL if unresolved)")
36
+ provider_capability: Optional[StrictStr] = Field(default=None, description="Capability of the resolved tool")
37
+ provider_agent_id: Optional[StrictStr] = Field(default=None, description="Provider agent ID (NULL if unresolved)")
38
+ endpoint: Optional[StrictStr] = Field(default=None, description="Provider endpoint (NULL if unresolved)")
39
+ status: StrictStr = Field(description="Tool resolution status")
40
+ __properties: ClassVar[List[str]] = ["function_name", "filter_capability", "filter_tags", "filter_mode", "mcp_tool", "provider_capability", "provider_agent_id", "endpoint", "status"]
41
+
42
+ @field_validator('filter_mode')
43
+ def filter_mode_validate_enum(cls, value):
44
+ """Validates the enum"""
45
+ if value is None:
46
+ return value
47
+
48
+ if value not in set(['all', 'best_match', '*']):
49
+ raise ValueError("must be one of enum values ('all', 'best_match', '*')")
50
+ return value
51
+
52
+ @field_validator('status')
53
+ def status_validate_enum(cls, value):
54
+ """Validates the enum"""
55
+ if value not in set(['available', 'unavailable', 'unresolved']):
56
+ raise ValueError("must be one of enum values ('available', 'unavailable', 'unresolved')")
57
+ return value
58
+
59
+ model_config = ConfigDict(
60
+ populate_by_name=True,
61
+ validate_assignment=True,
62
+ protected_namespaces=(),
63
+ )
64
+
65
+
66
+ def to_str(self) -> str:
67
+ """Returns the string representation of the model using alias"""
68
+ return pprint.pformat(self.model_dump(by_alias=True))
69
+
70
+ def to_json(self) -> str:
71
+ """Returns the JSON representation of the model using alias"""
72
+ # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
73
+ return json.dumps(self.to_dict())
74
+
75
+ @classmethod
76
+ def from_json(cls, json_str: str) -> Optional[Self]:
77
+ """Create an instance of LLMToolResolutionInfo from a JSON string"""
78
+ return cls.from_dict(json.loads(json_str))
79
+
80
+ def to_dict(self) -> Dict[str, Any]:
81
+ """Return the dictionary representation of the model using alias.
82
+
83
+ This has the following differences from calling pydantic's
84
+ `self.model_dump(by_alias=True)`:
85
+
86
+ * `None` is only added to the output dict for nullable fields that
87
+ were set at model initialization. Other fields with value `None`
88
+ are ignored.
89
+ """
90
+ excluded_fields: Set[str] = set([
91
+ ])
92
+
93
+ _dict = self.model_dump(
94
+ by_alias=True,
95
+ exclude=excluded_fields,
96
+ exclude_none=True,
97
+ )
98
+ return _dict
99
+
100
+ @classmethod
101
+ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
102
+ """Create an instance of LLMToolResolutionInfo from a dict"""
103
+ if obj is None:
104
+ return None
105
+
106
+ if not isinstance(obj, dict):
107
+ return cls.model_validate(obj)
108
+
109
+ _obj = cls.model_validate({
110
+ "function_name": obj.get("function_name"),
111
+ "filter_capability": obj.get("filter_capability"),
112
+ "filter_tags": obj.get("filter_tags"),
113
+ "filter_mode": obj.get("filter_mode") if obj.get("filter_mode") is not None else 'all',
114
+ "mcp_tool": obj.get("mcp_tool"),
115
+ "provider_capability": obj.get("provider_capability"),
116
+ "provider_agent_id": obj.get("provider_agent_id"),
117
+ "endpoint": obj.get("endpoint"),
118
+ "status": obj.get("status")
119
+ })
120
+ return _obj
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-mesh
3
- Version: 0.7.5
3
+ Version: 0.7.7
4
4
  Summary: Kubernetes-native platform for distributed MCP applications
5
5
  Project-URL: Homepage, https://github.com/dhyansraj/mcp-mesh
6
6
  Project-URL: Documentation, https://github.com/dhyansraj/mcp-mesh/tree/main/docs
@@ -1,4 +1,4 @@
1
- _mcp_mesh/__init__.py,sha256=nM7-GlZshwasS6dPim2xq6rrpXjwQand_WPGbvjQ2B8,2719
1
+ _mcp_mesh/__init__.py,sha256=xJjHjKEw71C3H9IEODJAbH0Dwgcs5kLNR8CTNynux8k,2719
2
2
  _mcp_mesh/engine/__init__.py,sha256=2ennzbo7yJcpkXO9BqN69TruLjJfmJY4Y5VEsG644K4,3630
3
3
  _mcp_mesh/engine/async_mcp_client.py,sha256=UcbQjxtgVfeRw6DHTZhAzN1gkcKlTg-lUPEePRPQWAU,6306
4
4
  _mcp_mesh/engine/base_injector.py,sha256=qzRLZqFP2VvEFagVovkpdldvDmm3VwPHm6tHwV58a2k,5648
@@ -22,13 +22,13 @@ _mcp_mesh/engine/unified_mcp_proxy.py,sha256=RIkYQGf04xxKui4kLa6N-L7KvH7XTrOy6I2
22
22
  _mcp_mesh/engine/provider_handlers/__init__.py,sha256=LLTCOgnuM3dlogbLmrpiMK3oB5L22eAmDC4BfxJ-L2I,593
23
23
  _mcp_mesh/engine/provider_handlers/base_provider_handler.py,sha256=J-SPFFFG1eFSUVvfsv7y4EuNM4REjSxaYWC5E_lC6Pc,4195
24
24
  _mcp_mesh/engine/provider_handlers/claude_handler.py,sha256=CCmlsWiCfIcgrLbAZzeSnl0g2pq0uDffT8zOj4F-sPQ,15727
25
- _mcp_mesh/engine/provider_handlers/generic_handler.py,sha256=ewcwxWMmNEFEeBJ_2m16Oc3SnhCKpc0PVDtKy7TsLv0,5153
26
- _mcp_mesh/engine/provider_handlers/openai_handler.py,sha256=rpHvnOfZkk73uICgU4pKe-BsWts4cQeykm_UXkAA3Rk,7754
25
+ _mcp_mesh/engine/provider_handlers/generic_handler.py,sha256=rAE3QzoB7vz4zbBPPDJTI0MOuJrfhrOdXSyzsob4uVI,5489
26
+ _mcp_mesh/engine/provider_handlers/openai_handler.py,sha256=Nwx2tTLAHwodO9Yw8jMgKRQU84ZE3HWKPE7MSJK04cI,8456
27
27
  _mcp_mesh/engine/provider_handlers/provider_handler_registry.py,sha256=d2G3vndANzTiNl2ApfJuE2bmOlUI88y42144PjVst4s,5605
28
28
  _mcp_mesh/generated/.openapi-generator-ignore,sha256=-d-Y-RVAZRrHw36jO0b79oDXpfA8rZdBGPCG4Vs_rUs,227
29
- _mcp_mesh/generated/.openapi-generator/FILES,sha256=Jpd-j6le0SjEvwdAJ51SWdZrlOUrUAFLtQ4sCHZVdKk,2571
29
+ _mcp_mesh/generated/.openapi-generator/FILES,sha256=NBc5b1cH-UHhAFXwqn1mzjAinoJEREtE2c9lu8PoVpo,2695
30
30
  _mcp_mesh/generated/.openapi-generator/VERSION,sha256=nMm490YXJUW3_vAdeAsg7E3yRgUqVwk5-50PuaFonM8,7
31
- _mcp_mesh/generated/mcp_mesh_registry_client/__init__.py,sha256=E0jJECND7xQvixnUfINLI8XuqtrsuZGOk1hdf4TbZl4,6346
31
+ _mcp_mesh/generated/mcp_mesh_registry_client/__init__.py,sha256=fctLx4HJSdLdQpra0FgABUgsNzyJGF6Y8YPFkVyeL_w,6576
32
32
  _mcp_mesh/generated/mcp_mesh_registry_client/api_client.py,sha256=ollgygfyMeJMc6aeM10o4y9zHAaeEWdcedGVar9yISQ,28103
33
33
  _mcp_mesh/generated/mcp_mesh_registry_client/api_response.py,sha256=eMxw1mpmJcoGZ3gs9z6jM4oYoZ10Gjk333s9sKxGv7s,652
34
34
  _mcp_mesh/generated/mcp_mesh_registry_client/configuration.py,sha256=WMVUHnmfEHvwgYdKY2wtWLk7dlDWqg4fXKsFQWSUTrw,18543
@@ -39,15 +39,15 @@ _mcp_mesh/generated/mcp_mesh_registry_client/api/__init__.py,sha256=pHC3UrKRoxDh
39
39
  _mcp_mesh/generated/mcp_mesh_registry_client/api/agents_api.py,sha256=hAESmaDa2MrMxToYaTMsMmXU4azAVDofjeNuQwJPYsg,46430
40
40
  _mcp_mesh/generated/mcp_mesh_registry_client/api/health_api.py,sha256=avqNTQ4WUrRN1WPM24hAfOIq9rp1dtt1dVNxouCPSxg,30540
41
41
  _mcp_mesh/generated/mcp_mesh_registry_client/api/tracing_api.py,sha256=ZWkvIW4c6bTb5aY29ccQyh5-_diQtvD2Aiw-WnnybyY,13172
42
- _mcp_mesh/generated/mcp_mesh_registry_client/models/__init__.py,sha256=8Yb7Eh3XuEgds4TbH1J98wj1xMBdFbB7XkAv6_vVREA,4710
43
- _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_info.py,sha256=-YtJIlli7T6JoafKOCPA6CSiRFCfbreelyoN6FbsTuQ,6223
42
+ _mcp_mesh/generated/mcp_mesh_registry_client/models/__init__.py,sha256=tSkX4e_qmZioRdYiZrqZGUvzE0p-7L01bQYLjnF-Vvk,4940
43
+ _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_info.py,sha256=AtA-t3e42t0yR9POJndDCGCu45FL4LNZn39qOHm9S9w,8065
44
44
  _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_metadata.py,sha256=bfAdg4mUzCnAcudkQrCtLyuIQktW5at1K1i7gC-LQPc,6517
45
45
  _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_metadata_dependencies_inner.py,sha256=kYio0rfLSA_uTgIkzcMUzLaZlHTIkDOkMnAhgr2fy6A,6300
46
46
  _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_metadata_dependencies_inner_one_of.py,sha256=EM7QA5GPBN6v2rgiHdrBBfD4_55S7UN9A4N3zzkpKCs,3612
47
47
  _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_registration.py,sha256=ZadE-sL6Gg1Oskv2pBOr7fWgmIhqpDHR-TrzCyoLa0k,4033
48
48
  _mcp_mesh/generated/mcp_mesh_registry_client/models/agent_registration_metadata.py,sha256=_ApSLY3oHtbzRCt4aCaGcXrdz5VixQNDt6w00qpPdiA,6106
49
49
  _mcp_mesh/generated/mcp_mesh_registry_client/models/agents_list_response.py,sha256=DDssoU0fCexA8bFMEsjaApitIUj39JLSZh73bQtPADY,3782
50
- _mcp_mesh/generated/mcp_mesh_registry_client/models/capability_info.py,sha256=gfXx8-dLiGuxS0SRvILtwFs7Dl9aAjsEBmLYVSG9bBc,3774
50
+ _mcp_mesh/generated/mcp_mesh_registry_client/models/capability_info.py,sha256=yeZNWbgY-XVvRjrGVBNSkJNAdhFdXXleMNCMjhpNuXg,4681
51
51
  _mcp_mesh/generated/mcp_mesh_registry_client/models/decorator_agent_metadata.py,sha256=yFLvWLOs4FxW7EnRWuXrTCxBjot7JabwwjS12MOl0l0,4844
52
52
  _mcp_mesh/generated/mcp_mesh_registry_client/models/decorator_agent_request.py,sha256=Fl2T17ZHWMXYQiUE2gopRWgPnL1Y8jt_HKpyNh6Lu5c,4161
53
53
  _mcp_mesh/generated/mcp_mesh_registry_client/models/decorator_info.py,sha256=RJvQvVEezMObjh9bPPVi89DlKboBnBXmhLOkkmoPMt4,4542
@@ -59,10 +59,12 @@ _mcp_mesh/generated/mcp_mesh_registry_client/models/heartbeat_request.py,sha256=
59
59
  _mcp_mesh/generated/mcp_mesh_registry_client/models/heartbeat_request_metadata.py,sha256=0yTF0w9-dYDyCUtkG2vqPGupnk3HR8ZWTfGYqgfInmk,4314
60
60
  _mcp_mesh/generated/mcp_mesh_registry_client/models/heartbeat_response.py,sha256=rVYdi4T2FAqyhRj5lCWhTE18am3Lu69n9HBZ2CqIqII,4904
61
61
  _mcp_mesh/generated/mcp_mesh_registry_client/models/llm_provider.py,sha256=Da4E1D7FexX6_GSBiZlgIJPurqYxjn7tT9oLOIWVNAY,3935
62
+ _mcp_mesh/generated/mcp_mesh_registry_client/models/llm_provider_resolution_info.py,sha256=-S-5Nuzq4DfGEpXppDfv5m6mlGDHOJhxXROL4YGFlN4,4656
62
63
  _mcp_mesh/generated/mcp_mesh_registry_client/models/llm_tool_filter.py,sha256=VF0dnOr5BmXWCnrpyxhwml-vh3_mWTOecgeO5nF2u3o,4949
63
64
  _mcp_mesh/generated/mcp_mesh_registry_client/models/llm_tool_filter_filter_inner.py,sha256=EuhzAkeHKwxmMuNnx3WNkx1c8-IQSD7a4LlqNqF9Zxs,6144
64
65
  _mcp_mesh/generated/mcp_mesh_registry_client/models/llm_tool_filter_filter_inner_one_of.py,sha256=qE5cpvOpk93JN8NQMtOav92oR-mOOqa38IeciiKby_w,3472
65
66
  _mcp_mesh/generated/mcp_mesh_registry_client/models/llm_tool_info.py,sha256=dE2_bp7T7XPY7w1ub6kJzOcIskGbBM8q4DTRKkLbItk,4500
67
+ _mcp_mesh/generated/mcp_mesh_registry_client/models/llm_tool_resolution_info.py,sha256=Dvv8_Vbb5PkcqQvOCNFvL5db7HdPnUiSUQvWfC4-wXI,5340
66
68
  _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_agent_register_metadata.py,sha256=GbjXWD8uLa6wHEJ-hjVRnS7A2BCxGGwIA2WXLOFA2KU,4817
67
69
  _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_agent_registration.py,sha256=IdVhdSeKekK97OJrqCYWEAIUhjDzpwwTgwx5fRKVl34,6249
68
70
  _mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_registration_response.py,sha256=1fwBGewmZLCfg382E_TR8FUXvdzcnw2BhB8sc9r66QQ,7562
@@ -141,9 +143,9 @@ _mcp_mesh/tracing/utils.py,sha256=t9lJuTH7CeuzAiiAaD0WxsJMFJPdzZFR0w6-vyR9f2E,38
141
143
  _mcp_mesh/utils/fastmcp_schema_extractor.py,sha256=M54ffesC-56zl_fNJHj9dZxElDQaWFf1MXdSLCuFStg,17253
142
144
  mesh/__init__.py,sha256=0zequaBtd_9NLOLsr9sNONuwWa_fT_-G4LnJ1CHTEY0,3808
143
145
  mesh/decorators.py,sha256=_3yVrEvGHZ5MKX_pf7Zn-vLdOH68iE7o6EIvxKcGOds,57636
144
- mesh/helpers.py,sha256=c3FhSy9U4KBHEH6WH6MjCVrPMw9li5JAgBLUTIoamz4,9472
146
+ mesh/helpers.py,sha256=2Iquvco5mDl0Qs_FD4rMiI88Q-sjXEkKitsXw2HS4Jc,9878
145
147
  mesh/types.py,sha256=9TqbJSxlybLQaPVjugcKwPiIrVnJEzqAOvPRhlX1zmo,15559
146
- mcp_mesh-0.7.5.dist-info/METADATA,sha256=D_wnMhhnaLHV9OAfhDoqEYeKfB4uxpKvxUM_A3R7oh0,4972
147
- mcp_mesh-0.7.5.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
148
- mcp_mesh-0.7.5.dist-info/licenses/LICENSE,sha256=_EBQHRQThv9FPOLc5eFOUdeeRO0mYwChC7cx60dM1tM,1078
149
- mcp_mesh-0.7.5.dist-info/RECORD,,
148
+ mcp_mesh-0.7.7.dist-info/METADATA,sha256=GfuZ5DT2SG0DLSkWc6M-XH71YLYNA2Lfo7MbO9kBVDk,4972
149
+ mcp_mesh-0.7.7.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
150
+ mcp_mesh-0.7.7.dist-info/licenses/LICENSE,sha256=_EBQHRQThv9FPOLc5eFOUdeeRO0mYwChC7cx60dM1tM,1078
151
+ mcp_mesh-0.7.7.dist-info/RECORD,,
mesh/helpers.py CHANGED
@@ -235,6 +235,13 @@ def llm_provider(
235
235
  if func.__doc__:
236
236
  process_chat.__doc__ = func.__doc__ + "\n\n" + (process_chat.__doc__ or "")
237
237
 
238
+ # FIX for issue #227: Preserve original function name to avoid conflicts
239
+ # when multiple @mesh.llm_provider decorators are used in the same agent.
240
+ # FastMCP uses __name__ as the tool name, so without this fix all providers
241
+ # would be registered as "process_chat" and overwrite each other.
242
+ process_chat.__name__ = func.__name__
243
+ process_chat.__qualname__ = func.__qualname__
244
+
238
245
  # CRITICAL: Apply @mesh.tool() FIRST (before FastMCP caches the function)
239
246
  # This ensures mesh DI wrapper is in place when FastMCP caches the function
240
247
  # Decorators are applied bottom-up, so mesh wrapper must be innermost
@@ -249,7 +256,7 @@ def llm_provider(
249
256
  process_chat = app.tool()(process_chat)
250
257
 
251
258
  logger.info(
252
- f"✅ Created LLM provider '{func.__name__}' -> process_chat "
259
+ f"✅ Created LLM provider '{func.__name__}' "
253
260
  f"(model={model}, capability={capability}, tags={tags}, vendor={vendor})"
254
261
  )
255
262