fix: add error handling and message on mcp servers, fixed tool mode on mcp (#8717)
* Added mcp error handling * Added error param to servers * Added error display on mcp servers page * Added error display on mcp component * Added error handling for other types of errors * uv lock update and add error handling * update mcp version * fixed tool mode not working on mcp component * update mcp to 1.9.4 * Removed server parameters custom commands on connection to stdio * removed unused import * disable mcp notice * Removed drop state when home type is mcp * Added loading before showing tools * Updated mcp to 1.9.4 * Decreased mcp timeout * Implemented error surfacing with exec command * removed non default keys either way when mcp_server is used * update to session handling * [autofix.ci] apply automated fixes * updated it to check if it is the same server to not clear on startup * update to components * Update mcp_component.py * Update mcp_component.py * Update mcp_component.py --------- Co-authored-by: Edwin Jose <edwin.jose@datastax.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@langflow.org>
This commit is contained in:
parent
e1624b8c6e
commit
f4d761e63d
14 changed files with 178 additions and 45 deletions
|
|
@ -105,7 +105,7 @@ dependencies = [
|
||||||
"arize-phoenix-otel>=0.6.1",
|
"arize-phoenix-otel>=0.6.1",
|
||||||
"openinference-instrumentation-langchain>=0.1.29",
|
"openinference-instrumentation-langchain>=0.1.29",
|
||||||
"crewai==0.102.0",
|
"crewai==0.102.0",
|
||||||
"mcp>=0.9.1",
|
"mcp>=1.9.4",
|
||||||
"uv>=0.5.7",
|
"uv>=0.5.7",
|
||||||
"scipy>=1.14.1",
|
"scipy>=1.14.1",
|
||||||
"ag2>=0.1.0",
|
"ag2>=0.1.0",
|
||||||
|
|
|
||||||
|
|
@ -122,13 +122,52 @@ async def get_servers(
|
||||||
)
|
)
|
||||||
server_info["mode"] = mode.lower()
|
server_info["mode"] = mode.lower()
|
||||||
server_info["toolsCount"] = len(tool_list)
|
server_info["toolsCount"] = len(tool_list)
|
||||||
|
if len(tool_list) == 0:
|
||||||
|
server_info["error"] = "No tools found"
|
||||||
|
except ValueError as e:
|
||||||
|
# Configuration validation errors, invalid URLs, etc.
|
||||||
|
logger.error(f"Configuration error for server {server_name}: {e}")
|
||||||
|
server_info["error"] = f"Configuration error: {e}"
|
||||||
|
except (ConnectionError, TimeoutError) as e:
|
||||||
|
# Network connection and timeout issues
|
||||||
|
logger.error(f"Connection error for server {server_name}: {e}")
|
||||||
|
server_info["error"] = f"Connection failed: {e}"
|
||||||
|
except OSError as e:
|
||||||
|
# System-level errors (process execution, file access)
|
||||||
|
logger.error(f"System error for server {server_name}: {e}")
|
||||||
|
server_info["error"] = f"System error: {e}"
|
||||||
|
except (KeyError, TypeError) as e:
|
||||||
|
# Data parsing and access errors
|
||||||
|
logger.error(f"Data error for server {server_name}: {e}")
|
||||||
|
server_info["error"] = f"Configuration data error: {e}"
|
||||||
|
except (RuntimeError, ProcessLookupError, PermissionError) as e:
|
||||||
|
# Runtime and process-related errors
|
||||||
|
logger.error(f"Runtime error for server {server_name}: {e}")
|
||||||
|
server_info["error"] = f"Runtime error: {e}"
|
||||||
except Exception as e: # noqa: BLE001
|
except Exception as e: # noqa: BLE001
|
||||||
logger.exception(f"Error checking server {server_name}: {e}")
|
# Generic catch-all for other exceptions (including ExceptionGroup)
|
||||||
|
if hasattr(e, "exceptions") and e.exceptions:
|
||||||
|
# Extract the first underlying exception for a more meaningful error message
|
||||||
|
underlying_error = e.exceptions[0]
|
||||||
|
logger.exception(f"Error checking server {server_name}: {underlying_error}")
|
||||||
|
server_info["error"] = f"Error loading server: {underlying_error}"
|
||||||
|
else:
|
||||||
|
logger.exception(f"Error checking server {server_name}: {e}")
|
||||||
|
server_info["error"] = f"Error loading server: {e}"
|
||||||
return server_info
|
return server_info
|
||||||
|
|
||||||
|
async def check_server_with_timeout(server_name: str) -> dict:
|
||||||
|
try:
|
||||||
|
return await asyncio.wait_for(
|
||||||
|
check_server(server_name), timeout=get_settings_service().settings.mcp_server_timeout
|
||||||
|
)
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
logger.error(f"Timeout checking server {server_name}")
|
||||||
|
return {"name": server_name, "mode": None, "toolsCount": None, "error": "Server check timed out."}
|
||||||
|
|
||||||
# Run all server checks concurrently
|
# Run all server checks concurrently
|
||||||
tasks = [check_server(server) for server in server_list["mcpServers"]]
|
tasks = [check_server_with_timeout(server) for server in server_list["mcpServers"]]
|
||||||
return await asyncio.gather(*tasks, return_exceptions=False)
|
return await asyncio.gather(*tasks, return_exceptions=True)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/servers/{server_name}")
|
@router.get("/servers/{server_name}")
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ from pydantic import BaseModel, Field, create_model
|
||||||
from sqlmodel import select
|
from sqlmodel import select
|
||||||
|
|
||||||
from langflow.services.database.models.flow.model import Flow
|
from langflow.services.database.models.flow.model import Flow
|
||||||
|
from langflow.services.deps import get_settings_service
|
||||||
|
|
||||||
HTTP_ERROR_STATUS_CODE = httpx_codes.BAD_REQUEST # HTTP status code for client errors
|
HTTP_ERROR_STATUS_CODE = httpx_codes.BAD_REQUEST # HTTP status code for client errors
|
||||||
NULLABLE_TYPE_LENGTH = 2 # Number of types in a nullable union (the type itself + null)
|
NULLABLE_TYPE_LENGTH = 2 # Number of types in a nullable union (the type itself + null)
|
||||||
|
|
@ -300,7 +301,7 @@ class MCPStdioClient:
|
||||||
self._connection_params = None
|
self._connection_params = None
|
||||||
self._connected = False
|
self._connected = False
|
||||||
|
|
||||||
async def connect_to_server(self, command_str: str, env: dict[str, str] | None = None) -> list[StructuredTool]:
|
async def _connect_to_server(self, command_str: str, env: dict[str, str] | None = None) -> list[StructuredTool]:
|
||||||
"""Connect to MCP server using stdio transport (SDK style)."""
|
"""Connect to MCP server using stdio transport (SDK style)."""
|
||||||
from mcp import StdioServerParameters
|
from mcp import StdioServerParameters
|
||||||
from mcp.client.stdio import stdio_client
|
from mcp.client.stdio import stdio_client
|
||||||
|
|
@ -320,7 +321,7 @@ class MCPStdioClient:
|
||||||
else:
|
else:
|
||||||
server_params = StdioServerParameters(
|
server_params = StdioServerParameters(
|
||||||
command="bash",
|
command="bash",
|
||||||
args=["-c", f"{command_str} || echo 'Command failed with exit code $?' >&2"],
|
args=["-c", f"exec {command_str} || echo 'Command failed with exit code $?' >&2"],
|
||||||
env=env_data,
|
env=env_data,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -339,6 +340,19 @@ class MCPStdioClient:
|
||||||
self._connected = False
|
self._connected = False
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
async def connect_to_server(self, command_str: str, env: dict[str, str] | None = None) -> list[StructuredTool]:
|
||||||
|
"""Connect to MCP server using stdio transport (SDK style)."""
|
||||||
|
try:
|
||||||
|
return await asyncio.wait_for(
|
||||||
|
self._connect_to_server(command_str, env), timeout=get_settings_service().settings.mcp_server_timeout
|
||||||
|
)
|
||||||
|
except (ConnectionError, TimeoutError, OSError, ValueError) as e:
|
||||||
|
logger.error(f"Failed to connect to MCP stdio server: {e}")
|
||||||
|
self._connection_params = None
|
||||||
|
self._connected = False
|
||||||
|
msg = f"Failed to connect to MCP stdio server: {e}"
|
||||||
|
raise ValueError(msg) from e
|
||||||
|
|
||||||
async def disconnect(self):
|
async def disconnect(self):
|
||||||
"""Properly close the connection and clean up resources."""
|
"""Properly close the connection and clean up resources."""
|
||||||
self.session = None
|
self.session = None
|
||||||
|
|
@ -425,7 +439,7 @@ class MCPSseClient:
|
||||||
logger.warning(f"Error checking redirects: {e}")
|
logger.warning(f"Error checking redirects: {e}")
|
||||||
return url
|
return url
|
||||||
|
|
||||||
async def connect_to_server(
|
async def _connect_to_server(
|
||||||
self,
|
self,
|
||||||
url: str | None,
|
url: str | None,
|
||||||
headers: dict[str, str] | None = None,
|
headers: dict[str, str] | None = None,
|
||||||
|
|
@ -464,6 +478,19 @@ class MCPSseClient:
|
||||||
response = await session.list_tools()
|
response = await session.list_tools()
|
||||||
self._connected = True
|
self._connected = True
|
||||||
return response.tools
|
return response.tools
|
||||||
|
except (ConnectionError, TimeoutError, OSError, ValueError) as e:
|
||||||
|
logger.error(f"Failed to connect to MCP SSE server: {e}")
|
||||||
|
self._connection_params = None
|
||||||
|
self._connected = False
|
||||||
|
msg = f"Failed to connect to MCP SSE server: {e}"
|
||||||
|
raise ValueError(msg) from e
|
||||||
|
|
||||||
|
async def connect_to_server(self, url: str, headers: dict[str, str] | None = None) -> list[StructuredTool]:
|
||||||
|
"""Connect to MCP server using SSE transport (SDK style)."""
|
||||||
|
try:
|
||||||
|
return await asyncio.wait_for(
|
||||||
|
self._connect_to_server(url, headers), timeout=get_settings_service().settings.mcp_server_timeout
|
||||||
|
)
|
||||||
except (ConnectionError, TimeoutError, OSError, ValueError) as e:
|
except (ConnectionError, TimeoutError, OSError, ValueError) as e:
|
||||||
logger.error(f"Failed to connect to MCP SSE server: {e}")
|
logger.error(f"Failed to connect to MCP SSE server: {e}")
|
||||||
self._connection_params = None
|
self._connection_params = None
|
||||||
|
|
@ -568,7 +595,10 @@ async def update_tools(
|
||||||
return "", [], {}
|
return "", [], {}
|
||||||
except (ConnectionError, TimeoutError, OSError, ValueError) as e:
|
except (ConnectionError, TimeoutError, OSError, ValueError) as e:
|
||||||
logger.error(f"Failed to connect to MCP server '{server_name}': {e}")
|
logger.error(f"Failed to connect to MCP server '{server_name}': {e}")
|
||||||
return "", [], {}
|
# return "", [], {}
|
||||||
|
msg = f"Failed to connect to MCP server '{server_name}': {e}"
|
||||||
|
logger.error(msg)
|
||||||
|
raise ValueError(msg) from e
|
||||||
|
|
||||||
if not tools or not client or not client._connected:
|
if not tools or not client or not client._connected:
|
||||||
logger.warning(f"No tools available from MCP server '{server_name}' or connection failed")
|
logger.warning(f"No tools available from MCP server '{server_name}' or connection failed")
|
||||||
|
|
@ -598,10 +628,11 @@ async def update_tools(
|
||||||
tool_cache[tool.name] = tool_obj
|
tool_cache[tool.name] = tool_obj
|
||||||
except (ConnectionError, TimeoutError, OSError, ValueError) as e:
|
except (ConnectionError, TimeoutError, OSError, ValueError) as e:
|
||||||
logger.error(f"Failed to create tool '{tool.name}' from server '{server_name}': {e}")
|
logger.error(f"Failed to create tool '{tool.name}' from server '{server_name}': {e}")
|
||||||
continue
|
msg = f"Failed to create tool '{tool.name}' from server '{server_name}': {e}"
|
||||||
|
raise ValueError(msg) from e
|
||||||
|
|
||||||
logger.info(f"Successfully loaded {len(tool_list)} tools from MCP server '{server_name}'")
|
logger.info(f"Successfully loaded {len(tool_list)} tools from MCP server '{server_name}'")
|
||||||
except (ConnectionError, TimeoutError, OSError, ValueError) as e:
|
except (ConnectionError, TimeoutError, OSError, ValueError, AttributeError, AssertionError) as e:
|
||||||
logger.error(f"Unexpected error while updating tools for MCP server '{server_name}': {e}")
|
logger.error(f"Unexpected error while updating tools for MCP server '{server_name}': {e}")
|
||||||
return "", [], {}
|
return "", [], {}
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@ class MCPToolsComponent(ComponentWithCache):
|
||||||
sse_client: MCPSseClient = MCPSseClient()
|
sse_client: MCPSseClient = MCPSseClient()
|
||||||
tools: list = []
|
tools: list = []
|
||||||
_tool_cache: dict = {}
|
_tool_cache: dict = {}
|
||||||
|
_last_selected_server: str | None = None # Cache for the last selected server
|
||||||
default_keys: list[str] = [
|
default_keys: list[str] = [
|
||||||
"code",
|
"code",
|
||||||
"_type",
|
"_type",
|
||||||
|
|
@ -217,12 +218,14 @@ class MCPToolsComponent(ComponentWithCache):
|
||||||
if len(self.tools) == 0:
|
if len(self.tools) == 0:
|
||||||
try:
|
try:
|
||||||
self.tools, build_config["mcp_server"]["value"] = await self.update_tool_list()
|
self.tools, build_config["mcp_server"]["value"] = await self.update_tool_list()
|
||||||
|
build_config["tool"]["options"] = [tool.name for tool in self.tools]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
build_config["tool"]["options"] = []
|
build_config["tool"]["options"] = []
|
||||||
build_config["tool"]["value"] = ""
|
build_config["tool"]["value"] = ""
|
||||||
build_config["tool"]["placeholder"] = "Error on MCP Server"
|
build_config["tool"]["placeholder"] = "Error on MCP Server"
|
||||||
return build_config
|
return build_config
|
||||||
build_config["tool"]["placeholder"] = ""
|
build_config["tool"]["placeholder"] = ""
|
||||||
|
|
||||||
if field_value == "":
|
if field_value == "":
|
||||||
return build_config
|
return build_config
|
||||||
tool_obj = None
|
tool_obj = None
|
||||||
|
|
@ -242,30 +245,61 @@ class MCPToolsComponent(ComponentWithCache):
|
||||||
else:
|
else:
|
||||||
return build_config
|
return build_config
|
||||||
elif field_name == "mcp_server":
|
elif field_name == "mcp_server":
|
||||||
try:
|
if not field_value:
|
||||||
# field_value is now a dict {name, config}
|
|
||||||
mcp_server_value = field_value
|
|
||||||
self.tools, build_config["mcp_server"]["value"] = await self.update_tool_list(mcp_server_value)
|
|
||||||
except ValueError:
|
|
||||||
if not build_config["tools_metadata"]["show"]:
|
|
||||||
build_config["tool"]["show"] = True
|
|
||||||
build_config["tool"]["options"] = []
|
|
||||||
build_config["tool"]["value"] = ""
|
|
||||||
build_config["tool"]["placeholder"] = "Error on MCP Server"
|
|
||||||
else:
|
|
||||||
build_config["tool"]["show"] = False
|
|
||||||
self.remove_non_default_keys(build_config)
|
|
||||||
return build_config
|
|
||||||
build_config["tool"]["placeholder"] = ""
|
|
||||||
if "tool" in build_config and len(self.tools) > 0 and not build_config["tools_metadata"]["show"]:
|
|
||||||
build_config["tool"]["show"] = True
|
|
||||||
build_config["tool"]["options"] = [tool.name for tool in self.tools]
|
|
||||||
await self._update_tool_config(build_config, build_config["tool"]["value"])
|
|
||||||
elif "tool" in build_config and len(self.tools) == 0:
|
|
||||||
self.remove_non_default_keys(build_config)
|
|
||||||
build_config["tool"]["show"] = False
|
build_config["tool"]["show"] = False
|
||||||
build_config["tool"]["options"] = []
|
build_config["tool"]["options"] = []
|
||||||
build_config["tool"]["value"] = ""
|
build_config["tool"]["value"] = ""
|
||||||
|
build_config["tool"]["placeholder"] = ""
|
||||||
|
self.remove_non_default_keys(build_config)
|
||||||
|
return build_config
|
||||||
|
|
||||||
|
current_server_name = field_value.get("name") if isinstance(field_value, dict) else field_value
|
||||||
|
|
||||||
|
# To avoid unnecessary updates, only proceed if the server has actually changed
|
||||||
|
if self._last_selected_server == current_server_name:
|
||||||
|
return build_config
|
||||||
|
|
||||||
|
# Determine if "Tool Mode" is active by checking if the tool dropdown is hidden.
|
||||||
|
# The check for _last_selected_server handles the initial state where the dropdown is
|
||||||
|
# hidden by default but we are not yet in "Tool Mode".
|
||||||
|
is_in_tool_mode = build_config["tools_metadata"]["value"]
|
||||||
|
self._last_selected_server = current_server_name
|
||||||
|
self.tools = [] # Clear previous tools
|
||||||
|
self.remove_non_default_keys(build_config) # Clear previous tool inputs
|
||||||
|
|
||||||
|
# Only show the tool dropdown if not in tool_mode
|
||||||
|
if not is_in_tool_mode:
|
||||||
|
build_config["tool"]["show"] = True
|
||||||
|
build_config["tool"]["placeholder"] = "Loading tools..."
|
||||||
|
build_config["tool"]["options"] = []
|
||||||
|
build_config["tool"]["value"] = ""
|
||||||
|
else:
|
||||||
|
# Keep the tool dropdown hidden if in tool_mode
|
||||||
|
build_config["tool"]["show"] = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Fetch tools for the newly selected server
|
||||||
|
tools, server_info = await self.update_tool_list(field_value)
|
||||||
|
build_config["mcp_server"]["value"] = server_info
|
||||||
|
|
||||||
|
if tools:
|
||||||
|
tool_names = [tool.name for tool in tools]
|
||||||
|
if not is_in_tool_mode:
|
||||||
|
build_config["tool"]["options"] = tool_names
|
||||||
|
build_config["tool"]["placeholder"] = "Select a tool"
|
||||||
|
elif not is_in_tool_mode:
|
||||||
|
build_config["tool"]["placeholder"] = "No tools found"
|
||||||
|
|
||||||
|
except (ValueError, AttributeError, KeyError, TypeError, ConnectionError, TimeoutError) as e:
|
||||||
|
logger.error(f"Failed to fetch tools for server '{current_server_name}': {e}")
|
||||||
|
if not is_in_tool_mode:
|
||||||
|
build_config["tool"]["placeholder"] = "Error fetching tools"
|
||||||
|
build_config["tool"]["options"] = []
|
||||||
|
build_config["tool"]["value"] = ""
|
||||||
|
finally:
|
||||||
|
# Ensure we don't show inputs for a tool that might no longer be valid
|
||||||
|
await self._update_tool_config(build_config, "")
|
||||||
|
|
||||||
elif field_name == "tool_mode":
|
elif field_name == "tool_mode":
|
||||||
try:
|
try:
|
||||||
self.tools, build_config["mcp_server"]["value"] = await self.update_tool_list()
|
self.tools, build_config["mcp_server"]["value"] = await self.update_tool_list()
|
||||||
|
|
@ -429,4 +463,5 @@ class MCPToolsComponent(ComponentWithCache):
|
||||||
async def _get_tools(self):
|
async def _get_tools(self):
|
||||||
"""Get cached tools or update if necessary."""
|
"""Get cached tools or update if necessary."""
|
||||||
mcp_server = getattr(self, "mcp_server", None)
|
mcp_server = getattr(self, "mcp_server", None)
|
||||||
return await self.update_tool_list(mcp_server)
|
tools, _ = await self.update_tool_list(mcp_server)
|
||||||
|
return tools
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,10 @@ class Settings(BaseSettings):
|
||||||
"""The number of seconds to wait before giving up on a lock to released or establishing a connection to the
|
"""The number of seconds to wait before giving up on a lock to released or establishing a connection to the
|
||||||
database."""
|
database."""
|
||||||
|
|
||||||
|
mcp_server_timeout: int = 20
|
||||||
|
"""The number of seconds to wait before giving up on a lock to released or establishing a connection to the
|
||||||
|
database."""
|
||||||
|
|
||||||
# sqlite configuration
|
# sqlite configuration
|
||||||
sqlite_pragmas: dict | None = {"synchronous": "NORMAL", "journal_mode": "WAL"}
|
sqlite_pragmas: dict | None = {"synchronous": "NORMAL", "journal_mode": "WAL"}
|
||||||
"""SQLite pragmas to use when connecting to the database."""
|
"""SQLite pragmas to use when connecting to the database."""
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ dependencies = [
|
||||||
"validators>=0.34.0",
|
"validators>=0.34.0",
|
||||||
"networkx>=3.4.2",
|
"networkx>=3.4.2",
|
||||||
"json-repair>=0.30.3",
|
"json-repair>=0.30.3",
|
||||||
"mcp>=1.6.0",
|
"mcp~=1.9.4",
|
||||||
"aiosqlite>=0.20.0",
|
"aiosqlite>=0.20.0",
|
||||||
"greenlet>=3.1.1",
|
"greenlet>=3.1.1",
|
||||||
"jsonquerylang>=1.1.1",
|
"jsonquerylang>=1.1.1",
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,7 @@ export default function Dropdown({
|
||||||
// We should only reset the value if it's not in options and not in filteredOptions
|
// We should only reset the value if it's not in options and not in filteredOptions
|
||||||
// and not a recently added custom value
|
// and not a recently added custom value
|
||||||
if (!options.includes(value) && !filteredOptions.includes(value)) {
|
if (!options.includes(value) && !filteredOptions.includes(value)) {
|
||||||
|
if (value) onSelect("", undefined, true);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,9 @@ export default function McpComponent({
|
||||||
name: server.name,
|
name: server.name,
|
||||||
description:
|
description:
|
||||||
server.toolsCount === null
|
server.toolsCount === null
|
||||||
? "Loading..."
|
? server.error
|
||||||
|
? "Error"
|
||||||
|
: "Loading..."
|
||||||
: !server.toolsCount
|
: !server.toolsCount
|
||||||
? "No actions found"
|
? "No actions found"
|
||||||
: `${server.toolsCount} action${server.toolsCount === 1 ? "" : "s"}`,
|
: `${server.toolsCount} action${server.toolsCount === 1 ? "" : "s"}`,
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,16 @@ export const useGetMCPServers: useQueryFunctionType<
|
||||||
if (cachedData && Array.isArray(cachedData)) {
|
if (cachedData && Array.isArray(cachedData)) {
|
||||||
const merged = data.map((server) => {
|
const merged = data.map((server) => {
|
||||||
const cached = cachedData.find((s) => s.name === server.name);
|
const cached = cachedData.find((s) => s.name === server.name);
|
||||||
return cached && (cached.toolsCount !== null || cached.mode !== null)
|
return cached &&
|
||||||
? { ...server, toolsCount: cached.toolsCount, mode: cached.mode }
|
(cached.toolsCount !== null ||
|
||||||
|
cached.mode !== null ||
|
||||||
|
cached.error !== null)
|
||||||
|
? {
|
||||||
|
...server,
|
||||||
|
toolsCount: cached.toolsCount,
|
||||||
|
mode: cached.mode,
|
||||||
|
error: cached.error,
|
||||||
|
}
|
||||||
: server;
|
: server;
|
||||||
});
|
});
|
||||||
return merged;
|
return merged;
|
||||||
|
|
|
||||||
|
|
@ -14,4 +14,4 @@ export const ENABLE_WIDGET = true;
|
||||||
export const ENABLE_VOICE_ASSISTANT = true;
|
export const ENABLE_VOICE_ASSISTANT = true;
|
||||||
export const ENABLE_IMAGE_ON_PLAYGROUND = false;
|
export const ENABLE_IMAGE_ON_PLAYGROUND = false;
|
||||||
export const ENABLE_MCP = true;
|
export const ENABLE_MCP = true;
|
||||||
export const ENABLE_MCP_NOTICE = true;
|
export const ENABLE_MCP_NOTICE = false;
|
||||||
|
|
|
||||||
|
|
@ -220,7 +220,7 @@ const HomePage = ({ type }: { type: "flows" | "components" | "mcp" }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CardsWrapComponent
|
<CardsWrapComponent
|
||||||
onFileDrop={handleFileDrop}
|
onFileDrop={flowType === "mcp" ? undefined : handleFileDrop}
|
||||||
dragMessage={`Drop your ${isEmptyFolder ? "flows or components" : flowType} here`}
|
dragMessage={`Drop your ${isEmptyFolder ? "flows or components" : flowType} here`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import ForwardedIconComponent from "@/components/common/genericIconComponent";
|
import ForwardedIconComponent from "@/components/common/genericIconComponent";
|
||||||
|
import ShadTooltip from "@/components/common/shadTooltipComponent";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
|
|
@ -14,6 +15,7 @@ import AddMcpServerModal from "@/modals/addMcpServerModal";
|
||||||
import DeleteConfirmationModal from "@/modals/deleteConfirmationModal";
|
import DeleteConfirmationModal from "@/modals/deleteConfirmationModal";
|
||||||
import useAlertStore from "@/stores/alertStore";
|
import useAlertStore from "@/stores/alertStore";
|
||||||
import { MCPServerInfoType } from "@/types/mcp";
|
import { MCPServerInfoType } from "@/types/mcp";
|
||||||
|
import { cn } from "@/utils/utils";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
export default function MCPServersPage() {
|
export default function MCPServersPage() {
|
||||||
|
|
@ -101,11 +103,20 @@ export default function MCPServersPage() {
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="text-sm font-medium">{server.name}</span>
|
<span className="text-sm font-medium">{server.name}</span>
|
||||||
<span className="text-mmd text-muted-foreground">
|
<ShadTooltip content={server.error}>
|
||||||
{server.toolsCount === null
|
<span
|
||||||
? "Loading..."
|
className={cn(
|
||||||
: `${server.toolsCount} action${server.toolsCount === 1 ? "" : "s"}`}
|
"cursor-default select-none !text-mmd text-muted-foreground",
|
||||||
</span>
|
server.error && "text-accent-red-foreground",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{server.toolsCount === null
|
||||||
|
? server.error
|
||||||
|
? "Error"
|
||||||
|
: "Loading..."
|
||||||
|
: `${server.toolsCount} action${server.toolsCount === 1 ? "" : "s"}`}
|
||||||
|
</span>
|
||||||
|
</ShadTooltip>
|
||||||
</div>
|
</div>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ export type MCPServerInfoType = {
|
||||||
description?: string;
|
description?: string;
|
||||||
mode: string | null;
|
mode: string | null;
|
||||||
toolsCount: number | null;
|
toolsCount: number | null;
|
||||||
|
error?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type MCPServerType = {
|
export type MCPServerType = {
|
||||||
|
|
|
||||||
5
uv.lock
generated
5
uv.lock
generated
|
|
@ -3,6 +3,7 @@ revision = 2
|
||||||
requires-python = ">=3.10, <3.14"
|
requires-python = ">=3.10, <3.14"
|
||||||
resolution-markers = [
|
resolution-markers = [
|
||||||
"python_full_version >= '3.13' and sys_platform == 'darwin'",
|
"python_full_version >= '3.13' and sys_platform == 'darwin'",
|
||||||
|
"python_version < '0'",
|
||||||
"python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
|
"python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
|
||||||
"(python_full_version >= '3.13' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
|
"(python_full_version >= '3.13' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
|
||||||
"python_full_version >= '3.12.4' and python_full_version < '3.13' and sys_platform == 'darwin'",
|
"python_full_version >= '3.12.4' and python_full_version < '3.13' and sys_platform == 'darwin'",
|
||||||
|
|
@ -5086,7 +5087,7 @@ requires-dist = [
|
||||||
{ name = "llama-cpp-python", marker = "extra == 'local'", specifier = "~=0.2.0" },
|
{ name = "llama-cpp-python", marker = "extra == 'local'", specifier = "~=0.2.0" },
|
||||||
{ name = "markdown", specifier = "==3.7" },
|
{ name = "markdown", specifier = "==3.7" },
|
||||||
{ name = "markupsafe", specifier = "==3.0.2" },
|
{ name = "markupsafe", specifier = "==3.0.2" },
|
||||||
{ name = "mcp", specifier = ">=0.9.1" },
|
{ name = "mcp", specifier = ">=1.9.4" },
|
||||||
{ name = "mem0ai", specifier = "==0.1.34" },
|
{ name = "mem0ai", specifier = "==0.1.34" },
|
||||||
{ name = "metal-sdk", specifier = "==2.5.1" },
|
{ name = "metal-sdk", specifier = "==2.5.1" },
|
||||||
{ name = "metaphor-python", specifier = "==0.1.23" },
|
{ name = "metaphor-python", specifier = "==0.1.23" },
|
||||||
|
|
@ -5363,7 +5364,7 @@ requires-dist = [
|
||||||
{ name = "llama-cpp-python", marker = "extra == 'all'", specifier = ">=0.2.0" },
|
{ name = "llama-cpp-python", marker = "extra == 'all'", specifier = ">=0.2.0" },
|
||||||
{ name = "llama-cpp-python", marker = "extra == 'local'", specifier = ">=0.2.0" },
|
{ name = "llama-cpp-python", marker = "extra == 'local'", specifier = ">=0.2.0" },
|
||||||
{ name = "loguru", specifier = ">=0.7.1,<1.0.0" },
|
{ name = "loguru", specifier = ">=0.7.1,<1.0.0" },
|
||||||
{ name = "mcp", specifier = ">=1.6.0" },
|
{ name = "mcp", specifier = "~=1.9.4" },
|
||||||
{ name = "multiprocess", specifier = ">=0.70.14,<1.0.0" },
|
{ name = "multiprocess", specifier = ">=0.70.14,<1.0.0" },
|
||||||
{ name = "nanoid", specifier = ">=2.0.0,<3.0.0" },
|
{ name = "nanoid", specifier = ">=2.0.0,<3.0.0" },
|
||||||
{ name = "nest-asyncio", specifier = ">=1.6.0,<2.0.0" },
|
{ name = "nest-asyncio", specifier = ">=1.6.0,<2.0.0" },
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue