mcp-mesh 0.8.1__py3-none-any.whl → 0.9.0b2__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 +1 -1
- _mcp_mesh/engine/mesh_llm_agent.py +17 -10
- _mcp_mesh/engine/mesh_llm_agent_injector.py +27 -10
- _mcp_mesh/engine/provider_handlers/__init__.py +2 -0
- _mcp_mesh/engine/provider_handlers/base_provider_handler.py +177 -0
- _mcp_mesh/engine/provider_handlers/claude_handler.py +129 -102
- _mcp_mesh/engine/provider_handlers/gemini_handler.py +218 -48
- _mcp_mesh/engine/provider_handlers/generic_handler.py +31 -0
- _mcp_mesh/engine/provider_handlers/openai_handler.py +4 -1
- _mcp_mesh/pipeline/mcp_heartbeat/rust_heartbeat.py +18 -0
- _mcp_mesh/tracing/trace_context_helper.py +5 -3
- {mcp_mesh-0.8.1.dist-info → mcp_mesh-0.9.0b2.dist-info}/METADATA +2 -2
- {mcp_mesh-0.8.1.dist-info → mcp_mesh-0.9.0b2.dist-info}/RECORD +16 -16
- mesh/helpers.py +39 -46
- {mcp_mesh-0.8.1.dist-info → mcp_mesh-0.9.0b2.dist-info}/WHEEL +0 -0
- {mcp_mesh-0.8.1.dist-info → mcp_mesh-0.9.0b2.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Gemini/Google provider handler.
|
|
3
3
|
|
|
4
|
-
Optimized for Gemini models (Gemini
|
|
4
|
+
Optimized for Gemini models (Gemini 3 Flash, Gemini 3 Pro, Gemini 2.0 Flash, etc.)
|
|
5
5
|
using Google's best practices for tool calling and structured output.
|
|
6
6
|
|
|
7
7
|
Features:
|
|
8
|
-
-
|
|
8
|
+
- Mixed structured output strategy based on tool presence:
|
|
9
|
+
- STRICT mode (response_format) when NO tools are present
|
|
10
|
+
- HINT mode (prompt-based JSON hints) when tools ARE present
|
|
9
11
|
- Native function calling support
|
|
10
12
|
- Support for Gemini 2.x and 3.x models
|
|
11
13
|
- Large context windows (up to 2M tokens)
|
|
12
14
|
|
|
15
|
+
Note:
|
|
16
|
+
- Gemini 3 + response_format + tools causes non-deterministic infinite tool loops,
|
|
17
|
+
so we avoid response_format when tools are present and use HINT mode instead.
|
|
18
|
+
- STRICT mode (response_format) is only used for tool-free requests.
|
|
19
|
+
|
|
13
20
|
Reference:
|
|
14
21
|
- https://docs.litellm.ai/docs/providers/gemini
|
|
15
22
|
- https://ai.google.dev/gemini-api/docs
|
|
@@ -24,8 +31,13 @@ from .base_provider_handler import (
|
|
|
24
31
|
BASE_TOOL_INSTRUCTIONS,
|
|
25
32
|
BaseProviderHandler,
|
|
26
33
|
make_schema_strict,
|
|
34
|
+
sanitize_schema_for_structured_output,
|
|
27
35
|
)
|
|
28
36
|
|
|
37
|
+
OUTPUT_MODE_TEXT = "text"
|
|
38
|
+
OUTPUT_MODE_STRICT = "strict"
|
|
39
|
+
OUTPUT_MODE_HINT = "hint"
|
|
40
|
+
|
|
29
41
|
logger = logging.getLogger(__name__)
|
|
30
42
|
|
|
31
43
|
|
|
@@ -34,24 +46,23 @@ class GeminiHandler(BaseProviderHandler):
|
|
|
34
46
|
Provider handler for Google Gemini models.
|
|
35
47
|
|
|
36
48
|
Gemini Characteristics:
|
|
37
|
-
-
|
|
49
|
+
- Mixed structured output strategy:
|
|
50
|
+
- STRICT mode (response_format) when NO tools are present
|
|
51
|
+
- HINT mode (prompt-based JSON hints) when tools ARE present
|
|
52
|
+
- Gemini 3 + response_format + tools causes non-deterministic infinite tool loops,
|
|
53
|
+
so we avoid combining them.
|
|
38
54
|
- Native function calling support
|
|
39
55
|
- Large context windows (1M-2M tokens)
|
|
40
56
|
- Multimodal support (text, images, video, audio)
|
|
41
57
|
- Works well with concise, focused prompts
|
|
42
58
|
|
|
43
|
-
Key Similarities with OpenAI:
|
|
44
|
-
- Uses response_format for structured output (via LiteLLM translation)
|
|
45
|
-
- Native function calling format
|
|
46
|
-
- Similar schema enforcement requirements
|
|
47
|
-
|
|
48
59
|
Supported Models (via LiteLLM):
|
|
60
|
+
- gemini/gemini-3-flash-preview (reasoning support)
|
|
61
|
+
- gemini/gemini-3-pro-preview (advanced reasoning)
|
|
49
62
|
- gemini/gemini-2.0-flash (fast, efficient)
|
|
50
63
|
- gemini/gemini-2.0-flash-lite (fastest, most efficient)
|
|
51
64
|
- gemini/gemini-1.5-pro (high capability)
|
|
52
65
|
- gemini/gemini-1.5-flash (balanced)
|
|
53
|
-
- gemini/gemini-3-flash-preview (reasoning support)
|
|
54
|
-
- gemini/gemini-3-pro-preview (advanced reasoning)
|
|
55
66
|
|
|
56
67
|
Reference:
|
|
57
68
|
https://docs.litellm.ai/docs/providers/gemini
|
|
@@ -60,11 +71,29 @@ class GeminiHandler(BaseProviderHandler):
|
|
|
60
71
|
def __init__(self):
|
|
61
72
|
"""Initialize Gemini handler."""
|
|
62
73
|
super().__init__(vendor="gemini")
|
|
74
|
+
# Store output schema for use in format_system_prompt (set by apply_structured_output)
|
|
75
|
+
self._pending_output_schema: dict[str, Any] | None = None
|
|
76
|
+
self._pending_output_type_name: str | None = None
|
|
77
|
+
|
|
78
|
+
def determine_output_mode(self, output_type, override_mode=None):
|
|
79
|
+
"""Determine output mode for Gemini.
|
|
80
|
+
|
|
81
|
+
Gemini 3 supports native response_format with tools.
|
|
82
|
+
Uses STRICT mode (response_format) for all schema types.
|
|
83
|
+
TEXT mode for string return types.
|
|
84
|
+
"""
|
|
85
|
+
if override_mode:
|
|
86
|
+
return override_mode
|
|
87
|
+
if output_type is str:
|
|
88
|
+
return OUTPUT_MODE_TEXT
|
|
89
|
+
if isinstance(output_type, type) and issubclass(output_type, BaseModel):
|
|
90
|
+
return OUTPUT_MODE_STRICT
|
|
91
|
+
return OUTPUT_MODE_STRICT
|
|
63
92
|
|
|
64
93
|
def prepare_request(
|
|
65
94
|
self,
|
|
66
95
|
messages: list[dict[str, Any]],
|
|
67
|
-
tools:
|
|
96
|
+
tools: list[dict[str, Any]] | None,
|
|
68
97
|
output_type: type,
|
|
69
98
|
**kwargs: Any,
|
|
70
99
|
) -> dict[str, Any]:
|
|
@@ -72,8 +101,8 @@ class GeminiHandler(BaseProviderHandler):
|
|
|
72
101
|
Prepare request parameters for Gemini API via LiteLLM.
|
|
73
102
|
|
|
74
103
|
Gemini Strategy:
|
|
75
|
-
- Use response_format
|
|
76
|
-
-
|
|
104
|
+
- Use native response_format with strict schema (Gemini 3+)
|
|
105
|
+
- Store schema for HINT fallback in format_system_prompt
|
|
77
106
|
- Skip structured output for str return types (text mode)
|
|
78
107
|
|
|
79
108
|
Args:
|
|
@@ -98,46 +127,60 @@ class GeminiHandler(BaseProviderHandler):
|
|
|
98
127
|
|
|
99
128
|
# Skip structured output for str return type (text mode)
|
|
100
129
|
if output_type is str:
|
|
130
|
+
self._pending_output_schema = None
|
|
131
|
+
self._pending_output_type_name = None
|
|
101
132
|
return request_params
|
|
102
133
|
|
|
103
|
-
# Only
|
|
104
|
-
if
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
134
|
+
# Only store schema for Pydantic models
|
|
135
|
+
if isinstance(output_type, type) and issubclass(output_type, BaseModel):
|
|
136
|
+
schema = output_type.model_json_schema()
|
|
137
|
+
schema = sanitize_schema_for_structured_output(schema)
|
|
138
|
+
|
|
139
|
+
# Store for HINT mode in format_system_prompt (always needed as fallback)
|
|
140
|
+
self._pending_output_schema = schema
|
|
141
|
+
self._pending_output_type_name = output_type.__name__
|
|
142
|
+
|
|
143
|
+
# Only use response_format when NO tools present
|
|
144
|
+
# Gemini 3 + response_format + tools causes non-deterministic infinite tool loops
|
|
145
|
+
if not tools:
|
|
146
|
+
strict_schema = make_schema_strict(schema, add_all_required=True)
|
|
147
|
+
request_params["response_format"] = {
|
|
148
|
+
"type": "json_schema",
|
|
149
|
+
"json_schema": {
|
|
150
|
+
"name": output_type.__name__,
|
|
151
|
+
"schema": strict_schema,
|
|
152
|
+
"strict": True,
|
|
153
|
+
},
|
|
154
|
+
}
|
|
155
|
+
logger.debug(
|
|
156
|
+
"Gemini: Using response_format with strict schema for '%s' (no tools)",
|
|
157
|
+
output_type.__name__,
|
|
158
|
+
)
|
|
159
|
+
else:
|
|
160
|
+
logger.debug(
|
|
161
|
+
"Gemini: Using HINT mode for '%s' (tools present, response_format skipped)",
|
|
162
|
+
output_type.__name__,
|
|
163
|
+
)
|
|
164
|
+
else:
|
|
165
|
+
self._pending_output_schema = None
|
|
166
|
+
self._pending_output_type_name = None
|
|
124
167
|
|
|
125
168
|
return request_params
|
|
126
169
|
|
|
127
170
|
def format_system_prompt(
|
|
128
171
|
self,
|
|
129
172
|
base_prompt: str,
|
|
130
|
-
tool_schemas:
|
|
173
|
+
tool_schemas: list[dict[str, Any]] | None,
|
|
131
174
|
output_type: type,
|
|
132
175
|
) -> str:
|
|
133
176
|
"""
|
|
134
|
-
Format system prompt for Gemini
|
|
177
|
+
Format system prompt for Gemini with structured output support.
|
|
135
178
|
|
|
136
179
|
Gemini Strategy:
|
|
137
180
|
1. Use base prompt as-is
|
|
138
181
|
2. Add tool calling instructions if tools present
|
|
139
|
-
3.
|
|
140
|
-
4.
|
|
182
|
+
3. STRICT mode: Brief note (response_format handles enforcement)
|
|
183
|
+
4. HINT mode fallback: Detailed JSON output instructions with example structure
|
|
141
184
|
|
|
142
185
|
Args:
|
|
143
186
|
base_prompt: Base system prompt
|
|
@@ -153,13 +196,77 @@ class GeminiHandler(BaseProviderHandler):
|
|
|
153
196
|
if tool_schemas:
|
|
154
197
|
system_content += BASE_TOOL_INSTRUCTIONS
|
|
155
198
|
|
|
156
|
-
#
|
|
157
|
-
|
|
199
|
+
# Get the output schema (may have been set by apply_structured_output or prepare_request)
|
|
200
|
+
# Check pending schema FIRST - it may be set even when output_type is str (delegate path)
|
|
201
|
+
output_schema = self._pending_output_schema
|
|
202
|
+
output_type_name = self._pending_output_type_name
|
|
203
|
+
|
|
204
|
+
# Fall back to output_type if no pending schema AND output_type is Pydantic model
|
|
205
|
+
if output_schema is None:
|
|
206
|
+
if output_type is str:
|
|
207
|
+
# No schema and str return type - skip JSON instructions
|
|
208
|
+
return system_content
|
|
209
|
+
elif isinstance(output_type, type) and issubclass(output_type, BaseModel):
|
|
210
|
+
output_schema = sanitize_schema_for_structured_output(
|
|
211
|
+
output_type.model_json_schema()
|
|
212
|
+
)
|
|
213
|
+
output_type_name = output_type.__name__
|
|
214
|
+
|
|
215
|
+
# Determine output mode
|
|
216
|
+
determined_mode = self.determine_output_mode(output_type)
|
|
217
|
+
|
|
218
|
+
# STRICT mode: Brief note (response_format handles enforcement)
|
|
219
|
+
# But only when no tools - with tools we use HINT mode
|
|
220
|
+
if determined_mode == OUTPUT_MODE_STRICT and not tool_schemas:
|
|
221
|
+
system_content += f"\n\nYour final response will be structured as JSON matching the {output_type_name} format."
|
|
158
222
|
return system_content
|
|
159
223
|
|
|
160
|
-
#
|
|
161
|
-
if
|
|
162
|
-
system_content +=
|
|
224
|
+
# HINT mode fallback: Detailed JSON instructions
|
|
225
|
+
if output_schema is not None:
|
|
226
|
+
system_content += "\n\nOUTPUT FORMAT:\n"
|
|
227
|
+
|
|
228
|
+
# Add DECISION GUIDE if tools are available
|
|
229
|
+
if tool_schemas:
|
|
230
|
+
system_content += "DECISION GUIDE:\n"
|
|
231
|
+
system_content += "- If your answer requires real-time data (weather, calculations, etc.), call the appropriate tool FIRST, then format your response as JSON.\n"
|
|
232
|
+
system_content += "- If your answer is general knowledge (like facts, explanations, definitions), directly return your response as JSON WITHOUT calling tools.\n\n"
|
|
233
|
+
|
|
234
|
+
system_content += "Your FINAL response must be ONLY valid JSON (no markdown, no code blocks) with this exact structure:\n"
|
|
235
|
+
system_content += "{\n"
|
|
236
|
+
|
|
237
|
+
# Build example showing expected structure with descriptions
|
|
238
|
+
properties = output_schema.get("properties", {})
|
|
239
|
+
prop_items = list(properties.items())
|
|
240
|
+
for i, (prop_name, prop_schema) in enumerate(prop_items):
|
|
241
|
+
prop_type = prop_schema.get("type", "string")
|
|
242
|
+
prop_desc = prop_schema.get("description", "")
|
|
243
|
+
|
|
244
|
+
# Show example value based on type
|
|
245
|
+
if prop_type == "string":
|
|
246
|
+
example_value = f'"<your {prop_name} here>"'
|
|
247
|
+
elif prop_type in ("number", "integer"):
|
|
248
|
+
example_value = "0"
|
|
249
|
+
elif prop_type == "array":
|
|
250
|
+
example_value = '["item1", "item2"]'
|
|
251
|
+
elif prop_type == "boolean":
|
|
252
|
+
example_value = "true"
|
|
253
|
+
elif prop_type == "object":
|
|
254
|
+
example_value = "{}"
|
|
255
|
+
else:
|
|
256
|
+
example_value = "..."
|
|
257
|
+
|
|
258
|
+
# Add comma for all but last property
|
|
259
|
+
comma = "," if i < len(prop_items) - 1 else ""
|
|
260
|
+
# Include description as comment if available
|
|
261
|
+
if prop_desc:
|
|
262
|
+
system_content += (
|
|
263
|
+
f' "{prop_name}": {example_value}{comma} // {prop_desc}\n'
|
|
264
|
+
)
|
|
265
|
+
else:
|
|
266
|
+
system_content += f' "{prop_name}": {example_value}{comma}\n'
|
|
267
|
+
|
|
268
|
+
system_content += "}\n\n"
|
|
269
|
+
system_content += "Return ONLY the JSON object with actual values. Do not include the schema definition, markdown formatting, or code blocks."
|
|
163
270
|
|
|
164
271
|
return system_content
|
|
165
272
|
|
|
@@ -171,11 +278,74 @@ class GeminiHandler(BaseProviderHandler):
|
|
|
171
278
|
Capability flags for Gemini
|
|
172
279
|
"""
|
|
173
280
|
return {
|
|
174
|
-
"native_tool_calling": True,
|
|
175
|
-
"structured_output": True, #
|
|
176
|
-
"streaming": True,
|
|
177
|
-
"vision": True,
|
|
281
|
+
"native_tool_calling": True,
|
|
282
|
+
"structured_output": True, # Via native response_format (Gemini 3+)
|
|
283
|
+
"streaming": True,
|
|
284
|
+
"vision": True,
|
|
178
285
|
"json_mode": True, # Native JSON mode via response_format
|
|
179
|
-
"large_context": True,
|
|
286
|
+
"large_context": True,
|
|
180
287
|
}
|
|
181
288
|
|
|
289
|
+
def apply_structured_output(
|
|
290
|
+
self,
|
|
291
|
+
output_schema: dict[str, Any],
|
|
292
|
+
output_type_name: str | None,
|
|
293
|
+
model_params: dict[str, Any],
|
|
294
|
+
) -> dict[str, Any]:
|
|
295
|
+
"""
|
|
296
|
+
Apply Gemini-specific structured output for mesh delegation.
|
|
297
|
+
|
|
298
|
+
Uses HINT mode (prompt injection) because mesh delegation always involves
|
|
299
|
+
tools, and Gemini 3 + response_format + tools causes infinite tool loops.
|
|
300
|
+
"""
|
|
301
|
+
sanitized_schema = sanitize_schema_for_structured_output(output_schema)
|
|
302
|
+
|
|
303
|
+
# Store for format_system_prompt
|
|
304
|
+
self._pending_output_schema = sanitized_schema
|
|
305
|
+
self._pending_output_type_name = output_type_name
|
|
306
|
+
|
|
307
|
+
# Inject HINT instructions into system messages
|
|
308
|
+
# (mesh delegation always has tools, so we can't use response_format)
|
|
309
|
+
messages = model_params.get("messages", [])
|
|
310
|
+
for msg in messages:
|
|
311
|
+
if msg.get("role") == "system":
|
|
312
|
+
base_content = msg.get("content", "")
|
|
313
|
+
# Build hint instructions
|
|
314
|
+
hint_text = "\n\nOUTPUT FORMAT:\n"
|
|
315
|
+
hint_text += "Your FINAL response must be ONLY valid JSON (no markdown, no code blocks) with this exact structure:\n"
|
|
316
|
+
hint_text += "{\n"
|
|
317
|
+
properties = sanitized_schema.get("properties", {})
|
|
318
|
+
prop_items = list(properties.items())
|
|
319
|
+
for i, (prop_name, prop_schema) in enumerate(prop_items):
|
|
320
|
+
prop_type = prop_schema.get("type", "string")
|
|
321
|
+
if prop_type == "string":
|
|
322
|
+
example_value = f'"<your {prop_name} here>"'
|
|
323
|
+
elif prop_type in ("number", "integer"):
|
|
324
|
+
example_value = "0"
|
|
325
|
+
elif prop_type == "array":
|
|
326
|
+
example_value = '["item1", "item2"]'
|
|
327
|
+
elif prop_type == "boolean":
|
|
328
|
+
example_value = "true"
|
|
329
|
+
elif prop_type == "object":
|
|
330
|
+
example_value = "{}"
|
|
331
|
+
else:
|
|
332
|
+
example_value = "..."
|
|
333
|
+
comma = "," if i < len(prop_items) - 1 else ""
|
|
334
|
+
prop_desc = prop_schema.get("description", "")
|
|
335
|
+
if prop_desc:
|
|
336
|
+
hint_text += (
|
|
337
|
+
f' "{prop_name}": {example_value}{comma} // {prop_desc}\n'
|
|
338
|
+
)
|
|
339
|
+
else:
|
|
340
|
+
hint_text += f' "{prop_name}": {example_value}{comma}\n'
|
|
341
|
+
hint_text += "}\n\n"
|
|
342
|
+
hint_text += "Return ONLY the JSON object with actual values. Do not include the schema definition, markdown formatting, or code blocks."
|
|
343
|
+
|
|
344
|
+
msg["content"] = base_content + hint_text
|
|
345
|
+
break
|
|
346
|
+
|
|
347
|
+
logger.info(
|
|
348
|
+
"Gemini hint mode for '%s' (mesh delegation, schema in prompt)",
|
|
349
|
+
output_type_name or "Response",
|
|
350
|
+
)
|
|
351
|
+
return model_params
|
|
@@ -160,3 +160,34 @@ TOOL CALLING RULES:
|
|
|
160
160
|
"vision": False, # Conservative - not all models support vision
|
|
161
161
|
"json_mode": False, # Conservative - use prompt-based JSON instead
|
|
162
162
|
}
|
|
163
|
+
|
|
164
|
+
def apply_structured_output(
|
|
165
|
+
self,
|
|
166
|
+
output_schema: dict[str, Any],
|
|
167
|
+
output_type_name: Optional[str],
|
|
168
|
+
model_params: dict[str, Any],
|
|
169
|
+
) -> dict[str, Any]:
|
|
170
|
+
"""
|
|
171
|
+
Apply structured output for generic vendors.
|
|
172
|
+
|
|
173
|
+
Generic strategy: Don't use response_format since not all vendors support it.
|
|
174
|
+
The consumer should rely on prompt-based instructions instead.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
output_schema: JSON schema dict from consumer
|
|
178
|
+
output_type_name: Name of the output type
|
|
179
|
+
model_params: Current model parameters dict
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
Unmodified model_params (no response_format added)
|
|
183
|
+
"""
|
|
184
|
+
# Don't add response_format - generic vendors may not support it
|
|
185
|
+
# The consumer's system prompt should include JSON instructions
|
|
186
|
+
import logging
|
|
187
|
+
|
|
188
|
+
logger = logging.getLogger(__name__)
|
|
189
|
+
logger.debug(
|
|
190
|
+
f"⚠️ Generic handler: skipping response_format for '{output_type_name}' "
|
|
191
|
+
f"(vendor '{self.vendor}' may not support structured output)"
|
|
192
|
+
)
|
|
193
|
+
return model_params
|
|
@@ -13,6 +13,7 @@ from .base_provider_handler import (
|
|
|
13
13
|
BASE_TOOL_INSTRUCTIONS,
|
|
14
14
|
BaseProviderHandler,
|
|
15
15
|
make_schema_strict,
|
|
16
|
+
sanitize_schema_for_structured_output,
|
|
16
17
|
)
|
|
17
18
|
|
|
18
19
|
|
|
@@ -96,6 +97,9 @@ class OpenAIHandler(BaseProviderHandler):
|
|
|
96
97
|
# rather than relying on prompt instructions alone
|
|
97
98
|
schema = output_type.model_json_schema()
|
|
98
99
|
|
|
100
|
+
# Sanitize schema first to remove unsupported validation keywords (minimum, maximum, etc.)
|
|
101
|
+
schema = sanitize_schema_for_structured_output(schema)
|
|
102
|
+
|
|
99
103
|
# Transform schema for OpenAI strict mode
|
|
100
104
|
# OpenAI requires additionalProperties: false and all properties in required
|
|
101
105
|
schema = make_schema_strict(schema, add_all_required=True)
|
|
@@ -176,4 +180,3 @@ class OpenAIHandler(BaseProviderHandler):
|
|
|
176
180
|
"vision": True, # GPT-4V and later support vision
|
|
177
181
|
"json_mode": True, # Has dedicated JSON mode via response_format
|
|
178
182
|
}
|
|
179
|
-
|
|
@@ -201,6 +201,21 @@ def _build_agent_spec(context: dict[str, Any]) -> Any:
|
|
|
201
201
|
)
|
|
202
202
|
break
|
|
203
203
|
|
|
204
|
+
# Extract kwargs (non-standard fields like vendor)
|
|
205
|
+
# These are spread as top-level keys in metadata by @mesh.tool(**kwargs)
|
|
206
|
+
standard_fields = {
|
|
207
|
+
"capability",
|
|
208
|
+
"tags",
|
|
209
|
+
"version",
|
|
210
|
+
"description",
|
|
211
|
+
"dependencies",
|
|
212
|
+
"input_schema",
|
|
213
|
+
}
|
|
214
|
+
kwargs_data = {
|
|
215
|
+
k: v for k, v in tool_metadata.items() if k not in standard_fields
|
|
216
|
+
}
|
|
217
|
+
kwargs_json = json.dumps(kwargs_data) if kwargs_data else None
|
|
218
|
+
|
|
204
219
|
tool_spec = core.ToolSpec(
|
|
205
220
|
function_name=tool_name,
|
|
206
221
|
capability=tool_metadata.get("capability", tool_name),
|
|
@@ -211,6 +226,7 @@ def _build_agent_spec(context: dict[str, Any]) -> Any:
|
|
|
211
226
|
input_schema=input_schema_json,
|
|
212
227
|
llm_filter=llm_filter_json,
|
|
213
228
|
llm_provider=llm_provider_json,
|
|
229
|
+
kwargs=kwargs_json,
|
|
214
230
|
)
|
|
215
231
|
tools.append(tool_spec)
|
|
216
232
|
logger.info(
|
|
@@ -559,6 +575,7 @@ async def _handle_llm_tools_update(
|
|
|
559
575
|
"input_schema": (
|
|
560
576
|
json.loads(tool.input_schema) if tool.input_schema else None
|
|
561
577
|
),
|
|
578
|
+
"description": tool.description if tool.description else "",
|
|
562
579
|
}
|
|
563
580
|
tool_list.append(tool_info)
|
|
564
581
|
|
|
@@ -606,6 +623,7 @@ async def _handle_llm_provider_update(
|
|
|
606
623
|
"endpoint": provider_info.endpoint,
|
|
607
624
|
"name": provider_info.function_name, # OpenAPI contract uses "name"
|
|
608
625
|
"model": provider_info.model,
|
|
626
|
+
"vendor": provider_info.vendor, # For handler selection
|
|
609
627
|
}
|
|
610
628
|
}
|
|
611
629
|
injector.process_llm_providers(llm_providers)
|
|
@@ -102,7 +102,9 @@ class TraceContextHelper:
|
|
|
102
102
|
trace_id = request.headers.get("X-Trace-ID")
|
|
103
103
|
parent_span = request.headers.get("X-Parent-Span")
|
|
104
104
|
|
|
105
|
-
# Try extracting from JSON-RPC body as fallback
|
|
105
|
+
# Try extracting from JSON-RPC body as fallback (for TypeScript agents that
|
|
106
|
+
# inject trace context into arguments since FastMCP doesn't expose HTTP headers)
|
|
107
|
+
# Keys use underscore prefix to match injection convention: _trace_id, _parent_span
|
|
106
108
|
if not trace_id:
|
|
107
109
|
try:
|
|
108
110
|
body = await request.body()
|
|
@@ -110,8 +112,8 @@ class TraceContextHelper:
|
|
|
110
112
|
payload = json.loads(body.decode("utf-8"))
|
|
111
113
|
if payload.get("method") == "tools/call":
|
|
112
114
|
arguments = payload.get("params", {}).get("arguments", {})
|
|
113
|
-
trace_id = arguments.get("
|
|
114
|
-
parent_span = arguments.get("
|
|
115
|
+
trace_id = arguments.get("_trace_id")
|
|
116
|
+
parent_span = arguments.get("_parent_span")
|
|
115
117
|
except Exception:
|
|
116
118
|
pass
|
|
117
119
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcp-mesh
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0b2
|
|
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
|
|
@@ -32,7 +32,7 @@ Requires-Dist: fastmcp<3.0.0,>=2.8.0
|
|
|
32
32
|
Requires-Dist: httpx<1.0.0,>=0.25.0
|
|
33
33
|
Requires-Dist: jinja2>=3.1.0
|
|
34
34
|
Requires-Dist: litellm>=1.30.0
|
|
35
|
-
Requires-Dist: mcp-mesh-core>=0.
|
|
35
|
+
Requires-Dist: mcp-mesh-core>=0.9.0b2
|
|
36
36
|
Requires-Dist: mcp<2.0.0,>=1.9.0
|
|
37
37
|
Requires-Dist: prometheus-client<1.0.0,>=0.19.0
|
|
38
38
|
Requires-Dist: pydantic<3.0.0,>=2.4.0
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
_mcp_mesh/__init__.py,sha256=
|
|
1
|
+
_mcp_mesh/__init__.py,sha256=Ks7_PA3uBoRbonNfrL0GO_EBPvl-PWBazHALLLWx0ao,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
|
|
@@ -9,8 +9,8 @@ _mcp_mesh/engine/dependency_injector.py,sha256=p_W7pJNnGNGvixiv14CsjRV5If7Ll1VdV
|
|
|
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
|
-
_mcp_mesh/engine/mesh_llm_agent.py,sha256=
|
|
13
|
-
_mcp_mesh/engine/mesh_llm_agent_injector.py,sha256=
|
|
12
|
+
_mcp_mesh/engine/mesh_llm_agent.py,sha256=tKUjDqxoBxzsnWQm1T3RR0S1duHKgG6MSFiHfMjcFJU,35677
|
|
13
|
+
_mcp_mesh/engine/mesh_llm_agent_injector.py,sha256=L3ptyni_lOclS6gRT7z5Paw5lLcrjbCRQnMCW8OMnBU,37899
|
|
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
|
|
@@ -19,12 +19,12 @@ _mcp_mesh/engine/signature_analyzer.py,sha256=bG9HEsDtJlzeS2ueypLpcp7qD4_zso4DH1
|
|
|
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
21
|
_mcp_mesh/engine/unified_mcp_proxy.py,sha256=SZXlgdeNzlEGynwLLmmTi5R1-OrBaw4P8Izqhfn-zmI,37846
|
|
22
|
-
_mcp_mesh/engine/provider_handlers/__init__.py,sha256=
|
|
23
|
-
_mcp_mesh/engine/provider_handlers/base_provider_handler.py,sha256=
|
|
24
|
-
_mcp_mesh/engine/provider_handlers/claude_handler.py,sha256=
|
|
25
|
-
_mcp_mesh/engine/provider_handlers/gemini_handler.py,sha256=
|
|
26
|
-
_mcp_mesh/engine/provider_handlers/generic_handler.py,sha256=
|
|
27
|
-
_mcp_mesh/engine/provider_handlers/openai_handler.py,sha256=
|
|
22
|
+
_mcp_mesh/engine/provider_handlers/__init__.py,sha256=07aK9bdo2-XlzkmdIqTOJQtZbycERBJNQN90nJg6AZg,934
|
|
23
|
+
_mcp_mesh/engine/provider_handlers/base_provider_handler.py,sha256=ExvRwfb2QVMbebWyuKQqJH_V-pkJ6R-3gnVGebBgu9o,13870
|
|
24
|
+
_mcp_mesh/engine/provider_handlers/claude_handler.py,sha256=t23IygAIqeEJuIL16PFdL6COHrCcHb8XrXYiVnX8Vpg,15455
|
|
25
|
+
_mcp_mesh/engine/provider_handlers/gemini_handler.py,sha256=cfCkXN6VJWxzwRUvklwA8_Le1LYYJpFqpvxBRjIwGUU,14338
|
|
26
|
+
_mcp_mesh/engine/provider_handlers/generic_handler.py,sha256=SBPWpEf0u-797WBZXzMcr78JTSnxPmgBsqvn7zIqJVw,6617
|
|
27
|
+
_mcp_mesh/engine/provider_handlers/openai_handler.py,sha256=fIxALmtdE5JL80c59jHt7btQvYddXNsTSU1xZ9xJ45g,6417
|
|
28
28
|
_mcp_mesh/engine/provider_handlers/provider_handler_registry.py,sha256=klBZW8iX6Jk88TASAUyP8oPo4wvLB3DaTNFHeYvGLiA,5708
|
|
29
29
|
_mcp_mesh/pipeline/__init__.py,sha256=MgPwpwbiD62ND4HXKKNGcnreDk-TvPmQOs5WmjtHQ3M,1263
|
|
30
30
|
_mcp_mesh/pipeline/api_heartbeat/__init__.py,sha256=qGjEgxbGJFSl9Qm3bwu3X5yizAMbN4WpFtIUekDSFuU,690
|
|
@@ -38,7 +38,7 @@ _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=
|
|
41
|
+
_mcp_mesh/pipeline/mcp_heartbeat/rust_heartbeat.py,sha256=9vKkmYE4YGr2vkXKKWExggIZbwMjkmSWSks0a4qw4cU,27699
|
|
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
|
|
@@ -72,14 +72,14 @@ _mcp_mesh/tracing/context.py,sha256=2ozqKEYfx4Qxj64DnbwoVIbMkhNLbaV8BNWtkzAPA7I,
|
|
|
72
72
|
_mcp_mesh/tracing/execution_tracer.py,sha256=dvM6QSbxk5s5rZZsCgVNaXWrr2J4_5I6XAfI1aM0sSA,10268
|
|
73
73
|
_mcp_mesh/tracing/fastapi_tracing_middleware.py,sha256=FXjhA1A1Krk-ngyuOZPc1Ic4Llggv4Wide9OuPmkwCY,6959
|
|
74
74
|
_mcp_mesh/tracing/redis_metadata_publisher.py,sha256=DeFrMt0ZX7k6393dH-xoRS2V5apPR-k80X8ZjrKBHMU,2890
|
|
75
|
-
_mcp_mesh/tracing/trace_context_helper.py,sha256=
|
|
75
|
+
_mcp_mesh/tracing/trace_context_helper.py,sha256=Iy9FDXCzNjcXOHJ7iT9T98DC3brHQC2UYMLVTcgmZjs,6724
|
|
76
76
|
_mcp_mesh/tracing/utils.py,sha256=GWwfvab0tYGr9QAe_zgZjZxgDKTTs0p5Mf8w6WJeWC0,4486
|
|
77
77
|
_mcp_mesh/utils/fastmcp_schema_extractor.py,sha256=fttO1EABbf4GWKjE9V5DimwbhzGY9DbfGWQ2ak4SRnE,17264
|
|
78
78
|
mesh/__init__.py,sha256=avMnUHkNAK7VgON2OhXkrFB290gr1HErghmTZpOXr-U,4207
|
|
79
79
|
mesh/decorators.py,sha256=Xru9NoOolmdm-awGuuQkUgBb-s5bq9UF4p5QdVidAvI,71374
|
|
80
|
-
mesh/helpers.py,sha256=
|
|
80
|
+
mesh/helpers.py,sha256=LM4eLwsBW6IlHaT5sPZkk39EN-CFK8mqCaxfgrWrsLw,15145
|
|
81
81
|
mesh/types.py,sha256=vr0CKyPbP6lHgxj9kh_GMSLo3xkJ66PFPV_opfRb1H4,17772
|
|
82
|
-
mcp_mesh-0.
|
|
83
|
-
mcp_mesh-0.
|
|
84
|
-
mcp_mesh-0.
|
|
85
|
-
mcp_mesh-0.
|
|
82
|
+
mcp_mesh-0.9.0b2.dist-info/METADATA,sha256=vL9HrUs2GjBkRVUE3AnHIZJnqH8GViOJ8NW8Tt_tw_E,5142
|
|
83
|
+
mcp_mesh-0.9.0b2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
84
|
+
mcp_mesh-0.9.0b2.dist-info/licenses/LICENSE,sha256=_EBQHRQThv9FPOLc5eFOUdeeRO0mYwChC7cx60dM1tM,1078
|
|
85
|
+
mcp_mesh-0.9.0b2.dist-info/RECORD,,
|