@@ -97,34 +97,13 @@ async def create_openai_response(
9797
9898 # Apply tool filtering if enabled and tools are provided
9999 filtered_tools = tools
100- if (
101- tools
102- and self .config .tools_filter .enabled
103- and len (tools ) > self .config .tools_filter .min_tools
104- ):
105- logger .info (
106- "Tool filtering enabled - filtering %d tools (threshold: %d)" ,
107- len (tools ),
108- self .config .tools_filter .min_tools ,
109- )
100+ if tools and self .config .tools_filter .enabled :
110101 filtered_tools = await self ._filter_tools_for_response (
111102 input = input ,
112103 tools = tools ,
113104 model = model ,
114105 conversation = conversation ,
115106 )
116- logger .info (
117- "Tool filtering complete - reduced from %d to %d tools" ,
118- len (tools ),
119- len (filtered_tools ) if filtered_tools else 0 ,
120- )
121- else :
122- logger .info (
123- "Skipping tool filtering - %d tools (threshold: %d, enabled: %s)" ,
124- len (tools ) if tools else 0 ,
125- self .config .tools_filter .min_tools ,
126- self .config .tools_filter .enabled ,
127- )
128107
129108 # Call parent with filtered tools and temperature
130109 return await super ().create_openai_response (
@@ -189,6 +168,20 @@ async def _filter_tools_for_response(
189168 logger .warning ("No tool definitions found for filtering" )
190169 return tools
191170
171+ if len (tools_for_filtering ) <= self .config .tools_filter .min_tools :
172+ logger .info (
173+ "Skipping tool filtering - %d tools (threshold: %d)" ,
174+ len (tools_for_filtering ),
175+ self .config .tools_filter .min_tools ,
176+ )
177+ return tools
178+
179+ logger .info (
180+ "Tool filtering enabled - filtering %d tools (threshold: %d)" ,
181+ len (tools_for_filtering ),
182+ self .config .tools_filter .min_tools ,
183+ )
184+
192185 # Extract user prompt text from input
193186 if isinstance (input , str ):
194187 user_prompt = input
@@ -246,28 +239,28 @@ async def _filter_tools_for_response(
246239 logger .error ("Failed to parse LLM response as JSON: %s" , exp )
247240 filtered_tool_names = []
248241
249- # Filter the original tools list
250- if filtered_tool_names or always_included_tools :
251- # Create a mapping from tool names to tool configs
252- tool_name_to_config = {}
253- for i , tool in enumerate (tools ):
242+ # Merge always-included tools into filtered list
243+ filtered_tool_names = list (set (filtered_tool_names ) | always_included_tools )
244+
245+ # Filter using expanded tool definitions
246+ if filtered_tool_names :
247+ result = []
248+ for tool in tools :
254249 tool_dict = tool if isinstance (tool , dict ) else tool .model_dump ()
255- tool_name = self ._get_tool_name_from_config (tool_dict , i )
256- tool_name_to_config [tool_name ] = tool
250+ tool_type = tool_dict .get ("type" )
257251
258- # Filter based on LLM response and always included tools
259- filtered_tools = [
260- tool_name_to_config [name ]
261- for name in tool_name_to_config
262- if name in filtered_tool_names or name in always_included_tools
263- ]
252+ if tool_type == "mcp" and len (filtered_tool_names ) > 0 :
253+ tool .allowed_tools = filtered_tool_names
254+ result .append (tool )
255+ else :
256+ result .append (tool )
264257
265258 logger .info (
266- "Filtered tools count : %d removed, %d remaining" ,
267- len (tools ) - len (filtered_tools ),
268- len (filtered_tools ),
259+ "Filtered tools: %d removed, %d remaining" ,
260+ len (tools_for_filtering ) - len (filtered_tool_names ),
261+ len (filtered_tool_names ),
269262 )
270- return filtered_tools
263+ return result
271264 else :
272265 logger .warning ("No tools matched filtering criteria, returning empty list" )
273266 return []
@@ -295,6 +288,9 @@ async def _get_previously_called_tools(self, conversation_id: str) -> set[str]:
295288 if item_type == "function_call" :
296289 if hasattr (item , "name" ) and item .name :
297290 tool_names .add (item .name )
291+ elif item_type in ("mcp_call" , "mcp_approval_request" ):
292+ if hasattr (item , "name" ) and item .name :
293+ tool_names .add (item .name )
298294 # Also check for nested tool_calls (legacy format)
299295 elif hasattr (item , "tool_calls" ) and item .tool_calls :
300296 for tool_call in item .tool_calls :
@@ -321,34 +317,23 @@ async def _extract_tool_definitions(
321317 List of dicts with tool_name and description
322318 """
323319 tool_defs = []
320+ seen_tool_names : set [str ] = set ()
324321
325- for i , tool in enumerate ( tools ) :
322+ for tool in tools :
326323 tool_dict = tool if isinstance (tool , dict ) else tool .model_dump ()
327324 tool_type = tool_dict .get ("type" )
328325
329326 if tool_type == "mcp" :
330327 mcp_tools = await self ._get_mcp_tool_definitions (tool_dict )
331- tool_defs .extend (mcp_tools )
332- elif tool_type == "file_search" :
333- tool_defs .append (
334- {
335- "tool_name" : "file_search" ,
336- "description" : "Search through uploaded files and knowledge base" ,
337- }
338- )
339- elif tool_type == "function" :
340- name = tool_dict .get ("name" , f"function_{ i } " )
341- description = tool_dict .get ("description" , "" )
342- tool_defs .append ({"tool_name" : name , "description" : description })
343- else :
344- logger .warning ("Unknown tool type: %s" , tool_type )
345- tool_defs .append (
346- {
347- "tool_name" : f"{ tool_type } _{ i } " ,
348- "description" : f"Tool of type { tool_type } " ,
349- }
350- )
351-
328+ for mcp_tool in mcp_tools :
329+ if mcp_tool ["tool_name" ] not in seen_tool_names :
330+ seen_tool_names .add (mcp_tool ["tool_name" ])
331+ tool_defs .append (mcp_tool )
332+
333+ logger .info (
334+ "Extracted %d unique tool definitions from %d tool configs" ,
335+ len (tool_defs ), len (tools ),
336+ )
352337 return tool_defs
353338
354339 async def _get_mcp_tool_definitions (
@@ -376,6 +361,9 @@ async def _get_mcp_tool_definitions(
376361 from llama_stack_api .common .content_types import URL
377362
378363 mcp_endpoint = URL (uri = server_url )
364+ # Note: llama-stack 0.4.x ignores the mcp_endpoint parameter and
365+ # returns tools from ALL registered MCP servers. Deduplication is
366+ # handled in _extract_tool_definitions.
379367 tools_response = await self .tool_runtime_api .list_runtime_tools (
380368 mcp_endpoint = mcp_endpoint
381369 )
@@ -385,6 +373,11 @@ async def _get_mcp_tool_definitions(
385373 {
386374 "tool_name" : tool_def .name ,
387375 "description" : tool_def .description or "" ,
376+ "parameters" : (
377+ tool_def .parameters
378+ if hasattr (tool_def , "parameters" )
379+ else {}
380+ ),
388381 }
389382 )
390383
0 commit comments