Files
Enrico Toniato 266a445934 Canary (#524)
* chore: enter canary prerelease mode

* Improve search tool response structure (#508)

* Refactor search_tools to return metadata and results

Co-authored-by: tonxipad <tonxipad@gmail.com>

* docs: update API documentation

* feat: enhance search_tools display and filtering capabilities

- Introduced a new function to format search_tools results as a tree structure, including metadata such as total tools and namespaces.
- Updated the display logic to handle both new and old formats of search_tools results for backward compatibility.
- Enhanced filtering in MCPClient and CodeExecutor to include server name in search criteria, improving tool discoverability.

* format

* docs: update code-mode documentation to reflect changes in search_tools response structure

- Modified examples to use the new response format from search_tools, which includes metadata and results.
- Enhanced clarity in code snippets by adding print statements for tool discovery results.
- Updated TypeScript and Python documentation to ensure consistency across both languages.

* refactor: clean up whitespace in display.py for improved readability

- Removed unnecessary blank lines throughout the _format_search_tools_as_tree function to enhance code clarity and maintainability.
- Ensured consistent formatting in the display logic for better visual structure.

* refactor: streamline line wrapping in display.py for improved readability

- Consolidated parameters in the text wrapping function to enhance code clarity and maintainability.

* feat: enhance search_tools metadata for improved context

- Updated search_tools to return additional metadata (total_tools, namespaces, result_count) alongside results, enhancing the context for model decision-making.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: tonxipad <tonxipad@gmail.com>

* chore(typescript): version packages (canary)

* feat: Integrate official UI components in templates and enhance widget development experience (#517)

* feat: Add ecommerce widgets and OpenAI Apps SDK UI components

Co-authored-by: tonxipad <tonxipad@gmail.com>

* feat: Update MCP app templates and resources

- Added new fruit images to the public directory for enhanced visual content.
- Removed outdated weather and ecommerce carousel components to streamline the codebase.
- Updated the product search result component to utilize new UI components and improved error handling.
- Modified package dependencies to include react-router and updated @openai/apps-sdk-ui version.
- Enhanced the .gitignore file to include .mcp-use for better project management.

Co-authored-by: tonxipad <tonxipad@gmail.com>

* feat: Introduce new lockfile and update dependencies

- Added a new bun.lock file to manage package dependencies for the TypeScript project.
- Updated pnpm-lock.yaml to include new dependencies such as @openai/apps-sdk-ui and react-router.
- Enhanced the CLI tool to support aborting fetch requests and improved error handling in widget building.
- Replaced outdated image assets with PNG versions for better compatibility and removed unused JPG files.
- Introduced new ErrorBoundary and Image components to improve error handling and image loading in the MCP app.

Co-authored-by: tonxipad <tonxipad@gmail.com>

* feat: Enhance Vite server configuration and update dependencies

- Added a new plugin to Vite for watching the resources directory, ensuring hot module replacement (HMR) works seamlessly when widget source files change.
- Updated pnpm-lock.yaml to include new dependencies such as @openai/apps-sdk-ui, react-router, and various development tools like @tailwindcss/vite and typescript.
- Improved the Vite server configuration to explicitly watch files outside the root directory, enhancing the development experience.

Co-authored-by: tonxipad <tonxipad@gmail.com>

* feat: Implement carousel component for product search results

- Added a new CarouselItem component to enhance the visual presentation of fruit items in the product search results.
- Introduced dynamic pointer tracking for interactive hover effects on carousel items, improving user engagement.
- Updated styles for carousel items and their backgrounds to create a visually appealing blur effect.
- Refactored the ProductSearchResult component to utilize the new carousel structure and improve layout responsiveness.

Co-authored-by: tonxipad <tonxipad@gmail.com>

* feat: Update ProductSearchResult and OpenAIComponentRenderer for theme support

- Added WidgetDebugger to the ProductSearchResult component for enhanced debugging capabilities.
- Updated the product search result text to reflect a new template and improved context for users.
- Enhanced OpenAIComponentRenderer to handle theme changes, ensuring the correct theme is applied when the iframe loads.
- Modified shared-utils to include theme information in WidgetData, allowing for better theme management across components.

Co-authored-by: tonxipad <tonxipad@gmail.com>

* feat: Refactor ProductSearchResult component and enhance WidgetFullscreenWrapper

- Replaced ThemeProvider and BrowserRouter with McpUseProvider in the ProductSearchResult component for improved context management.
- Removed unused getBasename function to streamline routing logic.
- Updated WidgetFullscreenWrapper to support viewControls prop, allowing conditional rendering of fullscreen and picture-in-picture buttons.
- Enhanced overall structure and readability of the ProductSearchResult component.

Co-authored-by: tonxipad <tonxipad@gmail.com>

* feat: Update dependencies and enhance component functionality

- Added `react-router-dom` as a dependency to support routing features.
- Updated `detect-libc` to version 2.1.2 for improved compatibility.
- Refactored various components for better readability and structure, including the `ProductSearchResult` and `OpenAIComponentRenderer`.
- Introduced new auto-sizing functionality in `McpUseProvider` to notify OpenAI about container height changes.
- Removed deprecated `WidgetDebugger` and `WidgetFullscreenWrapper` components to streamline the codebase.

Co-authored-by: tonxipad <tonxipad@gmail.com>

* refactor: Enhance OpenAIComponentRenderer and ToolResultDisplay for performance and readability

- Memoized component to prevent unnecessary re-renders in OpenAIComponentRenderer.
- Introduced new state management for iframe height notifications and improved height handling logic.
- Refactored ToolResultDisplay to utilize memoization for args and result, optimizing rendering performance.
- Cleaned up unused state variables and streamlined event handling for better maintainability.

Co-authored-by: tonxipad <tonxipad@gmail.com>

* refactor: Improve logging and code readability in various components

- Enhanced logging statements in OpenAIComponentRenderer, McpUseProvider, and WidgetControls for better debugging and clarity.
- Refactored memoization in ToolResultDisplay to improve performance and prevent unnecessary re-renders.
- Cleaned up code formatting for improved readability across multiple components.

Co-authored-by: tonxipad <tonxipad@gmail.com>

* refactor: Update ProductSearchResult and shared routes for improved development experience

- Modified the ProductSearchResult component to change the FAQ question and enhance the autosize feature description.
- Simplified asset loading in shared routes to directly fetch from the dev server, improving development efficiency.
- Enhanced security headers in shared-utils to include dev server origins for better handling of HMR scripts and resource loading.

Co-authored-by: tonxipad <tonxipad@gmail.com>

* feat: Integrate @tanstack/react-query and enhance fruits API in apps-sdk template

- Added @tanstack/react-query as a dependency to improve data fetching capabilities.
- Implemented a new API endpoint in the apps-sdk template to serve a list of fruits with their respective colors.
- Removed the outdated ProductSearchResult component to streamline the codebase and focus on the new API integration.

Co-authored-by: tonxipad <tonxipad@gmail.com>

* refactor: Clean up code formatting and improve readability in product search result components

- Removed unnecessary blank lines in various TypeScript files to enhance code clarity.
- Simplified function parameters in CarouselItem component for better readability.
- Streamlined the Accordion and AccordionItem components by removing excess whitespace.

Co-authored-by: tonxipad <tonxipad@gmail.com>

* change set

* chore: Remove bun.lock file to streamline dependency management

- Deleted the bun.lock file to eliminate potential conflicts and simplify the dependency resolution process in the project.

* feat: Update documentation and components for improved clarity and organization

- Added new pages for MCP UI resources and widget components in the documentation.
- Enhanced the migration guide to specify applicability for version 1.5.0 and later.
- Updated the ErrorBoundary component icon for better representation.
- Removed outdated template comparison section to streamline the templates documentation.

* fix: Correct indentation in documentation JSON for better readability

- Adjusted the indentation of the "apps-sdk-resources" entry in the documentation JSON to maintain consistent formatting.
- Removed unnecessary blank lines to enhance overall clarity in the documentation structure.

* chore: Update dependencies and clean up code for improved performance

- Updated react-router-dom to version 7.9.5 for enhanced functionality.
- Removed commented-out code in ProductSearchResult component to improve clarity.
- Cleaned up logging statements in OpenAIComponentRenderer for better debugging.
- Refactored height notification logic in McpUseProvider to optimize performance.
- Adjusted useWidget to avoid dependency issues with URL parameters.

Co-authored-by: tonxipad <tonxipad@gmail.com>

* chore: Update dependencies and clean up code for improved performance

- Updated react-router-dom to version 7.9.5 for enhanced functionality.
- Removed commented-out code in ProductSearchResult component to improve clarity.
- Cleaned up logging statements in OpenAIComponentRenderer for better debugging.
- Refactored height notification logic in McpUseProvider to optimize performance.
- Adjusted useWidget to avoid dependency issues with URL parameters.

Co-authored-by: tonxipad <tonxipad@gmail.com>

* refactor: Clean up code formatting and improve readability in OpenAIComponentRenderer and useWidget

- Adjusted formatting of variable declarations for better consistency and readability.
- Simplified the handleError function in OpenAIComponentRenderer for clarity.
- Ensured consistent indentation in useWidget for improved code structure.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: tonxipad <tonxipad@gmail.com>

* chore(typescript): version packages (canary)

* feat: Add JSON-RPC message logging to Inspector tabs (MCP-536) (#519)

* feat: Add ecommerce widgets and OpenAI Apps SDK UI components

Co-authored-by: tonxipad <tonxipad@gmail.com>

* feat: Update MCP app templates and resources

- Added new fruit images to the public directory for enhanced visual content.
- Removed outdated weather and ecommerce carousel components to streamline the codebase.
- Updated the product search result component to utilize new UI components and improved error handling.
- Modified package dependencies to include react-router and updated @openai/apps-sdk-ui version.
- Enhanced the .gitignore file to include .mcp-use for better project management.

Co-authored-by: tonxipad <tonxipad@gmail.com>

* feat: Introduce new lockfile and update dependencies

- Added a new bun.lock file to manage package dependencies for the TypeScript project.
- Updated pnpm-lock.yaml to include new dependencies such as @openai/apps-sdk-ui and react-router.
- Enhanced the CLI tool to support aborting fetch requests and improved error handling in widget building.
- Replaced outdated image assets with PNG versions for better compatibility and removed unused JPG files.
- Introduced new ErrorBoundary and Image components to improve error handling and image loading in the MCP app.

Co-authored-by: tonxipad <tonxipad@gmail.com>

* feat: Enhance Vite server configuration and update dependencies

- Added a new plugin to Vite for watching the resources directory, ensuring hot module replacement (HMR) works seamlessly when widget source files change.
- Updated pnpm-lock.yaml to include new dependencies such as @openai/apps-sdk-ui, react-router, and various development tools like @tailwindcss/vite and typescript.
- Improved the Vite server configuration to explicitly watch files outside the root directory, enhancing the development experience.

Co-authored-by: tonxipad <tonxipad@gmail.com>

* feat: Implement carousel component for product search results

- Added a new CarouselItem component to enhance the visual presentation of fruit items in the product search results.
- Introduced dynamic pointer tracking for interactive hover effects on carousel items, improving user engagement.
- Updated styles for carousel items and their backgrounds to create a visually appealing blur effect.
- Refactored the ProductSearchResult component to utilize the new carousel structure and improve layout responsiveness.

Co-authored-by: tonxipad <tonxipad@gmail.com>

* feat: Update ProductSearchResult and OpenAIComponentRenderer for theme support

- Added WidgetDebugger to the ProductSearchResult component for enhanced debugging capabilities.
- Updated the product search result text to reflect a new template and improved context for users.
- Enhanced OpenAIComponentRenderer to handle theme changes, ensuring the correct theme is applied when the iframe loads.
- Modified shared-utils to include theme information in WidgetData, allowing for better theme management across components.

Co-authored-by: tonxipad <tonxipad@gmail.com>

* feat: Refactor ProductSearchResult component and enhance WidgetFullscreenWrapper

- Replaced ThemeProvider and BrowserRouter with McpUseProvider in the ProductSearchResult component for improved context management.
- Removed unused getBasename function to streamline routing logic.
- Updated WidgetFullscreenWrapper to support viewControls prop, allowing conditional rendering of fullscreen and picture-in-picture buttons.
- Enhanced overall structure and readability of the ProductSearchResult component.

Co-authored-by: tonxipad <tonxipad@gmail.com>

* feat: Update dependencies and enhance component functionality

- Added `react-router-dom` as a dependency to support routing features.
- Updated `detect-libc` to version 2.1.2 for improved compatibility.
- Refactored various components for better readability and structure, including the `ProductSearchResult` and `OpenAIComponentRenderer`.
- Introduced new auto-sizing functionality in `McpUseProvider` to notify OpenAI about container height changes.
- Removed deprecated `WidgetDebugger` and `WidgetFullscreenWrapper` components to streamline the codebase.

Co-authored-by: tonxipad <tonxipad@gmail.com>

* refactor: Enhance OpenAIComponentRenderer and ToolResultDisplay for performance and readability

- Memoized component to prevent unnecessary re-renders in OpenAIComponentRenderer.
- Introduced new state management for iframe height notifications and improved height handling logic.
- Refactored ToolResultDisplay to utilize memoization for args and result, optimizing rendering performance.
- Cleaned up unused state variables and streamlined event handling for better maintainability.

Co-authored-by: tonxipad <tonxipad@gmail.com>

* refactor: Improve logging and code readability in various components

- Enhanced logging statements in OpenAIComponentRenderer, McpUseProvider, and WidgetControls for better debugging and clarity.
- Refactored memoization in ToolResultDisplay to improve performance and prevent unnecessary re-renders.
- Cleaned up code formatting for improved readability across multiple components.

Co-authored-by: tonxipad <tonxipad@gmail.com>

* refactor: Update ProductSearchResult and shared routes for improved development experience

- Modified the ProductSearchResult component to change the FAQ question and enhance the autosize feature description.
- Simplified asset loading in shared routes to directly fetch from the dev server, improving development efficiency.
- Enhanced security headers in shared-utils to include dev server origins for better handling of HMR scripts and resource loading.

Co-authored-by: tonxipad <tonxipad@gmail.com>

* feat: Integrate @tanstack/react-query and enhance fruits API in apps-sdk template

- Added @tanstack/react-query as a dependency to improve data fetching capabilities.
- Implemented a new API endpoint in the apps-sdk template to serve a list of fruits with their respective colors.
- Removed the outdated ProductSearchResult component to streamline the codebase and focus on the new API integration.

Co-authored-by: tonxipad <tonxipad@gmail.com>

* refactor: Clean up code formatting and improve readability in product search result components

- Removed unnecessary blank lines in various TypeScript files to enhance code clarity.
- Simplified function parameters in CarouselItem component for better readability.
- Streamlined the Accordion and AccordionItem components by removing excess whitespace.

Co-authored-by: tonxipad <tonxipad@gmail.com>

* feat: Integrate JsonRpcLoggerView into PromptsTab, ResourcesTab, and ToolsTab for enhanced logging

- Added JsonRpcLoggerView component to PromptsTab, ResourcesTab, and ToolsTab to facilitate RPC logging.
- Adjusted ResizablePanel default sizes for better layout consistency across tabs.
- Implemented keyboard shortcut handling in ToolExecutionPanel for executing tools with Cmd/Ctrl + Enter.

Co-authored-by: tonxipad <tonxipad@gmail.com>

* format

* chore: changeset

* docs: add rpc docs

* chore: Remove bun.lock file and clean up pnpm-lock.yaml

- Deleted the bun.lock file to streamline dependency management and eliminate potential conflicts.
- Removed outdated dependencies from pnpm-lock.yaml, including entries for test_app and its associated packages, to simplify the lockfile and improve clarity.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: tonxipad <tonxipad@gmail.com>

* chore(typescript): version packages (canary)

* docs: update API documentation

* docs: api update docs

* trigger release

* chore: Clean up pnpm-lock.yaml and remove bun.lock file for streamlined dependency management

* chore(typescript): version packages (canary)

* chore(inspector): update package.json with homepage, repository, and bugs information

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: tonxipad <tonxipad@gmail.com>
2025-11-24 20:59:22 +01:00

353 lines
13 KiB
Python

"""
Code execution engine for MCP tools.
This module provides secure code execution capabilities for MCP clients,
allowing agents to interact with tools through Python code instead of
direct tool calls.
"""
import asyncio
import io
import re
import time
from contextlib import redirect_stderr, redirect_stdout
from typing import TYPE_CHECKING, Any
from mcp_use.logging import logger
if TYPE_CHECKING:
from mcp_use.client.client import MCPClient
class CodeExecutor:
"""Executes Python code with access to MCP tools in a restricted namespace.
This class provides a secure execution environment where agent-written code
can call MCP tools through dynamically generated wrapper functions.
"""
def __init__(self, client: "MCPClient"):
"""Initialize the code executor.
Args:
client: The MCPClient instance to use for tool calls.
"""
self.client = client
self._tool_cache: dict[str, dict[str, Any]] = {}
async def execute(self, code: str, timeout: float = 30.0) -> dict[str, Any]:
"""Execute Python code with access to MCP tools.
Args:
code: Python code to execute.
timeout: Execution timeout in seconds.
Returns:
Dictionary with keys:
- result: The return value from the code
- logs: List of captured print statements
- error: Error message if execution failed (None on success)
- execution_time: Time taken to execute in seconds
"""
# Ensure all servers are connected (lazy connection)
# We check client.sessions directly to see internal state
configured_servers = set(self.client.get_server_names())
active_sessions = set(self.client.sessions.keys())
# If any configured server is missing from sessions, trigger connection
if not configured_servers.issubset(active_sessions):
logger.debug("Connecting to configured servers for code execution...")
await self.client.create_all_sessions()
start_time = time.time()
logs: list[str] = []
result = None
error = None
# Capture stdout/stderr
stdout_capture = io.StringIO()
stderr_capture = io.StringIO()
try:
# Build execution namespace
namespace = await self._build_namespace()
# Add print capture function
def captured_print(*args, **kwargs):
output = io.StringIO()
print(*args, file=output, **kwargs)
log_message = output.getvalue().rstrip("\n")
logs.append(log_message)
namespace["print"] = captured_print
# Execute code with timeout
with redirect_stdout(stdout_capture), redirect_stderr(stderr_capture):
try:
result = await asyncio.wait_for(self._execute_code(code, namespace), timeout=timeout)
except TimeoutError:
error = f"Execution timeout after {timeout} seconds"
logger.warning(f"Code execution timeout: {timeout}s")
except Exception as e:
error = str(e)
logger.error(f"Code execution error: {e}")
execution_time = time.time() - start_time
# Capture any stdout/stderr that wasn't captured by our print wrapper
if stdout_capture.getvalue():
logs.extend(stdout_capture.getvalue().strip().split("\n"))
if stderr_capture.getvalue():
logs.extend([f"[ERROR] {line}" for line in stderr_capture.getvalue().strip().split("\n")])
return {"result": result, "logs": logs, "error": error, "execution_time": execution_time}
async def _execute_code(self, code: str, namespace: dict[str, Any]) -> Any:
"""Execute code in the given namespace.
Args:
code: Python code to execute.
namespace: Execution namespace with restricted globals.
Returns:
The return value from executing the code.
"""
# Always wrap code in an async function to support top-level await
# and return statements
wrapped_code = "async def __execute_wrapper__():\n"
for line in code.split("\n"):
wrapped_code += f" {line}\n"
# Compile and execute the wrapper function definition
compiled_wrapped = compile(wrapped_code, "<agent_code>", "exec")
exec(compiled_wrapped, namespace)
# Execute the wrapper and return its result
return await namespace["__execute_wrapper__"]()
async def _build_namespace(self) -> dict[str, Any]:
"""Build restricted namespace with tool wrappers.
Returns:
Dictionary containing safe builtins and tool wrappers.
"""
# Start with safe builtins
safe_builtins = {
"print": print,
"len": len,
"range": range,
"enumerate": enumerate,
"zip": zip,
"map": map,
"filter": filter,
"list": list,
"dict": dict,
"set": set,
"tuple": tuple,
"str": str,
"int": int,
"float": float,
"bool": bool,
"abs": abs,
"min": min,
"max": max,
"sum": sum,
"sorted": sorted,
"any": any,
"all": all,
"isinstance": isinstance,
"hasattr": hasattr,
"getattr": getattr,
"type": type,
"repr": repr,
"None": None,
"True": True,
"False": False,
# Exception types for error handling
"Exception": Exception,
"ValueError": ValueError,
"TypeError": TypeError,
"KeyError": KeyError,
"IndexError": IndexError,
"AttributeError": AttributeError,
"RuntimeError": RuntimeError,
}
namespace = {
"__builtins__": safe_builtins,
"asyncio": asyncio, # Allow async/await
}
# Add search_tools function
namespace["search_tools"] = self._create_search_tools_function()
# Add tool namespaces organized by server
tool_namespaces = {}
logger.debug(f"Building execution namespace from sessions: {list(self.client.sessions.keys())}")
for server_name, session in self.client.sessions.items():
# Get tools for this server
try:
tools = await session.list_tools()
# Skip if no tools found
if not tools:
continue
# Create namespace object for this server
server_namespace = type(server_name, (), {})()
for tool in tools:
tool_name = tool.name
# Sanitize tool name to be a valid Python identifier
sanitized_name = re.sub(r"[^a-zA-Z0-9_]", "_", tool_name)
if not sanitized_name[0].isalpha() and sanitized_name[0] != "_":
sanitized_name = f"_{sanitized_name}"
# Create wrapper function for this tool
wrapper = self._create_tool_wrapper(server_name, tool_name, tool)
setattr(server_namespace, sanitized_name, wrapper)
# Also keep original name if it's valid, just in case
if sanitized_name != tool_name and tool_name.isidentifier():
setattr(server_namespace, tool_name, wrapper)
tool_namespaces[server_name] = server_namespace
logger.debug(f"Added namespace '{server_name}' with {len(tools)} tools")
except Exception as e:
logger.error(f"Failed to load tools for server {server_name}: {e}")
# Add all server namespaces to the execution namespace
namespace.update(tool_namespaces)
# Add metadata about available namespaces
namespace["__tool_namespaces"] = list(tool_namespaces.keys())
logger.debug(f"Final execution namespace keys: {list(namespace.keys())}")
return namespace
def _create_tool_wrapper(self, server_name: str, tool_name: str, tool_schema: Any) -> Any:
"""Create a wrapper function for an MCP tool.
Args:
server_name: Name of the MCP server.
tool_name: Name of the tool.
tool_schema: Tool schema with description and input schema.
Returns:
Async function that calls the MCP tool.
"""
async def tool_wrapper(**kwargs):
"""Dynamically generated tool wrapper."""
import json
session = self.client.get_session(server_name)
result = await session.call_tool(tool_name, kwargs)
# Extract content from result
if hasattr(result, "content") and result.content:
# Return first content item's text if available
if len(result.content) > 0:
content_item = result.content[0]
if hasattr(content_item, "text"):
text = content_item.text
# Try to parse as JSON if possible
try:
return json.loads(text)
except (json.JSONDecodeError, ValueError):
# Return as string if not valid JSON
return text
return content_item
return result.content
return result
# Set function metadata
tool_wrapper.__name__ = tool_name
if hasattr(tool_schema, "description"):
tool_wrapper.__doc__ = tool_schema.description
return tool_wrapper
def _create_search_tools_function(self):
"""Create the search_tools function for the execution namespace.
Returns:
Function that searches available tools.
"""
async def search_tools(query: str = "", detail_level: str = "full") -> dict[str, Any]:
"""Search available MCP tools.
Args:
query: Search query to filter tools by name or description.
detail_level: Level of detail to return ("names", "descriptions", "full").
Returns:
Dictionary with:
- meta: Dictionary containing total_tools, namespaces, and result_count
- results: List of tool information dictionaries matching the query
"""
all_tools = []
all_namespaces = set()
query_lower = query.lower()
# First pass: collect all tools and namespaces
for server_name, session in self.client.sessions.items():
try:
tools = await session.list_tools()
if tools:
all_namespaces.add(server_name)
for tool in tools:
# Build tool info based on detail level (before filtering)
if detail_level == "names":
tool_info = {
"name": tool.name,
"server": server_name,
}
elif detail_level == "descriptions":
tool_info = {
"name": tool.name,
"server": server_name,
"description": getattr(tool, "description", ""),
}
else: # full
tool_info = {
"name": tool.name,
"server": server_name,
"description": getattr(tool, "description", ""),
"input_schema": getattr(tool, "inputSchema", {}),
}
all_tools.append(tool_info)
except Exception as e:
logger.error(f"Failed to list tools for server {server_name}: {e}")
# Filter by query if provided
filtered_tools = all_tools
if query:
filtered_tools = []
for tool_info in all_tools:
tool_name_match = query_lower in tool_info["name"].lower()
desc_match = query_lower in tool_info.get("description", "").lower()
server_match = query_lower in tool_info["server"].lower()
if tool_name_match or desc_match or server_match:
filtered_tools.append(tool_info)
# Return metadata along with results
return {
"meta": {
"total_tools": len(all_tools),
"namespaces": sorted(list(all_namespaces)),
"result_count": len(filtered_tools),
},
"results": filtered_tools,
}
return search_tools