mirror of
https://github.com/mcp-use/mcp-use.git
synced 2026-09-14 18:06:01 +08:00
266a445934
* 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>
353 lines
13 KiB
Python
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
|